From 33fd1358507b4a5abb3dcebe78d407d0567717c1 Mon Sep 17 00:00:00 2001
From: Marcus Eibrink-Lunzenauer
Date: Tue, 18 Jun 2024 13:18:06 +0000
Subject: Deprecate `StudipAutoloader` and use composer's `autoload`
Closes #4282
Merge request studip/studip!3099
---
app/controllers/questionnaire.php | 2 -
composer.json | 62 +-
composer.lock | 1108 ++++----
db/migrations/1.2_step_102_datenfeldtypen.php | 2 +-
.../1.6_step_25_raumzeit_db_conversion.php | 4 +-
.../ConditionalAdmission.class.php | 563 ----
.../conditionaladmission/ConditionalAdmission.php | 563 ++++
.../CourseMemberAdmission.class.php | 260 --
.../CourseMemberAdmission.php | 260 ++
.../limitedadmission/LimitedAdmission.class.php | 284 --
.../limitedadmission/LimitedAdmission.php | 284 ++
.../lockedadmission/LockedAdmission.class.php | 136 -
.../lockedadmission/LockedAdmission.php | 136 +
.../ParticipantRestrictedAdmission.class.php | 230 --
.../ParticipantRestrictedAdmission.php | 230 ++
.../passwordadmission/PasswordAdmission.class.php | 241 --
.../passwordadmission/PasswordAdmission.php | 241 ++
.../PreferentialAdmission.class.php | 540 ----
.../PreferentialAdmission.php | 540 ++++
.../termsadmission/TermsAdmission.class.php | 171 --
.../termsadmission/TermsAdmission.php | 171 ++
.../timedadmission/TimedAdmission.class.php | 243 --
.../timedadmission/TimedAdmission.php | 243 ++
lib/bootstrap-autoload.php | 81 -
lib/bootstrap.php | 2 +-
lib/calendar/CalendarColumn.class.php | 371 ---
lib/calendar/CalendarColumn.php | 371 +++
lib/calendar/CalendarView.class.php | 344 ---
lib/calendar/CalendarView.php | 344 +++
lib/calendar/CalendarWeekView.class.php | 124 -
lib/calendar/CalendarWeekView.php | 124 +
lib/classes/AdminCourseFilter.class.php | 220 --
lib/classes/AdminCourseFilter.php | 220 ++
lib/classes/Assets.class.php | 439 ---
lib/classes/Assets.php | 439 +++
lib/classes/AuthorObject.class.php | 139 -
lib/classes/AuthorObject.php | 139 +
lib/classes/AutoInsert.class.php | 342 ---
lib/classes/AutoInsert.php | 342 +++
lib/classes/Avatar.class.php | 635 -----
lib/classes/Avatar.php | 635 +++++
lib/classes/BreadCrumb.class.php | 103 -
lib/classes/BreadCrumb.php | 103 +
lib/classes/Button.class.php | 58 -
lib/classes/Button.php | 58 +
lib/classes/CSVArrayObject.class.php | 47 -
lib/classes/CSVArrayObject.php | 47 +
lib/classes/Color.class.php | 370 ---
lib/classes/Color.php | 370 +++
lib/classes/Config.class.php | 469 ----
lib/classes/Config.php | 469 ++++
lib/classes/CourseAvatar.class.php | 44 -
lib/classes/CourseAvatar.php | 44 +
lib/classes/CourseConfig.class.php | 23 -
lib/classes/CourseConfig.php | 23 +
lib/classes/CronJob.class.php | 140 -
lib/classes/CronJob.php | 140 +
lib/classes/CronjobScheduler.class.php | 333 ---
lib/classes/CronjobScheduler.php | 333 +++
lib/classes/DBManager.class.php | 250 --
lib/classes/DBManager.php | 250 ++
lib/classes/DataFieldBoolEntry.class.php | 35 -
lib/classes/DataFieldBoolEntry.php | 35 +
lib/classes/DataFieldComboEntry.class.php | 79 -
lib/classes/DataFieldComboEntry.php | 79 +
lib/classes/DataFieldDateEntry.class.php | 80 -
lib/classes/DataFieldDateEntry.php | 80 +
lib/classes/DataFieldEmailEntry.class.php | 25 -
lib/classes/DataFieldEmailEntry.php | 25 +
lib/classes/DataFieldEntry.class.php | 588 ----
lib/classes/DataFieldEntry.php | 588 ++++
lib/classes/DataFieldLinkEntry.class.php | 54 -
lib/classes/DataFieldLinkEntry.php | 54 +
lib/classes/DataFieldPhoneEntry.class.php | 122 -
lib/classes/DataFieldPhoneEntry.php | 122 +
lib/classes/DataFieldRadioEntry.class.php | 39 -
lib/classes/DataFieldRadioEntry.php | 39 +
lib/classes/DataFieldSelectboxEntry.class.php | 100 -
lib/classes/DataFieldSelectboxEntry.php | 100 +
.../DataFieldSelectboxMultipleEntry.class.php | 93 -
lib/classes/DataFieldSelectboxMultipleEntry.php | 93 +
lib/classes/DataFieldTextareaEntry.class.php | 29 -
lib/classes/DataFieldTextareaEntry.php | 29 +
lib/classes/DataFieldTextareai18nEntry.class.php | 21 -
lib/classes/DataFieldTextareai18nEntry.php | 21 +
lib/classes/DataFieldTextlineEntry.class.php | 14 -
lib/classes/DataFieldTextlineEntry.php | 14 +
lib/classes/DataFieldTextlinei18nEntry.class.php | 21 -
lib/classes/DataFieldTextlinei18nEntry.php | 21 +
lib/classes/DataFieldTextmarkupEntry.class.php | 35 -
lib/classes/DataFieldTextmarkupEntry.php | 35 +
lib/classes/DataFieldTextmarkupi18nEntry.class.php | 47 -
lib/classes/DataFieldTextmarkupi18nEntry.php | 47 +
lib/classes/DataFieldTimeEntry.class.php | 50 -
lib/classes/DataFieldTimeEntry.php | 50 +
lib/classes/DatabaseObject.class.php | 149 -
lib/classes/DatabaseObject.php | 149 +
lib/classes/DateFormatter.class.php | 176 --
lib/classes/DateFormatter.php | 176 ++
lib/classes/DbSnapshot.class.php | 370 ---
lib/classes/DbSnapshot.php | 370 +++
lib/classes/DbView.class.php | 378 ---
lib/classes/DbView.php | 378 +++
lib/classes/Event.interface.php | 184 --
lib/classes/Event.php | 184 ++
lib/classes/Feedback.class.php | 101 -
lib/classes/Feedback.php | 101 +
lib/classes/FeedbackRange.interface.php | 55 -
lib/classes/FeedbackRange.php | 55 +
lib/classes/FileLock.class.php | 74 -
lib/classes/FileLock.php | 74 +
lib/classes/Fullcalendar.class.php | 109 -
lib/classes/Fullcalendar.php | 109 +
lib/classes/Icon.class.php | 395 ---
lib/classes/Icon.php | 395 +++
lib/classes/InstituteAvatar.class.php | 45 -
lib/classes/InstituteAvatar.php | 45 +
lib/classes/InstituteCalendarHelper.class.php | 810 ------
lib/classes/InstituteCalendarHelper.php | 810 ++++++
lib/classes/InstituteConfig.class.php | 15 -
lib/classes/InstituteConfig.php | 15 +
lib/classes/Interactable.class.php | 204 --
lib/classes/Interactable.php | 204 ++
lib/classes/JSONArrayObject.class.php | 40 -
lib/classes/JSONArrayObject.php | 40 +
lib/classes/LayoutMessage.interface.php | 18 -
lib/classes/LayoutMessage.php | 18 +
lib/classes/LinkButton.class.php | 58 -
lib/classes/LinkButton.php | 58 +
lib/classes/LockRules.class.php | 247 --
lib/classes/LockRules.php | 247 ++
lib/classes/Loggable.class.php | 53 -
lib/classes/Loggable.php | 53 +
lib/classes/MVV.class.php | 849 ------
lib/classes/MVV.php | 849 ++++++
lib/classes/Markup.class.php | 788 ------
lib/classes/Markup.php | 788 ++++++
lib/classes/MessageBox.class.php | 177 --
lib/classes/MessageBox.php | 177 ++
lib/classes/ModulesNotification.class.php | 182 --
lib/classes/ModulesNotification.php | 182 ++
lib/classes/MultiDimArrayObject.class.php | 129 -
lib/classes/MultiDimArrayObject.php | 129 +
lib/classes/MultiPersonSearch.class.php | 548 ----
lib/classes/MultiPersonSearch.php | 548 ++++
lib/classes/NotificationCenter.class.php | 174 --
lib/classes/NotificationCenter.php | 174 ++
lib/classes/PrivacyObject.interface.php | 11 -
lib/classes/PrivacyObject.php | 11 +
lib/classes/QuestionType.interface.php | 118 -
lib/classes/QuestionType.php | 118 +
lib/classes/QuickSearch.class.php | 503 ----
lib/classes/QuickSearch.php | 503 ++++
lib/classes/Range.interface.php | 66 -
lib/classes/Range.php | 66 +
lib/classes/RangeConfig.class.php | 223 --
lib/classes/RangeConfig.php | 223 ++
lib/classes/RangeTreeObject.class.php | 294 --
lib/classes/RangeTreeObject.php | 294 ++
lib/classes/RangeTreeObjectFak.class.php | 56 -
lib/classes/RangeTreeObjectFak.php | 56 +
lib/classes/RangeTreeObjectInst.class.php | 71 -
lib/classes/RangeTreeObjectInst.php | 71 +
lib/classes/Request.class.php | 828 ------
lib/classes/Request.php | 828 ++++++
lib/classes/ResetButton.class.php | 53 -
lib/classes/ResetButton.php | 53 +
lib/classes/Score.class.php | 225 --
lib/classes/Score.php | 225 ++
lib/classes/SemBrowse.class.php | 1264 ---------
lib/classes/SemBrowse.php | 1264 +++++++++
lib/classes/SemClass.class.php | 644 -----
lib/classes/SemClass.php | 644 +++++
lib/classes/SemType.class.php | 257 --
lib/classes/SemType.php | 257 ++
lib/classes/Seminar.class.php | 2439 ----------------
lib/classes/Seminar.php | 2439 ++++++++++++++++
lib/classes/SeminarCategories.class.php | 138 -
lib/classes/SeminarCategories.php | 138 +
lib/classes/SessionDecoder.class.php | 246 --
lib/classes/SessionDecoder.php | 246 ++
lib/classes/SimpleCollection.class.php | 782 ------
lib/classes/SimpleCollection.php | 782 ++++++
lib/classes/SimpleORMap.class.php | 2483 ----------------
lib/classes/SimpleORMap.php | 2483 ++++++++++++++++
lib/classes/SimpleORMapCollection.class.php | 258 --
lib/classes/SimpleORMapCollection.php | 258 ++
lib/classes/StudipArrayObject.class.php | 471 ----
lib/classes/StudipArrayObject.php | 471 ++++
lib/classes/StudipForm.class.php | 590 ----
lib/classes/StudipForm.php | 590 ++++
lib/classes/StudipItem.interface.php | 72 -
lib/classes/StudipItem.php | 72 +
lib/classes/StudipKing.class.php | 173 --
lib/classes/StudipKing.php | 173 ++
lib/classes/StudipLink.class.php | 47 -
lib/classes/StudipLink.php | 47 +
lib/classes/StudipLock.class.php | 99 -
lib/classes/StudipLock.php | 99 +
lib/classes/StudipLog.class.php | 380 ---
lib/classes/StudipLog.php | 380 +++
lib/classes/StudipLvgruppeSelection.class.php | 359 ---
lib/classes/StudipLvgruppeSelection.php | 359 +++
lib/classes/StudipMail.class.php | 487 ----
lib/classes/StudipMail.php | 487 ++++
lib/classes/StudipObject.class.php | 199 --
lib/classes/StudipObject.php | 199 ++
lib/classes/StudipPDO.class.php | 385 ---
lib/classes/StudipPDO.php | 385 +++
lib/classes/StudipRangeTree.class.php | 222 --
lib/classes/StudipRangeTree.php | 222 ++
lib/classes/StudipRangeTreeView.class.php | 101 -
lib/classes/StudipRangeTreeView.php | 101 +
lib/classes/StudipRangeTreeViewAdmin.class.php | 837 ------
lib/classes/StudipRangeTreeViewAdmin.php | 837 ++++++
lib/classes/StudipSemRangeTreeViewSimple.class.php | 247 --
lib/classes/StudipSemRangeTreeViewSimple.php | 247 ++
lib/classes/StudipSemSearch.class.php | 191 --
lib/classes/StudipSemSearch.php | 191 ++
lib/classes/StudipSemSearchHelper.class.php | 239 --
lib/classes/StudipSemSearchHelper.php | 239 ++
lib/classes/StudipSemTree.class.php | 312 --
lib/classes/StudipSemTree.php | 312 ++
lib/classes/StudipSemTreeSearch.class.php | 241 --
lib/classes/StudipSemTreeSearch.php | 241 ++
lib/classes/StudipSemTreeView.class.php | 215 --
lib/classes/StudipSemTreeView.php | 215 ++
lib/classes/StudipSemTreeViewAdmin.class.php | 825 ------
lib/classes/StudipSemTreeViewAdmin.php | 825 ++++++
lib/classes/StudipSemTreeViewSimple.class.php | 244 --
lib/classes/StudipSemTreeViewSimple.php | 244 ++
lib/classes/StudipStudyAreaSelection.class.php | 332 ---
lib/classes/StudipStudyAreaSelection.php | 332 +++
lib/classes/StudygroupAvatar.class.php | 16 -
lib/classes/StudygroupAvatar.php | 16 +
lib/classes/TreeAbstract.class.php | 431 ---
lib/classes/TreeAbstract.php | 431 +++
lib/classes/TreeView.class.php | 446 ---
lib/classes/TreeView.php | 446 +++
lib/classes/UpdateInformation.class.php | 118 -
lib/classes/UpdateInformation.php | 118 +
lib/classes/UserConfig.class.php | 40 -
lib/classes/UserConfig.php | 40 +
lib/classes/UserLookup.class.php | 503 ----
lib/classes/UserLookup.php | 503 ++++
lib/classes/UserManagement.class.php | 1353 ---------
lib/classes/UserManagement.php | 1353 +++++++++
lib/classes/admission/AdmissionAlgorithm.class.php | 35 -
lib/classes/admission/AdmissionAlgorithm.php | 35 +
lib/classes/admission/AdmissionPriority.class.php | 253 --
lib/classes/admission/AdmissionPriority.php | 253 ++
lib/classes/admission/AdmissionRule.class.php | 456 ---
lib/classes/admission/AdmissionRule.php | 456 +++
lib/classes/admission/AdmissionUserList.class.php | 405 ---
lib/classes/admission/AdmissionUserList.php | 405 +++
lib/classes/admission/CourseSet.class.php | 1185 --------
lib/classes/admission/CourseSet.php | 1185 ++++++++
lib/classes/admission/RandomAlgorithm.class.php | 437 ---
lib/classes/admission/RandomAlgorithm.php | 437 +++
lib/classes/admission/UserFilter.class.php | 279 --
lib/classes/admission/UserFilter.php | 279 ++
lib/classes/admission/UserFilterField.class.php | 473 ----
lib/classes/admission/UserFilterField.php | 473 ++++
.../userfilter/DatafieldCondition.class.php | 164 --
.../admission/userfilter/DatafieldCondition.php | 164 ++
.../admission/userfilter/DegreeCondition.class.php | 51 -
.../admission/userfilter/DegreeCondition.php | 51 +
.../userfilter/PermissionCondition.class.php | 46 -
.../admission/userfilter/PermissionCondition.php | 46 +
.../userfilter/SemesterOfStudyCondition.class.php | 81 -
.../userfilter/SemesterOfStudyCondition.php | 81 +
.../userfilter/StgteilVersionCondition.class.php | 83 -
.../userfilter/StgteilVersionCondition.php | 83 +
.../userfilter/SubjectCondition.class.php | 52 -
.../admission/userfilter/SubjectCondition.php | 52 +
.../userfilter/SubjectConditionAny.class.php | 50 -
.../admission/userfilter/SubjectConditionAny.php | 50 +
.../auth_plugins/StudipAuthAbstract.class.php | 578 ----
lib/classes/auth_plugins/StudipAuthAbstract.php | 578 ++++
lib/classes/auth_plugins/StudipAuthCAS.class.php | 89 -
lib/classes/auth_plugins/StudipAuthCAS.php | 89 +
lib/classes/auth_plugins/StudipAuthIP.class.php | 20 -
lib/classes/auth_plugins/StudipAuthIP.php | 20 +
lib/classes/auth_plugins/StudipAuthLTI.class.php | 135 -
lib/classes/auth_plugins/StudipAuthLTI.php | 135 +
lib/classes/auth_plugins/StudipAuthLdap.class.php | 216 --
lib/classes/auth_plugins/StudipAuthLdap.php | 216 ++
.../StudipAuthLdapReadAndBind.class.php | 81 -
.../auth_plugins/StudipAuthLdapReadAndBind.php | 81 +
lib/classes/auth_plugins/StudipAuthOIDC.class.php | 112 -
lib/classes/auth_plugins/StudipAuthOIDC.php | 112 +
lib/classes/auth_plugins/StudipAuthSSO.class.php | 51 -
lib/classes/auth_plugins/StudipAuthSSO.php | 51 +
lib/classes/auth_plugins/StudipAuthShib.class.php | 139 -
lib/classes/auth_plugins/StudipAuthShib.php | 139 +
.../auth_plugins/StudipAuthStandard.class.php | 89 -
lib/classes/auth_plugins/StudipAuthStandard.php | 89 +
lib/classes/cache/Cache.class.php | 213 --
lib/classes/cache/Cache.php | 213 ++
lib/classes/cache/DbCache.class.php | 143 -
lib/classes/cache/DbCache.php | 143 +
lib/classes/cache/Factory.class.php | 206 --
lib/classes/cache/Factory.php | 206 ++
lib/classes/cache/FileCache.class.php | 278 --
lib/classes/cache/FileCache.php | 278 ++
lib/classes/cache/Item.class.php | 164 --
lib/classes/cache/Item.php | 164 ++
lib/classes/cache/MemcachedCache.class.php | 151 -
lib/classes/cache/MemcachedCache.php | 151 +
lib/classes/cache/MemoryCache.class.php | 98 -
lib/classes/cache/MemoryCache.php | 98 +
lib/classes/cache/Proxy.class.php | 123 -
lib/classes/cache/Proxy.php | 123 +
lib/classes/cache/RedisCache.class.php | 198 --
lib/classes/cache/RedisCache.php | 198 ++
lib/classes/cache/Wrapper.class.php | 95 -
lib/classes/cache/Wrapper.php | 95 +
lib/classes/calendar/EventData.class.php | 114 -
lib/classes/calendar/EventData.php | 114 +
lib/classes/calendar/EventSource.interface.php | 64 -
lib/classes/calendar/EventSource.php | 64 +
lib/classes/calendar/ICalendarExport.class.php | 628 -----
lib/classes/calendar/ICalendarExport.php | 628 +++++
lib/classes/calendar/ICalendarImport.class.php | 678 -----
lib/classes/calendar/ICalendarImport.php | 678 +++++
lib/classes/calendar/Owner.interface.php | 40 -
lib/classes/calendar/Owner.php | 40 +
.../exportdocument/ExportDocument.interface.php | 59 -
lib/classes/exportdocument/ExportDocument.php | 59 +
lib/classes/exportdocument/ExportPDF.class.php | 369 ---
lib/classes/exportdocument/ExportPDF.php | 369 +++
.../librarysearch/LibraryDocument.class.php | 442 ---
lib/classes/librarysearch/LibraryDocument.php | 442 +++
.../LibraryResultParser.interface.php | 40 -
lib/classes/librarysearch/LibraryResultParser.php | 40 +
lib/classes/librarysearch/LibrarySearch.class.php | 150 -
lib/classes/librarysearch/LibrarySearch.php | 150 +
.../librarysearch/LibrarySearchManager.class.php | 174 --
lib/classes/librarysearch/LibrarySearchManager.php | 174 ++
.../BASELibraryResultParser.class.php | 158 --
.../resultparsers/BASELibraryResultParser.php | 158 ++
.../K10PlusLibraryResultParser.class.php | 84 -
.../resultparsers/K10PlusLibraryResultParser.php | 84 +
.../MarcxmlLibraryResultParser.class.php | 234 --
.../resultparsers/MarcxmlLibraryResultParser.php | 234 ++
.../resultparsers/SRULibraryResultParser.class.php | 96 -
.../resultparsers/SRULibraryResultParser.php | 96 +
.../searchmodules/BASELibrarySearch.class.php | 114 -
.../searchmodules/BASELibrarySearch.php | 114 +
.../K10PlusZentralLibrarySearch.class.php | 149 -
.../searchmodules/K10PlusZentralLibrarySearch.php | 149 +
.../searchmodules/SRULibrarySearch.class.php | 153 -
.../searchmodules/SRULibrarySearch.php | 153 +
lib/classes/searchtypes/MyCoursesSearch.class.php | 220 --
lib/classes/searchtypes/MyCoursesSearch.php | 220 ++
lib/classes/searchtypes/PermissionSearch.class.php | 219 --
lib/classes/searchtypes/PermissionSearch.php | 219 ++
lib/classes/searchtypes/RangeSearch.class.php | 87 -
lib/classes/searchtypes/RangeSearch.php | 87 +
lib/classes/searchtypes/ResourceSearch.class.php | 362 ---
lib/classes/searchtypes/ResourceSearch.php | 362 +++
lib/classes/searchtypes/RoomSearch.class.php | 192 --
lib/classes/searchtypes/RoomSearch.php | 192 ++
lib/classes/searchtypes/SQLSearch.class.php | 179 --
lib/classes/searchtypes/SQLSearch.php | 179 ++
lib/classes/searchtypes/SearchType.class.php | 107 -
lib/classes/searchtypes/SearchType.php | 107 +
lib/classes/searchtypes/SeminarSearch.class.php | 101 -
lib/classes/searchtypes/SeminarSearch.php | 101 +
lib/classes/searchtypes/StandardSearch.class.php | 206 --
lib/classes/searchtypes/StandardSearch.php | 206 ++
lib/classes/searchtypes/TreeSearch.class.php | 96 -
lib/classes/searchtypes/TreeSearch.php | 96 +
lib/classes/sidebar/ClipboardWidget.class.php | 143 -
lib/classes/sidebar/ClipboardWidget.php | 143 +
.../sidebar/InstituteSelectWidget.class.php | 111 -
lib/classes/sidebar/InstituteSelectWidget.php | 111 +
lib/classes/sidebar/ResourceTreeWidget.class.php | 145 -
lib/classes/sidebar/ResourceTreeWidget.php | 145 +
lib/classes/sidebar/RoomClipboardWidget.class.php | 57 -
lib/classes/sidebar/RoomClipboardWidget.php | 57 +
lib/classes/sidebar/RoomSearchTreeWidget.class.php | 41 -
lib/classes/sidebar/RoomSearchTreeWidget.php | 41 +
lib/classes/sidebar/RoomSearchWidget.class.php | 595 ----
lib/classes/sidebar/RoomSearchWidget.php | 595 ++++
lib/cronjobs/check_admission.class.php | 147 -
lib/cronjobs/check_admission.php | 147 +
lib/cronjobs/cleanup_log.class.php | 114 -
lib/cronjobs/cleanup_log.php | 114 +
lib/cronjobs/garbage_collector.class.php | 202 --
lib/cronjobs/garbage_collector.php | 202 ++
lib/cronjobs/purge_cache.class.php | 92 -
lib/cronjobs/purge_cache.php | 92 +
lib/cronjobs/remind_oer_upload.class.php | 73 -
lib/cronjobs/remind_oer_upload.php | 73 +
lib/cronjobs/send_mail_notifications.class.php | 141 -
lib/cronjobs/send_mail_notifications.php | 141 +
lib/cronjobs/send_mail_queue.class.php | 70 -
lib/cronjobs/send_mail_queue.php | 70 +
lib/cronjobs/session_gc.class.php | 29 -
lib/cronjobs/session_gc.php | 29 +
lib/elearning/ConnectedCMS.class.php | 466 ---
lib/elearning/ConnectedCMS.php | 466 +++
lib/elearning/ConnectedLink.class.php | 129 -
lib/elearning/ConnectedLink.php | 129 +
lib/elearning/ConnectedPermissions.class.php | 57 -
lib/elearning/ConnectedPermissions.php | 57 +
lib/elearning/ConnectedUser.class.php | 512 ----
lib/elearning/ConnectedUser.php | 512 ++++
lib/elearning/ContentModule.class.php | 388 ---
lib/elearning/ContentModule.php | 388 +++
lib/elearning/ContentModuleView.class.php | 189 --
lib/elearning/ContentModuleView.php | 189 ++
lib/elearning/ELearningUtils.class.php | 612 ----
lib/elearning/ELearningUtils.php | 612 ++++
lib/elearning/Ilias3ConnectedCMS.class.php | 350 ---
lib/elearning/Ilias3ConnectedCMS.php | 350 +++
lib/elearning/Ilias3ConnectedLink.class.php | 188 --
lib/elearning/Ilias3ConnectedLink.php | 188 ++
lib/elearning/Ilias3ConnectedPermissions.class.php | 256 --
lib/elearning/Ilias3ConnectedPermissions.php | 256 ++
lib/elearning/Ilias3ConnectedUser.class.php | 345 ---
lib/elearning/Ilias3ConnectedUser.php | 345 +++
lib/elearning/Ilias3ContentModule.class.php | 292 --
lib/elearning/Ilias3ContentModule.php | 292 ++
lib/elearning/Ilias3ObjectXMLParser.class.php | 236 --
lib/elearning/Ilias3ObjectXMLParser.php | 236 ++
lib/elearning/Ilias3SaxParser.class.php | 230 --
lib/elearning/Ilias3SaxParser.php | 230 ++
lib/elearning/Ilias3Soap.class.php | 1069 -------
lib/elearning/Ilias3Soap.php | 1069 +++++++
lib/elearning/Ilias4ConnectedCMS.class.php | 272 --
lib/elearning/Ilias4ConnectedCMS.php | 272 ++
lib/elearning/Ilias4ConnectedLink.class.php | 124 -
lib/elearning/Ilias4ConnectedLink.php | 124 +
lib/elearning/Ilias4ConnectedPermissions.class.php | 126 -
lib/elearning/Ilias4ConnectedPermissions.php | 126 +
lib/elearning/Ilias4ConnectedUser.class.php | 168 --
lib/elearning/Ilias4ConnectedUser.php | 168 ++
lib/elearning/Ilias4ContentModule.class.php | 106 -
lib/elearning/Ilias4ContentModule.php | 106 +
lib/elearning/Ilias4Soap.class.php | 181 --
lib/elearning/Ilias4Soap.php | 181 ++
lib/elearning/Ilias5ConnectedCMS.class.php | 21 -
lib/elearning/Ilias5ConnectedCMS.php | 21 +
lib/elearning/Ilias5ConnectedLink.class.php | 16 -
lib/elearning/Ilias5ConnectedLink.php | 16 +
lib/elearning/Ilias5ConnectedPermissions.class.php | 17 -
lib/elearning/Ilias5ConnectedPermissions.php | 17 +
lib/elearning/Ilias5ConnectedUser.class.php | 40 -
lib/elearning/Ilias5ConnectedUser.php | 40 +
lib/elearning/Ilias5ContentModule.class.php | 16 -
lib/elearning/Ilias5ContentModule.php | 16 +
lib/elearning/Ilias5Soap.class.php | 114 -
lib/elearning/Ilias5Soap.php | 114 +
lib/elearning/LonCapaConnectedCMS.class.php | 67 -
lib/elearning/LonCapaConnectedCMS.php | 67 +
lib/elearning/LonCapaConnectedLink.class.php | 67 -
lib/elearning/LonCapaConnectedLink.php | 67 +
lib/elearning/LonCapaContentModule.class.php | 86 -
lib/elearning/LonCapaContentModule.php | 86 +
lib/elearning/LonCapaRequest.class.php | 110 -
lib/elearning/LonCapaRequest.php | 110 +
lib/elearning/ObjectConnections.class.php | 254 --
lib/elearning/ObjectConnections.php | 254 ++
lib/elearning/PmWikiConnectedCMS.class.php | 86 -
lib/elearning/PmWikiConnectedCMS.php | 86 +
lib/elearning/PmWikiConnectedLink.class.php | 134 -
lib/elearning/PmWikiConnectedLink.php | 134 +
lib/elearning/PmWikiContentModule.class.php | 115 -
lib/elearning/PmWikiContentModule.php | 115 +
lib/exceptions/ClipboardException.class.php | 25 -
lib/exceptions/ClipboardException.php | 25 +
.../GlobalResourceLockException.class.php | 25 -
.../resources/GlobalResourceLockException.php | 25 +
.../GlobalResourceLockOverlapException.class.php | 25 -
.../GlobalResourceLockOverlapException.php | 25 +
.../InvalidResourceCategoryException.class.php | 25 -
.../resources/InvalidResourceCategoryException.php | 25 +
.../InvalidResourceClassException.class.php | 24 -
.../resources/InvalidResourceClassException.php | 24 +
.../resources/InvalidResourceException.class.php | 25 -
.../resources/InvalidResourceException.php | 25 +
.../InvalidResourceRequestException.class.php | 22 -
.../resources/InvalidResourceRequestException.php | 22 +
.../resources/NoResourceClassException.class.php | 11 -
.../resources/NoResourceClassException.php | 11 +
.../resources/ResourceBookingException.class.php | 24 -
.../resources/ResourceBookingException.php | 24 +
.../ResourceBookingOverlapException.class.php | 24 -
.../resources/ResourceBookingOverlapException.php | 24 +
.../ResourceBookingRangeException.class.php | 24 -
.../resources/ResourceBookingRangeException.php | 24 +
.../resources/ResourceException.class.php | 23 -
lib/exceptions/resources/ResourceException.php | 23 +
.../ResourceNoTimeRangeException.class.php | 24 -
.../resources/ResourceNoTimeRangeException.php | 24 +
.../ResourcePermissionException.class.php | 24 -
.../resources/ResourcePermissionException.php | 24 +
.../ResourcePropertyDefinitionException.class.php | 25 -
.../ResourcePropertyDefinitionException.php | 25 +
.../resources/ResourcePropertyException.class.php | 25 -
.../resources/ResourcePropertyException.php | 25 +
.../ResourcePropertyStateException.class.php | 23 -
.../resources/ResourcePropertyStateException.php | 23 +
.../resources/ResourceRequestException.class.php | 24 -
.../resources/ResourceRequestException.php | 24 +
.../ResourceUnavailableException.class.php | 24 -
.../resources/ResourceUnavailableException.php | 24 +
.../ResourceUnlockableException.class.php | 24 -
.../resources/ResourceUnlockableException.php | 24 +
.../resources/SeparableRoomException.class.php | 24 -
.../resources/SeparableRoomException.php | 24 +
lib/filesystem/FileArchiveManager.class.php | 998 -------
lib/filesystem/FileArchiveManager.php | 998 +++++++
.../FileArchiveManagerException.class.php | 17 -
lib/filesystem/FileArchiveManagerException.php | 17 +
lib/filesystem/LibraryFile.class.php | 401 ---
lib/filesystem/LibraryFile.php | 401 +++
lib/filesystem/ResourceFolder.class.php | 149 -
lib/filesystem/ResourceFolder.php | 149 +
lib/ilias_interface/ConnectedIlias.class.php | 1370 ---------
lib/ilias_interface/ConnectedIlias.php | 1370 +++++++++
lib/ilias_interface/IliasModule.class.php | 371 ---
lib/ilias_interface/IliasModule.php | 371 +++
.../IliasObjectConnections.class.php | 311 --
lib/ilias_interface/IliasObjectConnections.php | 311 ++
lib/ilias_interface/IliasSoap.class.php | 1709 -----------
lib/ilias_interface/IliasSoap.php | 1709 +++++++++++
lib/ilias_interface/IliasUser.class.php | 528 ----
lib/ilias_interface/IliasUser.php | 528 ++++
lib/models/AdmissionApplication.class.php | 296 --
lib/models/AdmissionApplication.php | 296 ++
lib/models/ArchivedCourse.class.php | 105 -
lib/models/ArchivedCourse.php | 105 +
lib/models/ArchivedCourseMember.class.php | 68 -
lib/models/ArchivedCourseMember.php | 68 +
lib/models/AuthUserMd5.class.php | 42 -
lib/models/AuthUserMd5.php | 42 +
lib/models/Banner.class.php | 232 --
lib/models/Banner.php | 232 ++
lib/models/BannerRoles.class.php | 122 -
lib/models/BannerRoles.php | 122 +
lib/models/Clipboard.class.php | 335 ---
lib/models/Clipboard.php | 335 +++
lib/models/ClipboardItem.class.php | 69 -
lib/models/ClipboardItem.php | 69 +
lib/models/ColourValue.class.php | 104 -
lib/models/ColourValue.php | 104 +
lib/models/ConfigEntry.class.php | 51 -
lib/models/ConfigEntry.php | 51 +
lib/models/Contact.class.php | 51 -
lib/models/Contact.php | 51 +
lib/models/ContactGroup.class.php | 44 -
lib/models/ContactGroup.php | 44 +
lib/models/ContactGroupItem.class.php | 61 -
lib/models/ContactGroupItem.php | 61 +
lib/models/ContentTermsOfUse.class.php | 225 --
lib/models/ContentTermsOfUse.php | 225 ++
lib/models/Course.class.php | 1181 --------
lib/models/Course.php | 1181 ++++++++
lib/models/CourseDate.class.php | 665 -----
lib/models/CourseDate.php | 665 +++++
lib/models/CourseExDate.class.php | 422 ---
lib/models/CourseExDate.php | 422 +++
lib/models/CourseMember.class.php | 465 ---
lib/models/CourseMember.php | 465 +++
lib/models/CourseTopic.class.php | 261 --
lib/models/CourseTopic.php | 261 ++
lib/models/CronjobLog.class.php | 70 -
lib/models/CronjobLog.php | 70 +
lib/models/CronjobSchedule.class.php | 272 --
lib/models/CronjobSchedule.php | 272 ++
lib/models/CronjobTask.class.php | 239 --
lib/models/CronjobTask.php | 239 ++
lib/models/DataField.class.php | 289 --
lib/models/DataField.php | 289 ++
lib/models/DatafieldEntryModel.class.php | 261 --
lib/models/DatafieldEntryModel.php | 261 ++
lib/models/DatafieldEntryModelI18N.class.php | 36 -
lib/models/DatafieldEntryModelI18N.php | 36 +
lib/models/Degree.class.php | 84 -
lib/models/Degree.php | 84 +
lib/models/Freetext.php | 2 +-
lib/models/HelpContent.class.php | 189 --
lib/models/HelpContent.php | 189 ++
lib/models/HelpTour.class.php | 407 ---
lib/models/HelpTour.php | 407 +++
lib/models/HelpTourAudience.class.php | 48 -
lib/models/HelpTourAudience.php | 48 +
lib/models/HelpTourSettings.class.php | 48 -
lib/models/HelpTourSettings.php | 48 +
lib/models/HelpTourStep.class.php | 107 -
lib/models/HelpTourStep.php | 107 +
lib/models/HelpTourUser.class.php | 69 -
lib/models/HelpTourUser.php | 69 +
lib/models/Institute.class.php | 368 ---
lib/models/Institute.php | 368 +++
lib/models/InstituteMember.class.php | 221 --
lib/models/InstituteMember.php | 221 ++
lib/models/InstitutePlanColumn.class.php | 72 -
lib/models/InstitutePlanColumn.php | 72 +
lib/models/Kategorie.class.php | 76 -
lib/models/Kategorie.php | 76 +
lib/models/LikertScale.php | 2 -
lib/models/LockRule.class.php | 135 -
lib/models/LockRule.php | 135 +
lib/models/LoginBackground.class.php | 127 -
lib/models/LoginBackground.php | 127 +
lib/models/LoginFaq.class.php | 33 -
lib/models/LoginFaq.php | 33 +
lib/models/MailQueueEntry.class.php | 141 -
lib/models/MailQueueEntry.php | 141 +
lib/models/Message.class.php | 343 ---
lib/models/Message.php | 343 +++
lib/models/MessageUser.class.php | 98 -
lib/models/MessageUser.php | 98 +
lib/models/MvvOverlappingConflict.class.php | 115 -
lib/models/MvvOverlappingConflict.php | 115 +
lib/models/MvvOverlappingSelection.class.php | 339 ---
lib/models/MvvOverlappingSelection.php | 339 +++
lib/models/NewsRange.class.php | 71 -
lib/models/NewsRange.php | 71 +
lib/models/NewsRoles.class.php | 142 -
lib/models/NewsRoles.php | 142 +
lib/models/OpenGraphURL.class.php | 326 ---
lib/models/OpenGraphURL.php | 326 +++
lib/models/OpenGraphURLCollection.class.php | 57 -
lib/models/OpenGraphURLCollection.php | 57 +
lib/models/PersonalNotifications.class.php | 470 ----
lib/models/PersonalNotifications.php | 470 ++++
lib/models/RangeScale.php | 2 -
lib/models/Semester.class.php | 515 ----
lib/models/Semester.php | 515 ++++
lib/models/SemesterCourse.class.php | 45 -
lib/models/SemesterCourse.php | 45 +
lib/models/SemesterHoliday.class.php | 146 -
lib/models/SemesterHoliday.php | 146 +
lib/models/SeminarCycleDate.class.php | 819 ------
lib/models/SeminarCycleDate.php | 819 ++++++
lib/models/StudipComment.class.php | 115 -
lib/models/StudipComment.php | 115 +
lib/models/StudipNews.class.php | 696 -----
lib/models/StudipNews.php | 696 +++++
lib/models/StudipScmEntry.class.php | 55 -
lib/models/StudipScmEntry.php | 55 +
lib/models/StudipStudyArea.class.php | 637 -----
lib/models/StudipStudyArea.php | 637 +++++
lib/models/StudyCourse.class.php | 77 -
lib/models/StudyCourse.php | 77 +
lib/models/User.class.php | 1650 -----------
lib/models/User.php | 1650 +++++++++++
lib/models/UserInfo.class.php | 60 -
lib/models/UserInfo.php | 60 +
lib/models/UserOnline.class.php | 27 -
lib/models/UserOnline.php | 27 +
lib/models/UserStudyCourse.class.php | 94 -
lib/models/UserStudyCourse.php | 94 +
lib/models/Vote.php | 2 -
lib/models/WebserviceAccessRule.class.php | 133 -
lib/models/WebserviceAccessRule.php | 133 +
lib/models/WikiPage.class.php | 311 --
lib/models/WikiPage.php | 311 ++
lib/models/calendar/CalendarCourseDate.class.php | 80 -
lib/models/calendar/CalendarCourseDate.php | 80 +
lib/models/calendar/CalendarCourseExDate.class.php | 35 -
lib/models/calendar/CalendarCourseExDate.php | 35 +
lib/models/calendar/CalendarDate.class.php | 946 -------
lib/models/calendar/CalendarDate.php | 946 +++++++
.../calendar/CalendarDateAssignment.class.php | 722 -----
lib/models/calendar/CalendarDateAssignment.php | 722 +++++
.../calendar/CalendarDateException.class.php | 40 -
lib/models/calendar/CalendarDateException.php | 40 +
lib/models/resources/BrokenResource.class.php | 274 --
lib/models/resources/BrokenResource.php | 274 ++
lib/models/resources/Building.class.php | 534 ----
lib/models/resources/Building.php | 534 ++++
lib/models/resources/GlobalResourceLock.class.php | 88 -
lib/models/resources/GlobalResourceLock.php | 88 +
lib/models/resources/Location.class.php | 455 ---
lib/models/resources/Location.php | 455 +++
lib/models/resources/Resource.class.php | 2965 --------------------
lib/models/resources/Resource.php | 2965 ++++++++++++++++++++
lib/models/resources/ResourceBooking.class.php | 1946 -------------
lib/models/resources/ResourceBooking.php | 1946 +++++++++++++
.../resources/ResourceBookingInterval.class.php | 122 -
lib/models/resources/ResourceBookingInterval.php | 122 +
lib/models/resources/ResourceCategory.class.php | 806 ------
lib/models/resources/ResourceCategory.php | 806 ++++++
.../resources/ResourceCategoryProperty.class.php | 74 -
lib/models/resources/ResourceCategoryProperty.php | 74 +
lib/models/resources/ResourceLabel.class.php | 267 --
lib/models/resources/ResourceLabel.php | 267 ++
lib/models/resources/ResourcePermission.class.php | 188 --
lib/models/resources/ResourcePermission.php | 188 ++
lib/models/resources/ResourceProperty.class.php | 127 -
lib/models/resources/ResourceProperty.php | 127 +
.../resources/ResourcePropertyDefinition.class.php | 409 ---
.../resources/ResourcePropertyDefinition.php | 409 +++
.../resources/ResourcePropertyGroup.class.php | 39 -
lib/models/resources/ResourcePropertyGroup.php | 39 +
lib/models/resources/ResourceRequest.class.php | 2478 ----------------
lib/models/resources/ResourceRequest.php | 2478 ++++++++++++++++
.../resources/ResourceRequestAppointment.class.php | 49 -
.../resources/ResourceRequestAppointment.php | 49 +
.../resources/ResourceRequestProperty.class.php | 80 -
lib/models/resources/ResourceRequestProperty.php | 80 +
.../ResourceTemporaryPermission.class.php | 254 --
.../resources/ResourceTemporaryPermission.php | 254 ++
lib/models/resources/Room.class.php | 723 -----
lib/models/resources/Room.php | 723 +++++
lib/models/resources/RoomRequest.class.php | 98 -
lib/models/resources/RoomRequest.php | 98 +
lib/models/resources/SeparableRoom.class.php | 190 --
lib/models/resources/SeparableRoom.php | 190 ++
lib/models/resources/SeparableRoomPart.class.php | 54 -
lib/models/resources/SeparableRoomPart.php | 54 +
lib/modules/Blubber.class.php | 136 -
lib/modules/Blubber.php | 136 +
lib/modules/ConsultationModule.class.php | 202 --
lib/modules/ConsultationModule.php | 202 ++
lib/modules/CoreAdmin.class.php | 142 -
lib/modules/CoreAdmin.php | 142 +
lib/modules/CoreCalendar.class.php | 65 -
lib/modules/CoreCalendar.php | 65 +
lib/modules/CoreDocuments.class.php | 205 --
lib/modules/CoreDocuments.php | 205 ++
lib/modules/CoreElearningInterface.class.php | 143 -
lib/modules/CoreElearningInterface.php | 143 +
lib/modules/CoreForum.class.php | 210 --
lib/modules/CoreForum.php | 210 ++
lib/modules/CoreOverview.class.php | 125 -
lib/modules/CoreOverview.php | 125 +
lib/modules/CoreParticipants.class.php | 224 --
lib/modules/CoreParticipants.php | 224 ++
lib/modules/CorePersonal.class.php | 61 -
lib/modules/CorePersonal.php | 61 +
lib/modules/CoreSchedule.class.php | 130 -
lib/modules/CoreSchedule.php | 130 +
lib/modules/CoreScm.class.php | 163 --
lib/modules/CoreScm.php | 163 ++
lib/modules/CoreStudygroupAdmin.class.php | 70 -
lib/modules/CoreStudygroupAdmin.php | 70 +
lib/modules/CoreStudygroupParticipants.class.php | 61 -
lib/modules/CoreStudygroupParticipants.php | 61 +
lib/modules/CoreWiki.class.php | 205 --
lib/modules/CoreWiki.php | 205 ++
lib/modules/CoursewareModule.class.php | 166 --
lib/modules/CoursewareModule.php | 166 ++
lib/modules/FeedbackModule.class.php | 71 -
lib/modules/FeedbackModule.php | 71 +
lib/modules/GradebookModule.class.php | 181 --
lib/modules/GradebookModule.php | 181 ++
lib/modules/IliasInterfaceModule.class.php | 164 --
lib/modules/IliasInterfaceModule.php | 164 ++
lib/modules/LtiToolModule.class.php | 124 -
lib/modules/LtiToolModule.php | 124 +
lib/modules/StudipModule.class.php | 72 -
lib/modules/StudipModule.php | 72 +
lib/phplib/CT_Cache.class.php | 68 -
lib/phplib/CT_Cache.php | 68 +
lib/phplib/CT_Sql.class.php | 101 -
lib/phplib/CT_Sql.php | 101 +
lib/phplib/DB_Sql.class.php | 260 --
lib/phplib/DB_Sql.php | 260 ++
lib/phplib/Seminar_Auth.class.php | 450 ---
lib/phplib/Seminar_Auth.php | 450 +++
lib/phplib/Seminar_Default_Auth.class.php | 19 -
lib/phplib/Seminar_Default_Auth.php | 19 +
lib/phplib/Seminar_Perm.class.php | 350 ---
lib/phplib/Seminar_Perm.php | 350 +++
lib/phplib/Seminar_Register_Auth.class.php | 242 --
lib/phplib/Seminar_Register_Auth.php | 242 ++
lib/phplib/Seminar_Session.class.php | 436 ---
lib/phplib/Seminar_Session.php | 436 +++
lib/phplib/Seminar_User.class.php | 116 -
lib/phplib/Seminar_User.php | 116 +
lib/phplib/email_validation.class.php | 271 --
lib/phplib/email_validation.php | 271 ++
lib/plugins/core/AdminCourseAction.class.php | 22 -
lib/plugins/core/AdminCourseAction.php | 22 +
lib/plugins/core/AdminCourseContents.class.php | 25 -
lib/plugins/core/AdminCourseContents.php | 25 +
lib/plugins/core/AdminCourseWidgetPlugin.class.php | 42 -
lib/plugins/core/AdminCourseWidgetPlugin.php | 42 +
lib/plugins/core/AdministrationPlugin.class.php | 19 -
lib/plugins/core/AdministrationPlugin.php | 19 +
lib/plugins/core/DetailspagePlugin.class.php | 27 -
lib/plugins/core/DetailspagePlugin.php | 27 +
lib/plugins/core/FileUploadHook.class.php | 12 -
lib/plugins/core/FileUploadHook.php | 12 +
lib/plugins/core/FilesystemPlugin.class.php | 83 -
lib/plugins/core/FilesystemPlugin.php | 83 +
lib/plugins/core/ForumModule.class.php | 140 -
lib/plugins/core/ForumModule.php | 140 +
lib/plugins/core/HomepagePlugin.class.php | 33 -
lib/plugins/core/HomepagePlugin.php | 33 +
lib/plugins/core/LibraryPlugin.class.php | 55 -
lib/plugins/core/LibraryPlugin.php | 55 +
lib/plugins/core/MetricsPlugin.class.php | 23 -
lib/plugins/core/MetricsPlugin.php | 23 +
lib/plugins/core/PortalPlugin.class.php | 33 -
lib/plugins/core/PortalPlugin.php | 33 +
lib/plugins/core/PrivacyPlugin.class.php | 24 -
lib/plugins/core/PrivacyPlugin.php | 24 +
.../core/QuestionnaireAssignmentPlugin.class.php | 57 -
lib/plugins/core/QuestionnaireAssignmentPlugin.php | 57 +
lib/plugins/core/RESTAPIPlugin.class.php | 26 -
lib/plugins/core/RESTAPIPlugin.php | 26 +
lib/plugins/core/Role.class.php | 89 -
lib/plugins/core/Role.php | 89 +
lib/plugins/core/ScorePlugin.class.php | 29 -
lib/plugins/core/ScorePlugin.php | 29 +
lib/plugins/core/StandardPlugin.class.php | 17 -
lib/plugins/core/StandardPlugin.php | 17 +
lib/plugins/core/StudIPPlugin.class.php | 257 --
lib/plugins/core/StudIPPlugin.php | 257 ++
lib/plugins/core/SystemPlugin.class.php | 17 -
lib/plugins/core/SystemPlugin.php | 17 +
lib/plugins/core/WebServicePlugin.class.php | 14 -
lib/plugins/core/WebServicePlugin.php | 14 +
lib/plugins/db/RolePersistence.class.php | 721 -----
lib/plugins/db/RolePersistence.php | 721 +++++
lib/plugins/engine/PluginEngine.class.php | 154 -
lib/plugins/engine/PluginEngine.php | 154 +
lib/plugins/engine/PluginManager.class.php | 719 -----
lib/plugins/engine/PluginManager.php | 719 +++++
lib/plugins/engine/PluginRepository.class.php | 160 --
lib/plugins/engine/PluginRepository.php | 160 ++
lib/raumzeit/CycleData.class.php | 434 ---
lib/raumzeit/CycleData.php | 434 +++
lib/raumzeit/CycleDataDB.class.php | 271 --
lib/raumzeit/CycleDataDB.php | 271 ++
lib/raumzeit/Issue.class.php | 268 --
lib/raumzeit/Issue.php | 268 ++
lib/raumzeit/IssueDB.class.php | 153 -
lib/raumzeit/IssueDB.php | 153 +
lib/raumzeit/MetaDate.class.php | 697 -----
lib/raumzeit/MetaDate.php | 697 +++++
lib/raumzeit/MetaDateDB.class.php | 55 -
lib/raumzeit/MetaDateDB.php | 55 +
lib/raumzeit/SeminarDB.class.php | 264 --
lib/raumzeit/SeminarDB.php | 264 ++
lib/raumzeit/SingleDate.class.php | 978 -------
lib/raumzeit/SingleDate.php | 978 +++++++
lib/raumzeit/SingleDateDB.class.php | 299 --
lib/raumzeit/SingleDateDB.php | 299 ++
lib/resources/ResourceManager.class.php | 1472 ----------
lib/resources/ResourceManager.php | 1472 ++++++++++
lib/resources/RoomManager.class.php | 994 -------
lib/resources/RoomManager.php | 994 +++++++
lib/soap/StudipSoapClient.class.php | 58 -
lib/soap/StudipSoapClient.php | 58 +
lib/soap/StudipSoapClient_PHP5.class.php | 63 -
lib/soap/StudipSoapClient_PHP5.php | 63 +
public/install.php | 14 +-
tests/_support/Helper/StudipDb.php | 2 +-
tests/functional/_bootstrap.php | 22 -
tests/jsonapi/_bootstrap.php | 49 +-
tests/unit/_bootstrap.php | 56 +-
tests/unit/lib/CalendarcolumnClassTest.php | 2 +-
tests/unit/lib/CalendarviewClassTest.php | 2 +-
tests/unit/lib/VisualTest.php | 4 +-
tests/unit/lib/classes/AvatarClassTest.php | 2 +-
tests/unit/lib/classes/MarkupClassTest.php | 10 +-
tests/unit/lib/classes/PluginRepositoryTest.php | 2 +-
.../library/session/OAuthSessionAbstract.class.php | 44 -
.../library/session/OAuthSessionAbstract.php | 44 +
.../OAuthSignatureMethod.class.php | 69 -
.../signature_method/OAuthSignatureMethod.php | 69 +
.../library/store/OAuthStoreAbstract.class.php | 151 -
.../oauth-php/library/store/OAuthStoreAbstract.php | 151 +
872 files changed, 109002 insertions(+), 108992 deletions(-)
delete mode 100644 lib/admissionrules/conditionaladmission/ConditionalAdmission.class.php
create mode 100644 lib/admissionrules/conditionaladmission/ConditionalAdmission.php
delete mode 100644 lib/admissionrules/coursememberadmission/CourseMemberAdmission.class.php
create mode 100644 lib/admissionrules/coursememberadmission/CourseMemberAdmission.php
delete mode 100644 lib/admissionrules/limitedadmission/LimitedAdmission.class.php
create mode 100644 lib/admissionrules/limitedadmission/LimitedAdmission.php
delete mode 100644 lib/admissionrules/lockedadmission/LockedAdmission.class.php
create mode 100644 lib/admissionrules/lockedadmission/LockedAdmission.php
delete mode 100644 lib/admissionrules/participantrestrictedadmission/ParticipantRestrictedAdmission.class.php
create mode 100644 lib/admissionrules/participantrestrictedadmission/ParticipantRestrictedAdmission.php
delete mode 100644 lib/admissionrules/passwordadmission/PasswordAdmission.class.php
create mode 100644 lib/admissionrules/passwordadmission/PasswordAdmission.php
delete mode 100644 lib/admissionrules/preferentialadmission/PreferentialAdmission.class.php
create mode 100644 lib/admissionrules/preferentialadmission/PreferentialAdmission.php
delete mode 100644 lib/admissionrules/termsadmission/TermsAdmission.class.php
create mode 100644 lib/admissionrules/termsadmission/TermsAdmission.php
delete mode 100644 lib/admissionrules/timedadmission/TimedAdmission.class.php
create mode 100644 lib/admissionrules/timedadmission/TimedAdmission.php
delete mode 100644 lib/calendar/CalendarColumn.class.php
create mode 100644 lib/calendar/CalendarColumn.php
delete mode 100644 lib/calendar/CalendarView.class.php
create mode 100644 lib/calendar/CalendarView.php
delete mode 100644 lib/calendar/CalendarWeekView.class.php
create mode 100644 lib/calendar/CalendarWeekView.php
delete mode 100644 lib/classes/AdminCourseFilter.class.php
create mode 100644 lib/classes/AdminCourseFilter.php
delete mode 100644 lib/classes/Assets.class.php
create mode 100644 lib/classes/Assets.php
delete mode 100644 lib/classes/AuthorObject.class.php
create mode 100644 lib/classes/AuthorObject.php
delete mode 100644 lib/classes/AutoInsert.class.php
create mode 100644 lib/classes/AutoInsert.php
delete mode 100644 lib/classes/Avatar.class.php
create mode 100644 lib/classes/Avatar.php
delete mode 100644 lib/classes/BreadCrumb.class.php
create mode 100644 lib/classes/BreadCrumb.php
delete mode 100644 lib/classes/Button.class.php
create mode 100644 lib/classes/Button.php
delete mode 100644 lib/classes/CSVArrayObject.class.php
create mode 100644 lib/classes/CSVArrayObject.php
delete mode 100644 lib/classes/Color.class.php
create mode 100644 lib/classes/Color.php
delete mode 100644 lib/classes/Config.class.php
create mode 100644 lib/classes/Config.php
delete mode 100644 lib/classes/CourseAvatar.class.php
create mode 100644 lib/classes/CourseAvatar.php
delete mode 100644 lib/classes/CourseConfig.class.php
create mode 100644 lib/classes/CourseConfig.php
delete mode 100644 lib/classes/CronJob.class.php
create mode 100644 lib/classes/CronJob.php
delete mode 100644 lib/classes/CronjobScheduler.class.php
create mode 100644 lib/classes/CronjobScheduler.php
delete mode 100644 lib/classes/DBManager.class.php
create mode 100644 lib/classes/DBManager.php
delete mode 100644 lib/classes/DataFieldBoolEntry.class.php
create mode 100644 lib/classes/DataFieldBoolEntry.php
delete mode 100644 lib/classes/DataFieldComboEntry.class.php
create mode 100644 lib/classes/DataFieldComboEntry.php
delete mode 100644 lib/classes/DataFieldDateEntry.class.php
create mode 100644 lib/classes/DataFieldDateEntry.php
delete mode 100644 lib/classes/DataFieldEmailEntry.class.php
create mode 100644 lib/classes/DataFieldEmailEntry.php
delete mode 100644 lib/classes/DataFieldEntry.class.php
create mode 100644 lib/classes/DataFieldEntry.php
delete mode 100644 lib/classes/DataFieldLinkEntry.class.php
create mode 100644 lib/classes/DataFieldLinkEntry.php
delete mode 100644 lib/classes/DataFieldPhoneEntry.class.php
create mode 100644 lib/classes/DataFieldPhoneEntry.php
delete mode 100644 lib/classes/DataFieldRadioEntry.class.php
create mode 100644 lib/classes/DataFieldRadioEntry.php
delete mode 100644 lib/classes/DataFieldSelectboxEntry.class.php
create mode 100644 lib/classes/DataFieldSelectboxEntry.php
delete mode 100644 lib/classes/DataFieldSelectboxMultipleEntry.class.php
create mode 100644 lib/classes/DataFieldSelectboxMultipleEntry.php
delete mode 100644 lib/classes/DataFieldTextareaEntry.class.php
create mode 100644 lib/classes/DataFieldTextareaEntry.php
delete mode 100644 lib/classes/DataFieldTextareai18nEntry.class.php
create mode 100644 lib/classes/DataFieldTextareai18nEntry.php
delete mode 100644 lib/classes/DataFieldTextlineEntry.class.php
create mode 100644 lib/classes/DataFieldTextlineEntry.php
delete mode 100644 lib/classes/DataFieldTextlinei18nEntry.class.php
create mode 100644 lib/classes/DataFieldTextlinei18nEntry.php
delete mode 100644 lib/classes/DataFieldTextmarkupEntry.class.php
create mode 100644 lib/classes/DataFieldTextmarkupEntry.php
delete mode 100644 lib/classes/DataFieldTextmarkupi18nEntry.class.php
create mode 100644 lib/classes/DataFieldTextmarkupi18nEntry.php
delete mode 100644 lib/classes/DataFieldTimeEntry.class.php
create mode 100644 lib/classes/DataFieldTimeEntry.php
delete mode 100644 lib/classes/DatabaseObject.class.php
create mode 100644 lib/classes/DatabaseObject.php
delete mode 100644 lib/classes/DateFormatter.class.php
create mode 100644 lib/classes/DateFormatter.php
delete mode 100644 lib/classes/DbSnapshot.class.php
create mode 100644 lib/classes/DbSnapshot.php
delete mode 100644 lib/classes/DbView.class.php
create mode 100644 lib/classes/DbView.php
delete mode 100644 lib/classes/Event.interface.php
create mode 100644 lib/classes/Event.php
delete mode 100644 lib/classes/Feedback.class.php
create mode 100644 lib/classes/Feedback.php
delete mode 100644 lib/classes/FeedbackRange.interface.php
create mode 100644 lib/classes/FeedbackRange.php
delete mode 100644 lib/classes/FileLock.class.php
create mode 100644 lib/classes/FileLock.php
delete mode 100644 lib/classes/Fullcalendar.class.php
create mode 100644 lib/classes/Fullcalendar.php
delete mode 100644 lib/classes/Icon.class.php
create mode 100644 lib/classes/Icon.php
delete mode 100644 lib/classes/InstituteAvatar.class.php
create mode 100644 lib/classes/InstituteAvatar.php
delete mode 100644 lib/classes/InstituteCalendarHelper.class.php
create mode 100644 lib/classes/InstituteCalendarHelper.php
delete mode 100644 lib/classes/InstituteConfig.class.php
create mode 100644 lib/classes/InstituteConfig.php
delete mode 100644 lib/classes/Interactable.class.php
create mode 100644 lib/classes/Interactable.php
delete mode 100644 lib/classes/JSONArrayObject.class.php
create mode 100644 lib/classes/JSONArrayObject.php
delete mode 100644 lib/classes/LayoutMessage.interface.php
create mode 100644 lib/classes/LayoutMessage.php
delete mode 100644 lib/classes/LinkButton.class.php
create mode 100644 lib/classes/LinkButton.php
delete mode 100644 lib/classes/LockRules.class.php
create mode 100644 lib/classes/LockRules.php
delete mode 100644 lib/classes/Loggable.class.php
create mode 100644 lib/classes/Loggable.php
delete mode 100644 lib/classes/MVV.class.php
create mode 100644 lib/classes/MVV.php
delete mode 100644 lib/classes/Markup.class.php
create mode 100644 lib/classes/Markup.php
delete mode 100644 lib/classes/MessageBox.class.php
create mode 100644 lib/classes/MessageBox.php
delete mode 100644 lib/classes/ModulesNotification.class.php
create mode 100644 lib/classes/ModulesNotification.php
delete mode 100644 lib/classes/MultiDimArrayObject.class.php
create mode 100644 lib/classes/MultiDimArrayObject.php
delete mode 100644 lib/classes/MultiPersonSearch.class.php
create mode 100644 lib/classes/MultiPersonSearch.php
delete mode 100644 lib/classes/NotificationCenter.class.php
create mode 100644 lib/classes/NotificationCenter.php
delete mode 100644 lib/classes/PrivacyObject.interface.php
create mode 100644 lib/classes/PrivacyObject.php
delete mode 100644 lib/classes/QuestionType.interface.php
create mode 100644 lib/classes/QuestionType.php
delete mode 100644 lib/classes/QuickSearch.class.php
create mode 100644 lib/classes/QuickSearch.php
delete mode 100644 lib/classes/Range.interface.php
create mode 100644 lib/classes/Range.php
delete mode 100644 lib/classes/RangeConfig.class.php
create mode 100644 lib/classes/RangeConfig.php
delete mode 100644 lib/classes/RangeTreeObject.class.php
create mode 100644 lib/classes/RangeTreeObject.php
delete mode 100644 lib/classes/RangeTreeObjectFak.class.php
create mode 100644 lib/classes/RangeTreeObjectFak.php
delete mode 100644 lib/classes/RangeTreeObjectInst.class.php
create mode 100644 lib/classes/RangeTreeObjectInst.php
delete mode 100644 lib/classes/Request.class.php
create mode 100644 lib/classes/Request.php
delete mode 100644 lib/classes/ResetButton.class.php
create mode 100644 lib/classes/ResetButton.php
delete mode 100644 lib/classes/Score.class.php
create mode 100644 lib/classes/Score.php
delete mode 100644 lib/classes/SemBrowse.class.php
create mode 100644 lib/classes/SemBrowse.php
delete mode 100644 lib/classes/SemClass.class.php
create mode 100644 lib/classes/SemClass.php
delete mode 100644 lib/classes/SemType.class.php
create mode 100644 lib/classes/SemType.php
delete mode 100644 lib/classes/Seminar.class.php
create mode 100644 lib/classes/Seminar.php
delete mode 100644 lib/classes/SeminarCategories.class.php
create mode 100644 lib/classes/SeminarCategories.php
delete mode 100644 lib/classes/SessionDecoder.class.php
create mode 100644 lib/classes/SessionDecoder.php
delete mode 100644 lib/classes/SimpleCollection.class.php
create mode 100644 lib/classes/SimpleCollection.php
delete mode 100644 lib/classes/SimpleORMap.class.php
create mode 100644 lib/classes/SimpleORMap.php
delete mode 100644 lib/classes/SimpleORMapCollection.class.php
create mode 100644 lib/classes/SimpleORMapCollection.php
delete mode 100644 lib/classes/StudipArrayObject.class.php
create mode 100644 lib/classes/StudipArrayObject.php
delete mode 100644 lib/classes/StudipForm.class.php
create mode 100644 lib/classes/StudipForm.php
delete mode 100644 lib/classes/StudipItem.interface.php
create mode 100644 lib/classes/StudipItem.php
delete mode 100644 lib/classes/StudipKing.class.php
create mode 100644 lib/classes/StudipKing.php
delete mode 100644 lib/classes/StudipLink.class.php
create mode 100644 lib/classes/StudipLink.php
delete mode 100644 lib/classes/StudipLock.class.php
create mode 100644 lib/classes/StudipLock.php
delete mode 100644 lib/classes/StudipLog.class.php
create mode 100644 lib/classes/StudipLog.php
delete mode 100644 lib/classes/StudipLvgruppeSelection.class.php
create mode 100644 lib/classes/StudipLvgruppeSelection.php
delete mode 100644 lib/classes/StudipMail.class.php
create mode 100644 lib/classes/StudipMail.php
delete mode 100644 lib/classes/StudipObject.class.php
create mode 100644 lib/classes/StudipObject.php
delete mode 100644 lib/classes/StudipPDO.class.php
create mode 100644 lib/classes/StudipPDO.php
delete mode 100644 lib/classes/StudipRangeTree.class.php
create mode 100644 lib/classes/StudipRangeTree.php
delete mode 100644 lib/classes/StudipRangeTreeView.class.php
create mode 100644 lib/classes/StudipRangeTreeView.php
delete mode 100644 lib/classes/StudipRangeTreeViewAdmin.class.php
create mode 100644 lib/classes/StudipRangeTreeViewAdmin.php
delete mode 100644 lib/classes/StudipSemRangeTreeViewSimple.class.php
create mode 100644 lib/classes/StudipSemRangeTreeViewSimple.php
delete mode 100644 lib/classes/StudipSemSearch.class.php
create mode 100644 lib/classes/StudipSemSearch.php
delete mode 100644 lib/classes/StudipSemSearchHelper.class.php
create mode 100644 lib/classes/StudipSemSearchHelper.php
delete mode 100644 lib/classes/StudipSemTree.class.php
create mode 100644 lib/classes/StudipSemTree.php
delete mode 100644 lib/classes/StudipSemTreeSearch.class.php
create mode 100644 lib/classes/StudipSemTreeSearch.php
delete mode 100644 lib/classes/StudipSemTreeView.class.php
create mode 100644 lib/classes/StudipSemTreeView.php
delete mode 100644 lib/classes/StudipSemTreeViewAdmin.class.php
create mode 100644 lib/classes/StudipSemTreeViewAdmin.php
delete mode 100644 lib/classes/StudipSemTreeViewSimple.class.php
create mode 100644 lib/classes/StudipSemTreeViewSimple.php
delete mode 100644 lib/classes/StudipStudyAreaSelection.class.php
create mode 100644 lib/classes/StudipStudyAreaSelection.php
delete mode 100644 lib/classes/StudygroupAvatar.class.php
create mode 100644 lib/classes/StudygroupAvatar.php
delete mode 100644 lib/classes/TreeAbstract.class.php
create mode 100644 lib/classes/TreeAbstract.php
delete mode 100644 lib/classes/TreeView.class.php
create mode 100644 lib/classes/TreeView.php
delete mode 100644 lib/classes/UpdateInformation.class.php
create mode 100644 lib/classes/UpdateInformation.php
delete mode 100644 lib/classes/UserConfig.class.php
create mode 100644 lib/classes/UserConfig.php
delete mode 100644 lib/classes/UserLookup.class.php
create mode 100644 lib/classes/UserLookup.php
delete mode 100644 lib/classes/UserManagement.class.php
create mode 100644 lib/classes/UserManagement.php
delete mode 100644 lib/classes/admission/AdmissionAlgorithm.class.php
create mode 100644 lib/classes/admission/AdmissionAlgorithm.php
delete mode 100644 lib/classes/admission/AdmissionPriority.class.php
create mode 100644 lib/classes/admission/AdmissionPriority.php
delete mode 100644 lib/classes/admission/AdmissionRule.class.php
create mode 100644 lib/classes/admission/AdmissionRule.php
delete mode 100644 lib/classes/admission/AdmissionUserList.class.php
create mode 100644 lib/classes/admission/AdmissionUserList.php
delete mode 100644 lib/classes/admission/CourseSet.class.php
create mode 100644 lib/classes/admission/CourseSet.php
delete mode 100644 lib/classes/admission/RandomAlgorithm.class.php
create mode 100644 lib/classes/admission/RandomAlgorithm.php
delete mode 100644 lib/classes/admission/UserFilter.class.php
create mode 100644 lib/classes/admission/UserFilter.php
delete mode 100644 lib/classes/admission/UserFilterField.class.php
create mode 100644 lib/classes/admission/UserFilterField.php
delete mode 100644 lib/classes/admission/userfilter/DatafieldCondition.class.php
create mode 100644 lib/classes/admission/userfilter/DatafieldCondition.php
delete mode 100644 lib/classes/admission/userfilter/DegreeCondition.class.php
create mode 100644 lib/classes/admission/userfilter/DegreeCondition.php
delete mode 100644 lib/classes/admission/userfilter/PermissionCondition.class.php
create mode 100644 lib/classes/admission/userfilter/PermissionCondition.php
delete mode 100644 lib/classes/admission/userfilter/SemesterOfStudyCondition.class.php
create mode 100644 lib/classes/admission/userfilter/SemesterOfStudyCondition.php
delete mode 100644 lib/classes/admission/userfilter/StgteilVersionCondition.class.php
create mode 100644 lib/classes/admission/userfilter/StgteilVersionCondition.php
delete mode 100644 lib/classes/admission/userfilter/SubjectCondition.class.php
create mode 100644 lib/classes/admission/userfilter/SubjectCondition.php
delete mode 100644 lib/classes/admission/userfilter/SubjectConditionAny.class.php
create mode 100644 lib/classes/admission/userfilter/SubjectConditionAny.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthAbstract.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthAbstract.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthCAS.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthCAS.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthIP.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthIP.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthLTI.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthLTI.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthLdap.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthLdap.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthLdapReadAndBind.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthLdapReadAndBind.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthOIDC.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthOIDC.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthSSO.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthSSO.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthShib.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthShib.php
delete mode 100644 lib/classes/auth_plugins/StudipAuthStandard.class.php
create mode 100644 lib/classes/auth_plugins/StudipAuthStandard.php
delete mode 100644 lib/classes/cache/Cache.class.php
create mode 100644 lib/classes/cache/Cache.php
delete mode 100644 lib/classes/cache/DbCache.class.php
create mode 100644 lib/classes/cache/DbCache.php
delete mode 100644 lib/classes/cache/Factory.class.php
create mode 100644 lib/classes/cache/Factory.php
delete mode 100644 lib/classes/cache/FileCache.class.php
create mode 100644 lib/classes/cache/FileCache.php
delete mode 100644 lib/classes/cache/Item.class.php
create mode 100644 lib/classes/cache/Item.php
delete mode 100644 lib/classes/cache/MemcachedCache.class.php
create mode 100644 lib/classes/cache/MemcachedCache.php
delete mode 100644 lib/classes/cache/MemoryCache.class.php
create mode 100644 lib/classes/cache/MemoryCache.php
delete mode 100644 lib/classes/cache/Proxy.class.php
create mode 100644 lib/classes/cache/Proxy.php
delete mode 100644 lib/classes/cache/RedisCache.class.php
create mode 100644 lib/classes/cache/RedisCache.php
delete mode 100644 lib/classes/cache/Wrapper.class.php
create mode 100644 lib/classes/cache/Wrapper.php
delete mode 100644 lib/classes/calendar/EventData.class.php
create mode 100644 lib/classes/calendar/EventData.php
delete mode 100644 lib/classes/calendar/EventSource.interface.php
create mode 100644 lib/classes/calendar/EventSource.php
delete mode 100644 lib/classes/calendar/ICalendarExport.class.php
create mode 100644 lib/classes/calendar/ICalendarExport.php
delete mode 100644 lib/classes/calendar/ICalendarImport.class.php
create mode 100644 lib/classes/calendar/ICalendarImport.php
delete mode 100644 lib/classes/calendar/Owner.interface.php
create mode 100644 lib/classes/calendar/Owner.php
delete mode 100644 lib/classes/exportdocument/ExportDocument.interface.php
create mode 100644 lib/classes/exportdocument/ExportDocument.php
delete mode 100644 lib/classes/exportdocument/ExportPDF.class.php
create mode 100644 lib/classes/exportdocument/ExportPDF.php
delete mode 100644 lib/classes/librarysearch/LibraryDocument.class.php
create mode 100644 lib/classes/librarysearch/LibraryDocument.php
delete mode 100644 lib/classes/librarysearch/LibraryResultParser.interface.php
create mode 100644 lib/classes/librarysearch/LibraryResultParser.php
delete mode 100644 lib/classes/librarysearch/LibrarySearch.class.php
create mode 100644 lib/classes/librarysearch/LibrarySearch.php
delete mode 100644 lib/classes/librarysearch/LibrarySearchManager.class.php
create mode 100644 lib/classes/librarysearch/LibrarySearchManager.php
delete mode 100644 lib/classes/librarysearch/resultparsers/BASELibraryResultParser.class.php
create mode 100644 lib/classes/librarysearch/resultparsers/BASELibraryResultParser.php
delete mode 100644 lib/classes/librarysearch/resultparsers/K10PlusLibraryResultParser.class.php
create mode 100644 lib/classes/librarysearch/resultparsers/K10PlusLibraryResultParser.php
delete mode 100644 lib/classes/librarysearch/resultparsers/MarcxmlLibraryResultParser.class.php
create mode 100644 lib/classes/librarysearch/resultparsers/MarcxmlLibraryResultParser.php
delete mode 100644 lib/classes/librarysearch/resultparsers/SRULibraryResultParser.class.php
create mode 100644 lib/classes/librarysearch/resultparsers/SRULibraryResultParser.php
delete mode 100644 lib/classes/librarysearch/searchmodules/BASELibrarySearch.class.php
create mode 100644 lib/classes/librarysearch/searchmodules/BASELibrarySearch.php
delete mode 100644 lib/classes/librarysearch/searchmodules/K10PlusZentralLibrarySearch.class.php
create mode 100644 lib/classes/librarysearch/searchmodules/K10PlusZentralLibrarySearch.php
delete mode 100644 lib/classes/librarysearch/searchmodules/SRULibrarySearch.class.php
create mode 100644 lib/classes/librarysearch/searchmodules/SRULibrarySearch.php
delete mode 100644 lib/classes/searchtypes/MyCoursesSearch.class.php
create mode 100644 lib/classes/searchtypes/MyCoursesSearch.php
delete mode 100644 lib/classes/searchtypes/PermissionSearch.class.php
create mode 100644 lib/classes/searchtypes/PermissionSearch.php
delete mode 100644 lib/classes/searchtypes/RangeSearch.class.php
create mode 100644 lib/classes/searchtypes/RangeSearch.php
delete mode 100644 lib/classes/searchtypes/ResourceSearch.class.php
create mode 100644 lib/classes/searchtypes/ResourceSearch.php
delete mode 100644 lib/classes/searchtypes/RoomSearch.class.php
create mode 100644 lib/classes/searchtypes/RoomSearch.php
delete mode 100644 lib/classes/searchtypes/SQLSearch.class.php
create mode 100644 lib/classes/searchtypes/SQLSearch.php
delete mode 100644 lib/classes/searchtypes/SearchType.class.php
create mode 100644 lib/classes/searchtypes/SearchType.php
delete mode 100644 lib/classes/searchtypes/SeminarSearch.class.php
create mode 100644 lib/classes/searchtypes/SeminarSearch.php
delete mode 100644 lib/classes/searchtypes/StandardSearch.class.php
create mode 100644 lib/classes/searchtypes/StandardSearch.php
delete mode 100644 lib/classes/searchtypes/TreeSearch.class.php
create mode 100644 lib/classes/searchtypes/TreeSearch.php
delete mode 100644 lib/classes/sidebar/ClipboardWidget.class.php
create mode 100644 lib/classes/sidebar/ClipboardWidget.php
delete mode 100644 lib/classes/sidebar/InstituteSelectWidget.class.php
create mode 100644 lib/classes/sidebar/InstituteSelectWidget.php
delete mode 100644 lib/classes/sidebar/ResourceTreeWidget.class.php
create mode 100644 lib/classes/sidebar/ResourceTreeWidget.php
delete mode 100644 lib/classes/sidebar/RoomClipboardWidget.class.php
create mode 100644 lib/classes/sidebar/RoomClipboardWidget.php
delete mode 100644 lib/classes/sidebar/RoomSearchTreeWidget.class.php
create mode 100644 lib/classes/sidebar/RoomSearchTreeWidget.php
delete mode 100644 lib/classes/sidebar/RoomSearchWidget.class.php
create mode 100644 lib/classes/sidebar/RoomSearchWidget.php
delete mode 100644 lib/cronjobs/check_admission.class.php
create mode 100644 lib/cronjobs/check_admission.php
delete mode 100644 lib/cronjobs/cleanup_log.class.php
create mode 100644 lib/cronjobs/cleanup_log.php
delete mode 100644 lib/cronjobs/garbage_collector.class.php
create mode 100644 lib/cronjobs/garbage_collector.php
delete mode 100644 lib/cronjobs/purge_cache.class.php
create mode 100644 lib/cronjobs/purge_cache.php
delete mode 100644 lib/cronjobs/remind_oer_upload.class.php
create mode 100644 lib/cronjobs/remind_oer_upload.php
delete mode 100644 lib/cronjobs/send_mail_notifications.class.php
create mode 100644 lib/cronjobs/send_mail_notifications.php
delete mode 100644 lib/cronjobs/send_mail_queue.class.php
create mode 100644 lib/cronjobs/send_mail_queue.php
delete mode 100644 lib/cronjobs/session_gc.class.php
create mode 100644 lib/cronjobs/session_gc.php
delete mode 100644 lib/elearning/ConnectedCMS.class.php
create mode 100644 lib/elearning/ConnectedCMS.php
delete mode 100644 lib/elearning/ConnectedLink.class.php
create mode 100644 lib/elearning/ConnectedLink.php
delete mode 100644 lib/elearning/ConnectedPermissions.class.php
create mode 100644 lib/elearning/ConnectedPermissions.php
delete mode 100644 lib/elearning/ConnectedUser.class.php
create mode 100644 lib/elearning/ConnectedUser.php
delete mode 100644 lib/elearning/ContentModule.class.php
create mode 100644 lib/elearning/ContentModule.php
delete mode 100644 lib/elearning/ContentModuleView.class.php
create mode 100644 lib/elearning/ContentModuleView.php
delete mode 100644 lib/elearning/ELearningUtils.class.php
create mode 100644 lib/elearning/ELearningUtils.php
delete mode 100644 lib/elearning/Ilias3ConnectedCMS.class.php
create mode 100644 lib/elearning/Ilias3ConnectedCMS.php
delete mode 100644 lib/elearning/Ilias3ConnectedLink.class.php
create mode 100644 lib/elearning/Ilias3ConnectedLink.php
delete mode 100644 lib/elearning/Ilias3ConnectedPermissions.class.php
create mode 100644 lib/elearning/Ilias3ConnectedPermissions.php
delete mode 100644 lib/elearning/Ilias3ConnectedUser.class.php
create mode 100644 lib/elearning/Ilias3ConnectedUser.php
delete mode 100644 lib/elearning/Ilias3ContentModule.class.php
create mode 100644 lib/elearning/Ilias3ContentModule.php
delete mode 100644 lib/elearning/Ilias3ObjectXMLParser.class.php
create mode 100644 lib/elearning/Ilias3ObjectXMLParser.php
delete mode 100644 lib/elearning/Ilias3SaxParser.class.php
create mode 100644 lib/elearning/Ilias3SaxParser.php
delete mode 100644 lib/elearning/Ilias3Soap.class.php
create mode 100644 lib/elearning/Ilias3Soap.php
delete mode 100644 lib/elearning/Ilias4ConnectedCMS.class.php
create mode 100644 lib/elearning/Ilias4ConnectedCMS.php
delete mode 100644 lib/elearning/Ilias4ConnectedLink.class.php
create mode 100644 lib/elearning/Ilias4ConnectedLink.php
delete mode 100644 lib/elearning/Ilias4ConnectedPermissions.class.php
create mode 100644 lib/elearning/Ilias4ConnectedPermissions.php
delete mode 100644 lib/elearning/Ilias4ConnectedUser.class.php
create mode 100644 lib/elearning/Ilias4ConnectedUser.php
delete mode 100644 lib/elearning/Ilias4ContentModule.class.php
create mode 100644 lib/elearning/Ilias4ContentModule.php
delete mode 100644 lib/elearning/Ilias4Soap.class.php
create mode 100644 lib/elearning/Ilias4Soap.php
delete mode 100644 lib/elearning/Ilias5ConnectedCMS.class.php
create mode 100644 lib/elearning/Ilias5ConnectedCMS.php
delete mode 100644 lib/elearning/Ilias5ConnectedLink.class.php
create mode 100644 lib/elearning/Ilias5ConnectedLink.php
delete mode 100644 lib/elearning/Ilias5ConnectedPermissions.class.php
create mode 100644 lib/elearning/Ilias5ConnectedPermissions.php
delete mode 100644 lib/elearning/Ilias5ConnectedUser.class.php
create mode 100644 lib/elearning/Ilias5ConnectedUser.php
delete mode 100644 lib/elearning/Ilias5ContentModule.class.php
create mode 100644 lib/elearning/Ilias5ContentModule.php
delete mode 100644 lib/elearning/Ilias5Soap.class.php
create mode 100644 lib/elearning/Ilias5Soap.php
delete mode 100644 lib/elearning/LonCapaConnectedCMS.class.php
create mode 100644 lib/elearning/LonCapaConnectedCMS.php
delete mode 100644 lib/elearning/LonCapaConnectedLink.class.php
create mode 100644 lib/elearning/LonCapaConnectedLink.php
delete mode 100644 lib/elearning/LonCapaContentModule.class.php
create mode 100644 lib/elearning/LonCapaContentModule.php
delete mode 100644 lib/elearning/LonCapaRequest.class.php
create mode 100644 lib/elearning/LonCapaRequest.php
delete mode 100644 lib/elearning/ObjectConnections.class.php
create mode 100644 lib/elearning/ObjectConnections.php
delete mode 100644 lib/elearning/PmWikiConnectedCMS.class.php
create mode 100644 lib/elearning/PmWikiConnectedCMS.php
delete mode 100644 lib/elearning/PmWikiConnectedLink.class.php
create mode 100644 lib/elearning/PmWikiConnectedLink.php
delete mode 100644 lib/elearning/PmWikiContentModule.class.php
create mode 100644 lib/elearning/PmWikiContentModule.php
delete mode 100644 lib/exceptions/ClipboardException.class.php
create mode 100644 lib/exceptions/ClipboardException.php
delete mode 100644 lib/exceptions/resources/GlobalResourceLockException.class.php
create mode 100644 lib/exceptions/resources/GlobalResourceLockException.php
delete mode 100644 lib/exceptions/resources/GlobalResourceLockOverlapException.class.php
create mode 100644 lib/exceptions/resources/GlobalResourceLockOverlapException.php
delete mode 100644 lib/exceptions/resources/InvalidResourceCategoryException.class.php
create mode 100644 lib/exceptions/resources/InvalidResourceCategoryException.php
delete mode 100644 lib/exceptions/resources/InvalidResourceClassException.class.php
create mode 100644 lib/exceptions/resources/InvalidResourceClassException.php
delete mode 100644 lib/exceptions/resources/InvalidResourceException.class.php
create mode 100644 lib/exceptions/resources/InvalidResourceException.php
delete mode 100644 lib/exceptions/resources/InvalidResourceRequestException.class.php
create mode 100644 lib/exceptions/resources/InvalidResourceRequestException.php
delete mode 100644 lib/exceptions/resources/NoResourceClassException.class.php
create mode 100644 lib/exceptions/resources/NoResourceClassException.php
delete mode 100644 lib/exceptions/resources/ResourceBookingException.class.php
create mode 100644 lib/exceptions/resources/ResourceBookingException.php
delete mode 100644 lib/exceptions/resources/ResourceBookingOverlapException.class.php
create mode 100644 lib/exceptions/resources/ResourceBookingOverlapException.php
delete mode 100644 lib/exceptions/resources/ResourceBookingRangeException.class.php
create mode 100644 lib/exceptions/resources/ResourceBookingRangeException.php
delete mode 100644 lib/exceptions/resources/ResourceException.class.php
create mode 100644 lib/exceptions/resources/ResourceException.php
delete mode 100644 lib/exceptions/resources/ResourceNoTimeRangeException.class.php
create mode 100644 lib/exceptions/resources/ResourceNoTimeRangeException.php
delete mode 100644 lib/exceptions/resources/ResourcePermissionException.class.php
create mode 100644 lib/exceptions/resources/ResourcePermissionException.php
delete mode 100644 lib/exceptions/resources/ResourcePropertyDefinitionException.class.php
create mode 100644 lib/exceptions/resources/ResourcePropertyDefinitionException.php
delete mode 100644 lib/exceptions/resources/ResourcePropertyException.class.php
create mode 100644 lib/exceptions/resources/ResourcePropertyException.php
delete mode 100644 lib/exceptions/resources/ResourcePropertyStateException.class.php
create mode 100644 lib/exceptions/resources/ResourcePropertyStateException.php
delete mode 100644 lib/exceptions/resources/ResourceRequestException.class.php
create mode 100644 lib/exceptions/resources/ResourceRequestException.php
delete mode 100644 lib/exceptions/resources/ResourceUnavailableException.class.php
create mode 100644 lib/exceptions/resources/ResourceUnavailableException.php
delete mode 100644 lib/exceptions/resources/ResourceUnlockableException.class.php
create mode 100644 lib/exceptions/resources/ResourceUnlockableException.php
delete mode 100644 lib/exceptions/resources/SeparableRoomException.class.php
create mode 100644 lib/exceptions/resources/SeparableRoomException.php
delete mode 100644 lib/filesystem/FileArchiveManager.class.php
create mode 100644 lib/filesystem/FileArchiveManager.php
delete mode 100644 lib/filesystem/FileArchiveManagerException.class.php
create mode 100644 lib/filesystem/FileArchiveManagerException.php
delete mode 100644 lib/filesystem/LibraryFile.class.php
create mode 100644 lib/filesystem/LibraryFile.php
delete mode 100644 lib/filesystem/ResourceFolder.class.php
create mode 100644 lib/filesystem/ResourceFolder.php
delete mode 100644 lib/ilias_interface/ConnectedIlias.class.php
create mode 100644 lib/ilias_interface/ConnectedIlias.php
delete mode 100644 lib/ilias_interface/IliasModule.class.php
create mode 100644 lib/ilias_interface/IliasModule.php
delete mode 100644 lib/ilias_interface/IliasObjectConnections.class.php
create mode 100644 lib/ilias_interface/IliasObjectConnections.php
delete mode 100644 lib/ilias_interface/IliasSoap.class.php
create mode 100644 lib/ilias_interface/IliasSoap.php
delete mode 100644 lib/ilias_interface/IliasUser.class.php
create mode 100644 lib/ilias_interface/IliasUser.php
delete mode 100644 lib/models/AdmissionApplication.class.php
create mode 100644 lib/models/AdmissionApplication.php
delete mode 100644 lib/models/ArchivedCourse.class.php
create mode 100644 lib/models/ArchivedCourse.php
delete mode 100644 lib/models/ArchivedCourseMember.class.php
create mode 100644 lib/models/ArchivedCourseMember.php
delete mode 100644 lib/models/AuthUserMd5.class.php
create mode 100644 lib/models/AuthUserMd5.php
delete mode 100644 lib/models/Banner.class.php
create mode 100644 lib/models/Banner.php
delete mode 100644 lib/models/BannerRoles.class.php
create mode 100644 lib/models/BannerRoles.php
delete mode 100644 lib/models/Clipboard.class.php
create mode 100644 lib/models/Clipboard.php
delete mode 100644 lib/models/ClipboardItem.class.php
create mode 100644 lib/models/ClipboardItem.php
delete mode 100644 lib/models/ColourValue.class.php
create mode 100644 lib/models/ColourValue.php
delete mode 100644 lib/models/ConfigEntry.class.php
create mode 100644 lib/models/ConfigEntry.php
delete mode 100644 lib/models/Contact.class.php
create mode 100644 lib/models/Contact.php
delete mode 100644 lib/models/ContactGroup.class.php
create mode 100644 lib/models/ContactGroup.php
delete mode 100644 lib/models/ContactGroupItem.class.php
create mode 100644 lib/models/ContactGroupItem.php
delete mode 100644 lib/models/ContentTermsOfUse.class.php
create mode 100644 lib/models/ContentTermsOfUse.php
delete mode 100644 lib/models/Course.class.php
create mode 100644 lib/models/Course.php
delete mode 100644 lib/models/CourseDate.class.php
create mode 100644 lib/models/CourseDate.php
delete mode 100644 lib/models/CourseExDate.class.php
create mode 100644 lib/models/CourseExDate.php
delete mode 100644 lib/models/CourseMember.class.php
create mode 100644 lib/models/CourseMember.php
delete mode 100644 lib/models/CourseTopic.class.php
create mode 100644 lib/models/CourseTopic.php
delete mode 100644 lib/models/CronjobLog.class.php
create mode 100644 lib/models/CronjobLog.php
delete mode 100644 lib/models/CronjobSchedule.class.php
create mode 100644 lib/models/CronjobSchedule.php
delete mode 100644 lib/models/CronjobTask.class.php
create mode 100644 lib/models/CronjobTask.php
delete mode 100644 lib/models/DataField.class.php
create mode 100644 lib/models/DataField.php
delete mode 100644 lib/models/DatafieldEntryModel.class.php
create mode 100644 lib/models/DatafieldEntryModel.php
delete mode 100644 lib/models/DatafieldEntryModelI18N.class.php
create mode 100644 lib/models/DatafieldEntryModelI18N.php
delete mode 100644 lib/models/Degree.class.php
create mode 100644 lib/models/Degree.php
delete mode 100644 lib/models/HelpContent.class.php
create mode 100644 lib/models/HelpContent.php
delete mode 100644 lib/models/HelpTour.class.php
create mode 100644 lib/models/HelpTour.php
delete mode 100644 lib/models/HelpTourAudience.class.php
create mode 100644 lib/models/HelpTourAudience.php
delete mode 100644 lib/models/HelpTourSettings.class.php
create mode 100644 lib/models/HelpTourSettings.php
delete mode 100644 lib/models/HelpTourStep.class.php
create mode 100644 lib/models/HelpTourStep.php
delete mode 100644 lib/models/HelpTourUser.class.php
create mode 100644 lib/models/HelpTourUser.php
delete mode 100644 lib/models/Institute.class.php
create mode 100644 lib/models/Institute.php
delete mode 100644 lib/models/InstituteMember.class.php
create mode 100644 lib/models/InstituteMember.php
delete mode 100644 lib/models/InstitutePlanColumn.class.php
create mode 100644 lib/models/InstitutePlanColumn.php
delete mode 100644 lib/models/Kategorie.class.php
create mode 100644 lib/models/Kategorie.php
delete mode 100644 lib/models/LockRule.class.php
create mode 100644 lib/models/LockRule.php
delete mode 100644 lib/models/LoginBackground.class.php
create mode 100644 lib/models/LoginBackground.php
delete mode 100644 lib/models/LoginFaq.class.php
create mode 100644 lib/models/LoginFaq.php
delete mode 100644 lib/models/MailQueueEntry.class.php
create mode 100644 lib/models/MailQueueEntry.php
delete mode 100644 lib/models/Message.class.php
create mode 100644 lib/models/Message.php
delete mode 100644 lib/models/MessageUser.class.php
create mode 100644 lib/models/MessageUser.php
delete mode 100644 lib/models/MvvOverlappingConflict.class.php
create mode 100644 lib/models/MvvOverlappingConflict.php
delete mode 100644 lib/models/MvvOverlappingSelection.class.php
create mode 100644 lib/models/MvvOverlappingSelection.php
delete mode 100644 lib/models/NewsRange.class.php
create mode 100644 lib/models/NewsRange.php
delete mode 100644 lib/models/NewsRoles.class.php
create mode 100644 lib/models/NewsRoles.php
delete mode 100644 lib/models/OpenGraphURL.class.php
create mode 100644 lib/models/OpenGraphURL.php
delete mode 100644 lib/models/OpenGraphURLCollection.class.php
create mode 100644 lib/models/OpenGraphURLCollection.php
delete mode 100644 lib/models/PersonalNotifications.class.php
create mode 100644 lib/models/PersonalNotifications.php
delete mode 100644 lib/models/Semester.class.php
create mode 100644 lib/models/Semester.php
delete mode 100644 lib/models/SemesterCourse.class.php
create mode 100644 lib/models/SemesterCourse.php
delete mode 100644 lib/models/SemesterHoliday.class.php
create mode 100644 lib/models/SemesterHoliday.php
delete mode 100644 lib/models/SeminarCycleDate.class.php
create mode 100644 lib/models/SeminarCycleDate.php
delete mode 100644 lib/models/StudipComment.class.php
create mode 100644 lib/models/StudipComment.php
delete mode 100644 lib/models/StudipNews.class.php
create mode 100644 lib/models/StudipNews.php
delete mode 100644 lib/models/StudipScmEntry.class.php
create mode 100644 lib/models/StudipScmEntry.php
delete mode 100644 lib/models/StudipStudyArea.class.php
create mode 100644 lib/models/StudipStudyArea.php
delete mode 100644 lib/models/StudyCourse.class.php
create mode 100644 lib/models/StudyCourse.php
delete mode 100644 lib/models/User.class.php
create mode 100644 lib/models/User.php
delete mode 100644 lib/models/UserInfo.class.php
create mode 100644 lib/models/UserInfo.php
delete mode 100644 lib/models/UserOnline.class.php
create mode 100644 lib/models/UserOnline.php
delete mode 100644 lib/models/UserStudyCourse.class.php
create mode 100644 lib/models/UserStudyCourse.php
delete mode 100644 lib/models/WebserviceAccessRule.class.php
create mode 100644 lib/models/WebserviceAccessRule.php
delete mode 100644 lib/models/WikiPage.class.php
create mode 100644 lib/models/WikiPage.php
delete mode 100644 lib/models/calendar/CalendarCourseDate.class.php
create mode 100644 lib/models/calendar/CalendarCourseDate.php
delete mode 100644 lib/models/calendar/CalendarCourseExDate.class.php
create mode 100644 lib/models/calendar/CalendarCourseExDate.php
delete mode 100644 lib/models/calendar/CalendarDate.class.php
create mode 100644 lib/models/calendar/CalendarDate.php
delete mode 100644 lib/models/calendar/CalendarDateAssignment.class.php
create mode 100644 lib/models/calendar/CalendarDateAssignment.php
delete mode 100644 lib/models/calendar/CalendarDateException.class.php
create mode 100644 lib/models/calendar/CalendarDateException.php
delete mode 100644 lib/models/resources/BrokenResource.class.php
create mode 100644 lib/models/resources/BrokenResource.php
delete mode 100644 lib/models/resources/Building.class.php
create mode 100644 lib/models/resources/Building.php
delete mode 100644 lib/models/resources/GlobalResourceLock.class.php
create mode 100644 lib/models/resources/GlobalResourceLock.php
delete mode 100644 lib/models/resources/Location.class.php
create mode 100644 lib/models/resources/Location.php
delete mode 100644 lib/models/resources/Resource.class.php
create mode 100644 lib/models/resources/Resource.php
delete mode 100644 lib/models/resources/ResourceBooking.class.php
create mode 100644 lib/models/resources/ResourceBooking.php
delete mode 100644 lib/models/resources/ResourceBookingInterval.class.php
create mode 100644 lib/models/resources/ResourceBookingInterval.php
delete mode 100644 lib/models/resources/ResourceCategory.class.php
create mode 100644 lib/models/resources/ResourceCategory.php
delete mode 100644 lib/models/resources/ResourceCategoryProperty.class.php
create mode 100644 lib/models/resources/ResourceCategoryProperty.php
delete mode 100644 lib/models/resources/ResourceLabel.class.php
create mode 100644 lib/models/resources/ResourceLabel.php
delete mode 100644 lib/models/resources/ResourcePermission.class.php
create mode 100644 lib/models/resources/ResourcePermission.php
delete mode 100644 lib/models/resources/ResourceProperty.class.php
create mode 100644 lib/models/resources/ResourceProperty.php
delete mode 100644 lib/models/resources/ResourcePropertyDefinition.class.php
create mode 100644 lib/models/resources/ResourcePropertyDefinition.php
delete mode 100644 lib/models/resources/ResourcePropertyGroup.class.php
create mode 100644 lib/models/resources/ResourcePropertyGroup.php
delete mode 100644 lib/models/resources/ResourceRequest.class.php
create mode 100644 lib/models/resources/ResourceRequest.php
delete mode 100644 lib/models/resources/ResourceRequestAppointment.class.php
create mode 100644 lib/models/resources/ResourceRequestAppointment.php
delete mode 100644 lib/models/resources/ResourceRequestProperty.class.php
create mode 100644 lib/models/resources/ResourceRequestProperty.php
delete mode 100644 lib/models/resources/ResourceTemporaryPermission.class.php
create mode 100644 lib/models/resources/ResourceTemporaryPermission.php
delete mode 100644 lib/models/resources/Room.class.php
create mode 100644 lib/models/resources/Room.php
delete mode 100644 lib/models/resources/RoomRequest.class.php
create mode 100644 lib/models/resources/RoomRequest.php
delete mode 100644 lib/models/resources/SeparableRoom.class.php
create mode 100644 lib/models/resources/SeparableRoom.php
delete mode 100644 lib/models/resources/SeparableRoomPart.class.php
create mode 100644 lib/models/resources/SeparableRoomPart.php
delete mode 100644 lib/modules/Blubber.class.php
create mode 100644 lib/modules/Blubber.php
delete mode 100644 lib/modules/ConsultationModule.class.php
create mode 100644 lib/modules/ConsultationModule.php
delete mode 100644 lib/modules/CoreAdmin.class.php
create mode 100644 lib/modules/CoreAdmin.php
delete mode 100644 lib/modules/CoreCalendar.class.php
create mode 100644 lib/modules/CoreCalendar.php
delete mode 100644 lib/modules/CoreDocuments.class.php
create mode 100644 lib/modules/CoreDocuments.php
delete mode 100644 lib/modules/CoreElearningInterface.class.php
create mode 100644 lib/modules/CoreElearningInterface.php
delete mode 100644 lib/modules/CoreForum.class.php
create mode 100644 lib/modules/CoreForum.php
delete mode 100644 lib/modules/CoreOverview.class.php
create mode 100644 lib/modules/CoreOverview.php
delete mode 100644 lib/modules/CoreParticipants.class.php
create mode 100644 lib/modules/CoreParticipants.php
delete mode 100644 lib/modules/CorePersonal.class.php
create mode 100644 lib/modules/CorePersonal.php
delete mode 100644 lib/modules/CoreSchedule.class.php
create mode 100644 lib/modules/CoreSchedule.php
delete mode 100644 lib/modules/CoreScm.class.php
create mode 100644 lib/modules/CoreScm.php
delete mode 100644 lib/modules/CoreStudygroupAdmin.class.php
create mode 100644 lib/modules/CoreStudygroupAdmin.php
delete mode 100644 lib/modules/CoreStudygroupParticipants.class.php
create mode 100644 lib/modules/CoreStudygroupParticipants.php
delete mode 100644 lib/modules/CoreWiki.class.php
create mode 100644 lib/modules/CoreWiki.php
delete mode 100644 lib/modules/CoursewareModule.class.php
create mode 100644 lib/modules/CoursewareModule.php
delete mode 100644 lib/modules/FeedbackModule.class.php
create mode 100644 lib/modules/FeedbackModule.php
delete mode 100644 lib/modules/GradebookModule.class.php
create mode 100644 lib/modules/GradebookModule.php
delete mode 100644 lib/modules/IliasInterfaceModule.class.php
create mode 100644 lib/modules/IliasInterfaceModule.php
delete mode 100644 lib/modules/LtiToolModule.class.php
create mode 100644 lib/modules/LtiToolModule.php
delete mode 100644 lib/modules/StudipModule.class.php
create mode 100644 lib/modules/StudipModule.php
delete mode 100644 lib/phplib/CT_Cache.class.php
create mode 100644 lib/phplib/CT_Cache.php
delete mode 100644 lib/phplib/CT_Sql.class.php
create mode 100644 lib/phplib/CT_Sql.php
delete mode 100644 lib/phplib/DB_Sql.class.php
create mode 100644 lib/phplib/DB_Sql.php
delete mode 100644 lib/phplib/Seminar_Auth.class.php
create mode 100644 lib/phplib/Seminar_Auth.php
delete mode 100644 lib/phplib/Seminar_Default_Auth.class.php
create mode 100644 lib/phplib/Seminar_Default_Auth.php
delete mode 100644 lib/phplib/Seminar_Perm.class.php
create mode 100644 lib/phplib/Seminar_Perm.php
delete mode 100644 lib/phplib/Seminar_Register_Auth.class.php
create mode 100644 lib/phplib/Seminar_Register_Auth.php
delete mode 100644 lib/phplib/Seminar_Session.class.php
create mode 100644 lib/phplib/Seminar_Session.php
delete mode 100644 lib/phplib/Seminar_User.class.php
create mode 100644 lib/phplib/Seminar_User.php
delete mode 100644 lib/phplib/email_validation.class.php
create mode 100644 lib/phplib/email_validation.php
delete mode 100644 lib/plugins/core/AdminCourseAction.class.php
create mode 100644 lib/plugins/core/AdminCourseAction.php
delete mode 100644 lib/plugins/core/AdminCourseContents.class.php
create mode 100644 lib/plugins/core/AdminCourseContents.php
delete mode 100644 lib/plugins/core/AdminCourseWidgetPlugin.class.php
create mode 100644 lib/plugins/core/AdminCourseWidgetPlugin.php
delete mode 100644 lib/plugins/core/AdministrationPlugin.class.php
create mode 100644 lib/plugins/core/AdministrationPlugin.php
delete mode 100644 lib/plugins/core/DetailspagePlugin.class.php
create mode 100644 lib/plugins/core/DetailspagePlugin.php
delete mode 100644 lib/plugins/core/FileUploadHook.class.php
create mode 100644 lib/plugins/core/FileUploadHook.php
delete mode 100644 lib/plugins/core/FilesystemPlugin.class.php
create mode 100644 lib/plugins/core/FilesystemPlugin.php
delete mode 100644 lib/plugins/core/ForumModule.class.php
create mode 100644 lib/plugins/core/ForumModule.php
delete mode 100644 lib/plugins/core/HomepagePlugin.class.php
create mode 100644 lib/plugins/core/HomepagePlugin.php
delete mode 100644 lib/plugins/core/LibraryPlugin.class.php
create mode 100644 lib/plugins/core/LibraryPlugin.php
delete mode 100644 lib/plugins/core/MetricsPlugin.class.php
create mode 100644 lib/plugins/core/MetricsPlugin.php
delete mode 100644 lib/plugins/core/PortalPlugin.class.php
create mode 100644 lib/plugins/core/PortalPlugin.php
delete mode 100644 lib/plugins/core/PrivacyPlugin.class.php
create mode 100644 lib/plugins/core/PrivacyPlugin.php
delete mode 100644 lib/plugins/core/QuestionnaireAssignmentPlugin.class.php
create mode 100644 lib/plugins/core/QuestionnaireAssignmentPlugin.php
delete mode 100644 lib/plugins/core/RESTAPIPlugin.class.php
create mode 100644 lib/plugins/core/RESTAPIPlugin.php
delete mode 100644 lib/plugins/core/Role.class.php
create mode 100644 lib/plugins/core/Role.php
delete mode 100644 lib/plugins/core/ScorePlugin.class.php
create mode 100644 lib/plugins/core/ScorePlugin.php
delete mode 100644 lib/plugins/core/StandardPlugin.class.php
create mode 100644 lib/plugins/core/StandardPlugin.php
delete mode 100644 lib/plugins/core/StudIPPlugin.class.php
create mode 100644 lib/plugins/core/StudIPPlugin.php
delete mode 100644 lib/plugins/core/SystemPlugin.class.php
create mode 100644 lib/plugins/core/SystemPlugin.php
delete mode 100644 lib/plugins/core/WebServicePlugin.class.php
create mode 100644 lib/plugins/core/WebServicePlugin.php
delete mode 100644 lib/plugins/db/RolePersistence.class.php
create mode 100644 lib/plugins/db/RolePersistence.php
delete mode 100644 lib/plugins/engine/PluginEngine.class.php
create mode 100644 lib/plugins/engine/PluginEngine.php
delete mode 100644 lib/plugins/engine/PluginManager.class.php
create mode 100644 lib/plugins/engine/PluginManager.php
delete mode 100644 lib/plugins/engine/PluginRepository.class.php
create mode 100644 lib/plugins/engine/PluginRepository.php
delete mode 100644 lib/raumzeit/CycleData.class.php
create mode 100644 lib/raumzeit/CycleData.php
delete mode 100644 lib/raumzeit/CycleDataDB.class.php
create mode 100644 lib/raumzeit/CycleDataDB.php
delete mode 100644 lib/raumzeit/Issue.class.php
create mode 100644 lib/raumzeit/Issue.php
delete mode 100644 lib/raumzeit/IssueDB.class.php
create mode 100644 lib/raumzeit/IssueDB.php
delete mode 100644 lib/raumzeit/MetaDate.class.php
create mode 100644 lib/raumzeit/MetaDate.php
delete mode 100644 lib/raumzeit/MetaDateDB.class.php
create mode 100644 lib/raumzeit/MetaDateDB.php
delete mode 100644 lib/raumzeit/SeminarDB.class.php
create mode 100644 lib/raumzeit/SeminarDB.php
delete mode 100644 lib/raumzeit/SingleDate.class.php
create mode 100644 lib/raumzeit/SingleDate.php
delete mode 100644 lib/raumzeit/SingleDateDB.class.php
create mode 100644 lib/raumzeit/SingleDateDB.php
delete mode 100644 lib/resources/ResourceManager.class.php
create mode 100644 lib/resources/ResourceManager.php
delete mode 100644 lib/resources/RoomManager.class.php
create mode 100644 lib/resources/RoomManager.php
delete mode 100644 lib/soap/StudipSoapClient.class.php
create mode 100644 lib/soap/StudipSoapClient.php
delete mode 100644 lib/soap/StudipSoapClient_PHP5.class.php
create mode 100644 lib/soap/StudipSoapClient_PHP5.php
delete mode 100644 vendor/oauth-php/library/session/OAuthSessionAbstract.class.php
create mode 100644 vendor/oauth-php/library/session/OAuthSessionAbstract.php
delete mode 100644 vendor/oauth-php/library/signature_method/OAuthSignatureMethod.class.php
create mode 100644 vendor/oauth-php/library/signature_method/OAuthSignatureMethod.php
delete mode 100644 vendor/oauth-php/library/store/OAuthStoreAbstract.class.php
create mode 100644 vendor/oauth-php/library/store/OAuthStoreAbstract.php
diff --git a/app/controllers/questionnaire.php b/app/controllers/questionnaire.php
index 4016b9d..94e4d04 100644
--- a/app/controllers/questionnaire.php
+++ b/app/controllers/questionnaire.php
@@ -1,7 +1,5 @@
= 7.1",
- "psr/http-message": "^1.0",
- "symfony/polyfill-mbstring": "^1.0"
+ "ext-mbstring": "*",
+ "ext-zlib": "*",
+ "php-64bit": "^8.1"
},
"require-dev": {
"ext-zip": "*",
- "guzzlehttp/guzzle": ">= 6.3",
+ "friendsofphp/php-cs-fixer": "^3.16",
+ "guzzlehttp/guzzle": "^7.5",
"mikey179/vfsstream": "^1.6",
- "phpunit/phpunit": ">= 7.5"
+ "php-coveralls/php-coveralls": "^2.5",
+ "phpunit/phpunit": "^10.0",
+ "vimeo/psalm": "^5.0"
+ },
+ "suggest": {
+ "guzzlehttp/psr7": "^2.4",
+ "psr/http-message": "^2.0"
},
"type": "library",
"autoload": {
@@ -1514,7 +1520,7 @@
],
"support": {
"issues": "https://github.com/maennchen/ZipStream-PHP/issues",
- "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.1.0"
+ "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.0"
},
"funding": [
{
@@ -1526,7 +1532,7 @@
"type": "open_collective"
}
],
- "time": "2020-05-30T13:11:16+00:00"
+ "time": "2023-06-21T14:59:35+00:00"
},
{
"name": "markbaker/complex",
@@ -1637,16 +1643,16 @@
},
{
"name": "monolog/monolog",
- "version": "2.9.1",
+ "version": "2.9.3",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1"
+ "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f259e2b15fb95494c83f52d3caad003bbf5ffaa1",
- "reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/a30bfe2e142720dfa990d0a7e573997f5d884215",
+ "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215",
"shasum": ""
},
"require": {
@@ -1667,8 +1673,8 @@
"mongodb/mongodb": "^1.8",
"php-amqplib/php-amqplib": "~2.4 || ^3",
"phpspec/prophecy": "^1.15",
- "phpstan/phpstan": "^0.12.91",
- "phpunit/phpunit": "^8.5.14",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^8.5.38 || ^9.6.19",
"predis/predis": "^1.1 || ^2.0",
"rollbar/rollbar": "^1.3 || ^2 || ^3",
"ruflin/elastica": "^7",
@@ -1723,7 +1729,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
- "source": "https://github.com/Seldaek/monolog/tree/2.9.1"
+ "source": "https://github.com/Seldaek/monolog/tree/2.9.3"
},
"funding": [
{
@@ -1735,94 +1741,33 @@
"type": "tidelift"
}
],
- "time": "2023-02-06T13:44:46+00:00"
- },
- {
- "name": "myclabs/php-enum",
- "version": "1.7.7",
- "source": {
- "type": "git",
- "url": "https://github.com/myclabs/php-enum.git",
- "reference": "d178027d1e679832db9f38248fcc7200647dc2b7"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/myclabs/php-enum/zipball/d178027d1e679832db9f38248fcc7200647dc2b7",
- "reference": "d178027d1e679832db9f38248fcc7200647dc2b7",
- "shasum": ""
- },
- "require": {
- "ext-json": "*",
- "php": ">=7.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^7",
- "squizlabs/php_codesniffer": "1.*",
- "vimeo/psalm": "^3.8"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "MyCLabs\\Enum\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "PHP Enum contributors",
- "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
- }
- ],
- "description": "PHP Enum implementation",
- "homepage": "http://github.com/myclabs/php-enum",
- "keywords": [
- "enum"
- ],
- "support": {
- "issues": "https://github.com/myclabs/php-enum/issues",
- "source": "https://github.com/myclabs/php-enum/tree/1.7.7"
- },
- "funding": [
- {
- "url": "https://github.com/mnapoli",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
- "type": "tidelift"
- }
- ],
- "time": "2020-11-14T18:14:52+00:00"
+ "time": "2024-04-12T20:52:51+00:00"
},
{
"name": "neomerx/cors-psr7",
- "version": "v2.0.2",
+ "version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/neomerx/cors-psr7.git",
- "reference": "454e923aaf9ba4aa162f7aca2a514e41708b6ba5"
+ "reference": "515d7fdb60b9d475da70029d4e5662beaae1875f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/neomerx/cors-psr7/zipball/454e923aaf9ba4aa162f7aca2a514e41708b6ba5",
- "reference": "454e923aaf9ba4aa162f7aca2a514e41708b6ba5",
+ "url": "https://api.github.com/repos/neomerx/cors-psr7/zipball/515d7fdb60b9d475da70029d4e5662beaae1875f",
+ "reference": "515d7fdb60b9d475da70029d4e5662beaae1875f",
"shasum": ""
},
"require": {
- "php": ">=7.1.0",
+ "php": ">=8.0.0",
"psr/http-message": "^1.0",
- "psr/log": "^1.0"
+ "psr/log": "^3.0"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^2.14",
+ "friendsofphp/php-cs-fixer": "^3.11",
"mockery/mockery": "^1.0",
- "phpmd/phpmd": "^2.6",
- "phpunit/phpunit": "^7.0",
+ "phpunit/phpunit": "^9.2",
"scrutinizer/ocular": "^1.4",
- "squizlabs/php_codesniffer": "^2.9"
+ "squizlabs/php_codesniffer": "^3.0"
},
"type": "library",
"autoload": {
@@ -1854,9 +1799,9 @@
],
"support": {
"issues": "https://github.com/neomerx/cors-psr7/issues",
- "source": "https://github.com/neomerx/cors-psr7/tree/develop"
+ "source": "https://github.com/neomerx/cors-psr7/tree/3.0.2"
},
- "time": "2019-02-14T10:35:50+00:00"
+ "time": "2022-11-28T03:29:06+00:00"
},
{
"name": "nikic/fast-route",
@@ -2218,16 +2163,16 @@
},
{
"name": "paragonie/constant_time_encoding",
- "version": "v2.6.3",
+ "version": "v2.7.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
- "reference": "58c3f47f650c94ec05a151692652a868995d2938"
+ "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938",
- "reference": "58c3f47f650c94ec05a151692652a868995d2938",
+ "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105",
+ "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105",
"shasum": ""
},
"require": {
@@ -2281,7 +2226,7 @@
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
- "time": "2022-06-14T06:56:20+00:00"
+ "time": "2024-05-08T12:18:48+00:00"
},
{
"name": "paragonie/random_compat",
@@ -2438,24 +2383,26 @@
},
{
"name": "php-di/invoker",
- "version": "2.0.0",
+ "version": "2.3.4",
"source": {
"type": "git",
"url": "https://github.com/PHP-DI/Invoker.git",
- "reference": "540c27c86f663e20fe39a24cd72fa76cdb21d41a"
+ "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/540c27c86f663e20fe39a24cd72fa76cdb21d41a",
- "reference": "540c27c86f663e20fe39a24cd72fa76cdb21d41a",
+ "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/33234b32dafa8eb69202f950a1fc92055ed76a86",
+ "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86",
"shasum": ""
},
"require": {
- "psr/container": "~1.0"
+ "php": ">=7.3",
+ "psr/container": "^1.0|^2.0"
},
"require-dev": {
"athletic/athletic": "~0.1.8",
- "phpunit/phpunit": "~4.5"
+ "mnapoli/hard-mode": "~0.3.0",
+ "phpunit/phpunit": "^9.0"
},
"type": "library",
"autoload": {
@@ -2479,9 +2426,15 @@
],
"support": {
"issues": "https://github.com/PHP-DI/Invoker/issues",
- "source": "https://github.com/PHP-DI/Invoker/tree/master"
+ "source": "https://github.com/PHP-DI/Invoker/tree/2.3.4"
},
- "time": "2017-03-20T19:28:22+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ }
+ ],
+ "time": "2023-09-08T09:24:21+00:00"
},
{
"name": "php-di/php-di",
@@ -2899,21 +2852,21 @@
},
{
"name": "phpxmlrpc/extras",
- "version": "1.0.0-beta2",
+ "version": "1.0.0-beta4",
"source": {
"type": "git",
"url": "https://github.com/gggeek/phpxmlrpc-extras.git",
- "reference": "4bf48852955f7b4698b37ae91cce83c49f658b4c"
+ "reference": "7aedeb5100b0bca4085692a2c626fafb726c73b6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/gggeek/phpxmlrpc-extras/zipball/4bf48852955f7b4698b37ae91cce83c49f658b4c",
- "reference": "4bf48852955f7b4698b37ae91cce83c49f658b4c",
+ "url": "https://api.github.com/repos/gggeek/phpxmlrpc-extras/zipball/7aedeb5100b0bca4085692a2c626fafb726c73b6",
+ "reference": "7aedeb5100b0bca4085692a2c626fafb726c73b6",
"shasum": ""
},
"require": {
- "php": "^5.3.0 || ^7.0 || ^8.0",
- "phpxmlrpc/phpxmlrpc": "^4.6.0"
+ "php": "^5.4.0 || ^7.0 || ^8.0",
+ "phpxmlrpc/phpxmlrpc": "^4.10.1"
},
"require-dev": {
"ext-curl": "*",
@@ -2945,22 +2898,22 @@
],
"support": {
"issues": "https://github.com/gggeek/phpxmlrpc-extras/issues",
- "source": "https://github.com/gggeek/phpxmlrpc-extras/tree/1.0.0-beta2"
+ "source": "https://github.com/gggeek/phpxmlrpc-extras/tree/1.0.0-beta4"
},
- "time": "2023-01-19T14:20:38+00:00"
+ "time": "2024-04-16T15:48:39+00:00"
},
{
"name": "phpxmlrpc/phpxmlrpc",
- "version": "4.10.0",
+ "version": "4.10.3",
"source": {
"type": "git",
"url": "https://github.com/gggeek/phpxmlrpc.git",
- "reference": "7103864975ca0b930574cabc8b4593093ee4432e"
+ "reference": "6725f215cd7d7c7d36c1d773aa89902aef2da67c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/gggeek/phpxmlrpc/zipball/7103864975ca0b930574cabc8b4593093ee4432e",
- "reference": "7103864975ca0b930574cabc8b4593093ee4432e",
+ "url": "https://api.github.com/repos/gggeek/phpxmlrpc/zipball/6725f215cd7d7c7d36c1d773aa89902aef2da67c",
+ "reference": "6725f215cd7d7c7d36c1d773aa89902aef2da67c",
"shasum": ""
},
"require": {
@@ -2968,7 +2921,8 @@
"php": "^5.4.0 || ^7.0 || ^8.0"
},
"conflict": {
- "phpxmlrpc/extras": "<= 0.6.3"
+ "phpxmlrpc/extras": "<= 1.0.0-beta2",
+ "phpxmlrpc/jsonrpc": "<= 1.0.0-beta1"
},
"require-dev": {
"ext-curl": "*",
@@ -3004,9 +2958,9 @@
],
"support": {
"issues": "https://github.com/gggeek/phpxmlrpc/issues",
- "source": "https://github.com/gggeek/phpxmlrpc/tree/4.10.0"
+ "source": "https://github.com/gggeek/phpxmlrpc/tree/4.10.3"
},
- "time": "2023-02-11T17:10:30+00:00"
+ "time": "2024-04-23T10:50:06+00:00"
},
{
"name": "psr/cache",
@@ -3107,22 +3061,27 @@
},
{
"name": "psr/container",
- "version": "1.1.2",
+ "version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
- "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
- "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"shasum": ""
},
"require": {
"php": ">=7.4.0"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
@@ -3149,27 +3108,27 @@
],
"support": {
"issues": "https://github.com/php-fig/container/issues",
- "source": "https://github.com/php-fig/container/tree/1.1.2"
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
},
- "time": "2021-11-05T16:50:12+00:00"
+ "time": "2021-11-05T16:47:00+00:00"
},
{
"name": "psr/http-client",
- "version": "1.0.1",
+ "version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
- "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
- "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
- "psr/http-message": "^1.0"
+ "psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
@@ -3189,7 +3148,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
@@ -3201,26 +3160,26 @@
"psr-18"
],
"support": {
- "source": "https://github.com/php-fig/http-client/tree/master"
+ "source": "https://github.com/php-fig/http-client"
},
- "time": "2020-06-29T06:28:15+00:00"
+ "time": "2023-09-23T14:17:50+00:00"
},
{
"name": "psr/http-factory",
- "version": "1.0.2",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
- "reference": "e616d01114759c4c489f93b099585439f795fe35"
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
- "reference": "e616d01114759c4c489f93b099585439f795fe35",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": ""
},
"require": {
- "php": ">=7.0.0",
+ "php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
@@ -3244,7 +3203,7 @@
"homepage": "https://www.php-fig.org/"
}
],
- "description": "Common interfaces for PSR-7 HTTP message factories",
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
@@ -3256,9 +3215,9 @@
"response"
],
"support": {
- "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+ "source": "https://github.com/php-fig/http-factory"
},
- "time": "2023-04-10T20:10:41+00:00"
+ "time": "2024-04-15T12:06:14+00:00"
},
{
"name": "psr/http-message",
@@ -3315,21 +3274,21 @@
},
{
"name": "psr/http-server-handler",
- "version": "1.0.1",
+ "version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-server-handler.git",
- "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7"
+ "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/aff2f80e33b7f026ec96bb42f63242dc50ffcae7",
- "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7",
+ "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4",
+ "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4",
"shasum": ""
},
"require": {
"php": ">=7.0",
- "psr/http-message": "^1.0"
+ "psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
@@ -3349,7 +3308,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP server-side request handler",
@@ -3365,28 +3324,27 @@
"server"
],
"support": {
- "issues": "https://github.com/php-fig/http-server-handler/issues",
- "source": "https://github.com/php-fig/http-server-handler/tree/master"
+ "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2"
},
- "time": "2018-10-30T16:46:14+00:00"
+ "time": "2023-04-10T20:06:20+00:00"
},
{
"name": "psr/http-server-middleware",
- "version": "1.0.1",
+ "version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-server-middleware.git",
- "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5"
+ "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/2296f45510945530b9dceb8bcedb5cb84d40c5f5",
- "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5",
+ "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829",
+ "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829",
"shasum": ""
},
"require": {
"php": ">=7.0",
- "psr/http-message": "^1.0",
+ "psr/http-message": "^1.0 || ^2.0",
"psr/http-server-handler": "^1.0"
},
"type": "library",
@@ -3407,7 +3365,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP server-side middleware",
@@ -3423,36 +3381,36 @@
],
"support": {
"issues": "https://github.com/php-fig/http-server-middleware/issues",
- "source": "https://github.com/php-fig/http-server-middleware/tree/master"
+ "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2"
},
- "time": "2018-10-30T17:12:04+00:00"
+ "time": "2023-04-11T06:14:47+00:00"
},
{
"name": "psr/log",
- "version": "1.1.4",
+ "version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
- "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
- "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
+ "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.1.x-dev"
+ "dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
- "Psr\\Log\\": "Psr/Log/"
+ "Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -3473,31 +3431,31 @@
"psr-3"
],
"support": {
- "source": "https://github.com/php-fig/log/tree/1.1.4"
+ "source": "https://github.com/php-fig/log/tree/3.0.0"
},
- "time": "2021-05-03T11:20:27+00:00"
+ "time": "2021-07-14T16:46:02+00:00"
},
{
"name": "psr/simple-cache",
- "version": "1.0.1",
+ "version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
- "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
- "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "3.0.x-dev"
}
},
"autoload": {
@@ -3512,7 +3470,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common interfaces for simple caching",
@@ -3524,9 +3482,9 @@
"simple-cache"
],
"support": {
- "source": "https://github.com/php-fig/simple-cache/tree/master"
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
},
- "time": "2017-10-23T01:57:42+00:00"
+ "time": "2021-10-29T13:26:27+00:00"
},
{
"name": "psy/psysh",
@@ -4706,16 +4664,16 @@
},
{
"name": "symfony/string",
- "version": "v6.4.7",
+ "version": "v6.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69"
+ "reference": "a147c0f826c4a1f3afb763ab8e009e37c877a44d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/ffeb9591c61f65a68d47f77d12b83fa530227a69",
- "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69",
+ "url": "https://api.github.com/repos/symfony/string/zipball/a147c0f826c4a1f3afb763ab8e009e37c877a44d",
+ "reference": "a147c0f826c4a1f3afb763ab8e009e37c877a44d",
"shasum": ""
},
"require": {
@@ -4772,7 +4730,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v6.4.7"
+ "source": "https://github.com/symfony/string/tree/v6.4.8"
},
"funding": [
{
@@ -4788,7 +4746,7 @@
"type": "tidelift"
}
],
- "time": "2024-04-18T09:22:46+00:00"
+ "time": "2024-05-31T14:49:08+00:00"
},
{
"name": "symfony/var-dumper",
@@ -5651,6 +5609,52 @@
"time": "2022-02-16T19:48:08+00:00"
},
{
+ "name": "codeception/specify",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Codeception/Specify.git",
+ "reference": "b718b4b9bb722be4756aa7c9aa7e89b6babc2cf6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Codeception/Specify/zipball/b718b4b9bb722be4756aa7c9aa7e89b6babc2cf6",
+ "reference": "b718b4b9bb722be4756aa7c9aa7e89b6babc2cf6",
+ "shasum": ""
+ },
+ "require": {
+ "myclabs/deep-copy": "^1.10",
+ "php": ">=7.4.0",
+ "phpunit/phpunit": "^8.0|^9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Codeception\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Bodnarchuk",
+ "email": "davert@codeception.com"
+ },
+ {
+ "name": "Gustavo Nieves",
+ "homepage": "https://medium.com/@ganieves"
+ }
+ ],
+ "description": "BDD code blocks for PHPUnit and Codeception",
+ "support": {
+ "issues": "https://github.com/Codeception/Specify/issues",
+ "source": "https://github.com/Codeception/Specify/tree/2.0.0"
+ },
+ "time": "2021-11-22T00:16:11+00:00"
+ },
+ {
"name": "codeception/stub",
"version": "4.1.3",
"source": {
@@ -5692,6 +5696,76 @@
"time": "2024-02-02T19:21:00+00:00"
},
{
+ "name": "doctrine/instantiator",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
+ "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^11",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/phpstan": "^1.9.4",
+ "phpstan/phpstan-phpunit": "^1.3",
+ "phpunit/phpunit": "^9.5.27",
+ "vimeo/psalm": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-12-30T00:23:10+00:00"
+ },
+ {
"name": "maximebf/debugbar",
"version": "v1.22.3",
"source": {
@@ -5761,16 +5835,16 @@
},
{
"name": "myclabs/deep-copy",
- "version": "1.11.1",
+ "version": "1.12.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
+ "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
- "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
+ "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"shasum": ""
},
"require": {
@@ -5778,11 +5852,12 @@
},
"conflict": {
"doctrine/collections": "<1.6.8",
- "doctrine/common": "<2.13.3 || >=3,<3.2.2"
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
},
"require-dev": {
"doctrine/collections": "^1.6.8",
"doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
},
"type": "library",
@@ -5808,7 +5883,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
},
"funding": [
{
@@ -5816,7 +5891,7 @@
"type": "tidelift"
}
],
- "time": "2023-03-08T13:26:56+00:00"
+ "time": "2024-06-12T14:39:25+00:00"
},
{
"name": "overtrue/phplint",
@@ -6034,34 +6109,29 @@
},
{
"name": "php-http/httplug",
- "version": "2.3.0",
+ "version": "2.4.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/httplug.git",
- "reference": "f640739f80dfa1152533976e3c112477f69274eb"
+ "reference": "625ad742c360c8ac580fcc647a1541d29e257f67"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-http/httplug/zipball/f640739f80dfa1152533976e3c112477f69274eb",
- "reference": "f640739f80dfa1152533976e3c112477f69274eb",
+ "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67",
+ "reference": "625ad742c360c8ac580fcc647a1541d29e257f67",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
"php-http/promise": "^1.1",
"psr/http-client": "^1.0",
- "psr/http-message": "^1.0"
+ "psr/http-message": "^1.0 || ^2.0"
},
"require-dev": {
- "friends-of-phpspec/phpspec-code-coverage": "^4.1",
- "phpspec/phpspec": "^5.1 || ^6.0"
+ "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0",
+ "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.x-dev"
- }
- },
"autoload": {
"psr-4": {
"Http\\Client\\": "src/"
@@ -6090,37 +6160,32 @@
],
"support": {
"issues": "https://github.com/php-http/httplug/issues",
- "source": "https://github.com/php-http/httplug/tree/2.3.0"
+ "source": "https://github.com/php-http/httplug/tree/2.4.0"
},
- "time": "2022-02-21T09:52:22+00:00"
+ "time": "2023-04-14T15:10:03+00:00"
},
{
"name": "php-http/promise",
- "version": "1.1.0",
+ "version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/php-http/promise.git",
- "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88"
+ "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
- "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
+ "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83",
+ "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
- "friends-of-phpspec/phpspec-code-coverage": "^4.3.2",
- "phpspec/phpspec": "^5.1.2 || ^6.2"
+ "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3",
+ "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.1-dev"
- }
- },
"autoload": {
"psr-4": {
"Http\\Promise\\": "src/"
@@ -6147,9 +6212,9 @@
],
"support": {
"issues": "https://github.com/php-http/promise/issues",
- "source": "https://github.com/php-http/promise/tree/1.1.0"
+ "source": "https://github.com/php-http/promise/tree/1.3.1"
},
- "time": "2020-07-07T09:29:14+00:00"
+ "time": "2024-03-15T13:55:21+00:00"
},
{
"name": "phpstan/phpstan",
@@ -6211,16 +6276,16 @@
},
{
"name": "phpunit/php-code-coverage",
- "version": "10.1.14",
+ "version": "9.2.31",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b"
+ "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
- "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965",
+ "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965",
"shasum": ""
},
"require": {
@@ -6228,18 +6293,18 @@
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.18 || ^5.0",
- "php": ">=8.1",
- "phpunit/php-file-iterator": "^4.0",
- "phpunit/php-text-template": "^3.0",
- "sebastian/code-unit-reverse-lookup": "^3.0",
- "sebastian/complexity": "^3.0",
- "sebastian/environment": "^6.0",
- "sebastian/lines-of-code": "^2.0",
- "sebastian/version": "^4.0",
+ "php": ">=7.3",
+ "phpunit/php-file-iterator": "^3.0.3",
+ "phpunit/php-text-template": "^2.0.2",
+ "sebastian/code-unit-reverse-lookup": "^2.0.2",
+ "sebastian/complexity": "^2.0",
+ "sebastian/environment": "^5.1.2",
+ "sebastian/lines-of-code": "^1.0.3",
+ "sebastian/version": "^3.0.1",
"theseer/tokenizer": "^1.2.0"
},
"require-dev": {
- "phpunit/phpunit": "^10.1"
+ "phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-pcov": "PHP extension that provides line coverage",
@@ -6248,7 +6313,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "10.1-dev"
+ "dev-master": "9.2-dev"
}
},
"autoload": {
@@ -6277,7 +6342,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14"
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31"
},
"funding": [
{
@@ -6285,32 +6350,32 @@
"type": "github"
}
],
- "time": "2024-03-12T15:33:41+00:00"
+ "time": "2024-03-02T06:37:42+00:00"
},
{
"name": "phpunit/php-file-iterator",
- "version": "4.1.0",
+ "version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
- "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "4.0-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
@@ -6337,8 +6402,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
- "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
- "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
},
"funding": [
{
@@ -6346,28 +6410,28 @@
"type": "github"
}
],
- "time": "2023-08-31T06:24:48+00:00"
+ "time": "2021-12-02T12:48:52+00:00"
},
{
"name": "phpunit/php-invoker",
- "version": "4.0.0",
+ "version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-invoker.git",
- "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7"
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
- "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
"ext-pcntl": "*",
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-pcntl": "*"
@@ -6375,7 +6439,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "4.0-dev"
+ "dev-master": "3.1-dev"
}
},
"autoload": {
@@ -6401,7 +6465,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-invoker/issues",
- "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0"
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
},
"funding": [
{
@@ -6409,32 +6473,32 @@
"type": "github"
}
],
- "time": "2023-02-03T06:56:09+00:00"
+ "time": "2020-09-28T05:58:55+00:00"
},
{
"name": "phpunit/php-text-template",
- "version": "3.0.1",
+ "version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
- "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
- "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.0-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -6460,8 +6524,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-text-template/issues",
- "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
- "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
},
"funding": [
{
@@ -6469,32 +6532,32 @@
"type": "github"
}
],
- "time": "2023-08-31T14:07:24+00:00"
+ "time": "2020-10-26T05:33:50+00:00"
},
{
"name": "phpunit/php-timer",
- "version": "6.0.0",
+ "version": "5.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d"
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d",
- "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "6.0-dev"
+ "dev-master": "5.0-dev"
}
},
"autoload": {
@@ -6520,7 +6583,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-timer/issues",
- "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0"
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
},
"funding": [
{
@@ -6528,23 +6591,24 @@
"type": "github"
}
],
- "time": "2023-02-03T06:57:52+00:00"
+ "time": "2020-10-26T13:16:10+00:00"
},
{
"name": "phpunit/phpunit",
- "version": "10.5.20",
+ "version": "9.6.19",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3"
+ "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3",
- "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8",
+ "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8",
"shasum": ""
},
"require": {
+ "doctrine/instantiator": "^1.3.1 || ^2",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
@@ -6554,26 +6618,27 @@
"myclabs/deep-copy": "^1.10.1",
"phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
- "php": ">=8.1",
- "phpunit/php-code-coverage": "^10.1.5",
- "phpunit/php-file-iterator": "^4.0",
- "phpunit/php-invoker": "^4.0",
- "phpunit/php-text-template": "^3.0",
- "phpunit/php-timer": "^6.0",
- "sebastian/cli-parser": "^2.0",
- "sebastian/code-unit": "^2.0",
- "sebastian/comparator": "^5.0",
- "sebastian/diff": "^5.0",
- "sebastian/environment": "^6.0",
- "sebastian/exporter": "^5.1",
- "sebastian/global-state": "^6.0.1",
- "sebastian/object-enumerator": "^5.0",
- "sebastian/recursion-context": "^5.0",
- "sebastian/type": "^4.0",
- "sebastian/version": "^4.0"
+ "php": ">=7.3",
+ "phpunit/php-code-coverage": "^9.2.28",
+ "phpunit/php-file-iterator": "^3.0.5",
+ "phpunit/php-invoker": "^3.1.1",
+ "phpunit/php-text-template": "^2.0.3",
+ "phpunit/php-timer": "^5.0.2",
+ "sebastian/cli-parser": "^1.0.1",
+ "sebastian/code-unit": "^1.0.6",
+ "sebastian/comparator": "^4.0.8",
+ "sebastian/diff": "^4.0.3",
+ "sebastian/environment": "^5.1.3",
+ "sebastian/exporter": "^4.0.5",
+ "sebastian/global-state": "^5.0.1",
+ "sebastian/object-enumerator": "^4.0.3",
+ "sebastian/resource-operations": "^3.0.3",
+ "sebastian/type": "^3.2",
+ "sebastian/version": "^3.0.2"
},
"suggest": {
- "ext-soap": "To be able to generate mocks based on WSDL files"
+ "ext-soap": "To be able to generate mocks based on WSDL files",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
},
"bin": [
"phpunit"
@@ -6581,7 +6646,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "10.5-dev"
+ "dev-master": "9.6-dev"
}
},
"autoload": {
@@ -6613,7 +6678,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.20"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19"
},
"funding": [
{
@@ -6629,7 +6694,7 @@
"type": "tidelift"
}
],
- "time": "2024-04-24T06:32:35+00:00"
+ "time": "2024-04-05T04:35:58+00:00"
},
{
"name": "psr/event-dispatcher",
@@ -6683,28 +6748,28 @@
},
{
"name": "sebastian/cli-parser",
- "version": "2.0.1",
+ "version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git",
- "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
+ "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
- "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
+ "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.0-dev"
+ "dev-master": "1.0-dev"
}
},
"autoload": {
@@ -6727,8 +6792,7 @@
"homepage": "https://github.com/sebastianbergmann/cli-parser",
"support": {
"issues": "https://github.com/sebastianbergmann/cli-parser/issues",
- "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
- "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2"
},
"funding": [
{
@@ -6736,32 +6800,32 @@
"type": "github"
}
],
- "time": "2024-03-02T07:12:49+00:00"
+ "time": "2024-03-02T06:27:43+00:00"
},
{
"name": "sebastian/code-unit",
- "version": "2.0.0",
+ "version": "1.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit.git",
- "reference": "a81fee9eef0b7a76af11d121767abc44c104e503"
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503",
- "reference": "a81fee9eef0b7a76af11d121767abc44c104e503",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.0-dev"
+ "dev-master": "1.0-dev"
}
},
"autoload": {
@@ -6784,7 +6848,7 @@
"homepage": "https://github.com/sebastianbergmann/code-unit",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit/issues",
- "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0"
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
},
"funding": [
{
@@ -6792,32 +6856,32 @@
"type": "github"
}
],
- "time": "2023-02-03T06:58:43+00:00"
+ "time": "2020-10-26T13:08:54+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
- "version": "3.0.0",
+ "version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
- "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d"
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
- "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.0-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -6839,7 +6903,7 @@
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
- "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0"
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
},
"funding": [
{
@@ -6847,36 +6911,34 @@
"type": "github"
}
],
- "time": "2023-02-03T06:59:15+00:00"
+ "time": "2020-09-28T05:30:19+00:00"
},
{
"name": "sebastian/comparator",
- "version": "5.0.1",
+ "version": "4.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "2db5010a484d53ebf536087a70b4a5423c102372"
+ "reference": "fa0f136dd2334583309d32b62544682ee972b51a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372",
- "reference": "2db5010a484d53ebf536087a70b4a5423c102372",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a",
+ "reference": "fa0f136dd2334583309d32b62544682ee972b51a",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-mbstring": "*",
- "php": ">=8.1",
- "sebastian/diff": "^5.0",
- "sebastian/exporter": "^5.0"
+ "php": ">=7.3",
+ "sebastian/diff": "^4.0",
+ "sebastian/exporter": "^4.0"
},
"require-dev": {
- "phpunit/phpunit": "^10.3"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "5.0-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -6915,8 +6977,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
- "security": "https://github.com/sebastianbergmann/comparator/security/policy",
- "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1"
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8"
},
"funding": [
{
@@ -6924,33 +6985,33 @@
"type": "github"
}
],
- "time": "2023-08-14T13:18:12+00:00"
+ "time": "2022-09-14T12:41:17+00:00"
},
{
"name": "sebastian/complexity",
- "version": "3.2.0",
+ "version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git",
- "reference": "68ff824baeae169ec9f2137158ee529584553799"
+ "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
- "reference": "68ff824baeae169ec9f2137158ee529584553799",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a",
+ "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a",
"shasum": ""
},
"require": {
"nikic/php-parser": "^4.18 || ^5.0",
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.2-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -6973,8 +7034,7 @@
"homepage": "https://github.com/sebastianbergmann/complexity",
"support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues",
- "security": "https://github.com/sebastianbergmann/complexity/security/policy",
- "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
+ "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3"
},
"funding": [
{
@@ -6982,33 +7042,33 @@
"type": "github"
}
],
- "time": "2023-12-21T08:37:17+00:00"
+ "time": "2023-12-22T06:19:30+00:00"
},
{
"name": "sebastian/diff",
- "version": "5.1.1",
+ "version": "4.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
- "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0",
- "symfony/process": "^6.4"
+ "phpunit/phpunit": "^9.3",
+ "symfony/process": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "5.1-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -7040,8 +7100,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
- "security": "https://github.com/sebastianbergmann/diff/security/policy",
- "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6"
},
"funding": [
{
@@ -7049,27 +7108,27 @@
"type": "github"
}
],
- "time": "2024-03-02T07:15:17+00:00"
+ "time": "2024-03-02T06:30:58+00:00"
},
{
"name": "sebastian/environment",
- "version": "6.1.0",
+ "version": "5.1.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
+ "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
- "reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
+ "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-posix": "*"
@@ -7077,7 +7136,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "6.1-dev"
+ "dev-master": "5.1-dev"
}
},
"autoload": {
@@ -7096,7 +7155,7 @@
}
],
"description": "Provides functionality to handle HHVM/PHP environments",
- "homepage": "https://github.com/sebastianbergmann/environment",
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
"keywords": [
"Xdebug",
"environment",
@@ -7104,8 +7163,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
- "security": "https://github.com/sebastianbergmann/environment/security/policy",
- "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
+ "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5"
},
"funding": [
{
@@ -7113,34 +7171,34 @@
"type": "github"
}
],
- "time": "2024-03-23T08:47:14+00:00"
+ "time": "2023-02-03T06:03:51+00:00"
},
{
"name": "sebastian/exporter",
- "version": "5.1.2",
+ "version": "4.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "955288482d97c19a372d3f31006ab3f37da47adf"
+ "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
- "reference": "955288482d97c19a372d3f31006ab3f37da47adf",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72",
+ "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72",
"shasum": ""
},
"require": {
- "ext-mbstring": "*",
- "php": ">=8.1",
- "sebastian/recursion-context": "^5.0"
+ "php": ">=7.3",
+ "sebastian/recursion-context": "^4.0"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "ext-mbstring": "*",
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "5.1-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -7182,8 +7240,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
- "security": "https://github.com/sebastianbergmann/exporter/security/policy",
- "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6"
},
"funding": [
{
@@ -7191,35 +7248,38 @@
"type": "github"
}
],
- "time": "2024-03-02T07:17:12+00:00"
+ "time": "2024-03-02T06:33:00+00:00"
},
{
"name": "sebastian/global-state",
- "version": "6.0.2",
+ "version": "5.0.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
+ "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
- "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9",
+ "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9",
"shasum": ""
},
"require": {
- "php": ">=8.1",
- "sebastian/object-reflector": "^3.0",
- "sebastian/recursion-context": "^5.0"
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
},
"require-dev": {
"ext-dom": "*",
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-uopz": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "6.0-dev"
+ "dev-master": "5.0-dev"
}
},
"autoload": {
@@ -7238,14 +7298,13 @@
}
],
"description": "Snapshotting of global state",
- "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
- "security": "https://github.com/sebastianbergmann/global-state/security/policy",
- "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7"
},
"funding": [
{
@@ -7253,33 +7312,33 @@
"type": "github"
}
],
- "time": "2024-03-02T07:19:19+00:00"
+ "time": "2024-03-02T06:35:11+00:00"
},
{
"name": "sebastian/lines-of-code",
- "version": "2.0.2",
+ "version": "1.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
- "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
+ "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
- "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5",
+ "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5",
"shasum": ""
},
"require": {
"nikic/php-parser": "^4.18 || ^5.0",
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.0-dev"
+ "dev-master": "1.0-dev"
}
},
"autoload": {
@@ -7302,8 +7361,7 @@
"homepage": "https://github.com/sebastianbergmann/lines-of-code",
"support": {
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
- "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
- "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4"
},
"funding": [
{
@@ -7311,34 +7369,34 @@
"type": "github"
}
],
- "time": "2023-12-21T08:38:20+00:00"
+ "time": "2023-12-22T06:20:34+00:00"
},
{
"name": "sebastian/object-enumerator",
- "version": "5.0.0",
+ "version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
- "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906"
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906",
- "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
"shasum": ""
},
"require": {
- "php": ">=8.1",
- "sebastian/object-reflector": "^3.0",
- "sebastian/recursion-context": "^5.0"
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "5.0-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -7360,7 +7418,7 @@
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
- "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0"
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
},
"funding": [
{
@@ -7368,32 +7426,32 @@
"type": "github"
}
],
- "time": "2023-02-03T07:08:32+00:00"
+ "time": "2020-10-26T13:12:34+00:00"
},
{
"name": "sebastian/object-reflector",
- "version": "3.0.0",
+ "version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-reflector.git",
- "reference": "24ed13d98130f0e7122df55d06c5c4942a577957"
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957",
- "reference": "24ed13d98130f0e7122df55d06c5c4942a577957",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.0-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -7415,7 +7473,7 @@
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-reflector/issues",
- "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0"
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
},
"funding": [
{
@@ -7423,32 +7481,32 @@
"type": "github"
}
],
- "time": "2023-02-03T07:06:18+00:00"
+ "time": "2020-10-26T13:14:26+00:00"
},
{
"name": "sebastian/recursion-context",
- "version": "5.0.0",
+ "version": "4.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "05909fb5bc7df4c52992396d0116aed689f93712"
+ "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712",
- "reference": "05909fb5bc7df4c52992396d0116aed689f93712",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
+ "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "5.0-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -7478,7 +7536,7 @@
"homepage": "https://github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
- "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0"
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5"
},
"funding": [
{
@@ -7486,32 +7544,86 @@
"type": "github"
}
],
- "time": "2023-02-03T07:05:40+00:00"
+ "time": "2023-02-03T06:07:39+00:00"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "3.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+ "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides a list of PHP built-in functions that operate on resources",
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "support": {
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-14T16:00:52+00:00"
},
{
"name": "sebastian/type",
- "version": "4.0.0",
+ "version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
- "reference": "462699a16464c3944eefc02ebdd77882bd3925bf"
+ "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf",
- "reference": "462699a16464c3944eefc02ebdd77882bd3925bf",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
+ "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^9.5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "4.0-dev"
+ "dev-master": "3.2-dev"
}
},
"autoload": {
@@ -7534,7 +7646,7 @@
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
- "source": "https://github.com/sebastianbergmann/type/tree/4.0.0"
+ "source": "https://github.com/sebastianbergmann/type/tree/3.2.1"
},
"funding": [
{
@@ -7542,29 +7654,29 @@
"type": "github"
}
],
- "time": "2023-02-03T07:10:45+00:00"
+ "time": "2023-02-03T06:13:03+00:00"
},
{
"name": "sebastian/version",
- "version": "4.0.1",
+ "version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
- "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17"
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17",
- "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "4.0-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
@@ -7587,7 +7699,7 @@
"homepage": "https://github.com/sebastianbergmann/version",
"support": {
"issues": "https://github.com/sebastianbergmann/version/issues",
- "source": "https://github.com/sebastianbergmann/version/tree/4.0.1"
+ "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
},
"funding": [
{
@@ -7595,20 +7707,20 @@
"type": "github"
}
],
- "time": "2023-02-07T11:34:05+00:00"
+ "time": "2020-09-28T06:39:44+00:00"
},
{
"name": "symfony/cache",
- "version": "v6.4.7",
+ "version": "v6.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
- "reference": "b9e9b93c9817ec6c789c7943f5e54b57a041c16a"
+ "reference": "287142df5579ce223c485b3872df3efae8390984"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache/zipball/b9e9b93c9817ec6c789c7943f5e54b57a041c16a",
- "reference": "b9e9b93c9817ec6c789c7943f5e54b57a041c16a",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/287142df5579ce223c485b3872df3efae8390984",
+ "reference": "287142df5579ce223c485b3872df3efae8390984",
"shasum": ""
},
"require": {
@@ -7675,7 +7787,7 @@
"psr6"
],
"support": {
- "source": "https://github.com/symfony/cache/tree/v6.4.7"
+ "source": "https://github.com/symfony/cache/tree/v6.4.8"
},
"funding": [
{
@@ -7691,7 +7803,7 @@
"type": "tidelift"
}
],
- "time": "2024-04-18T09:22:46+00:00"
+ "time": "2024-05-31T14:49:08+00:00"
},
{
"name": "symfony/cache-contracts",
@@ -7771,16 +7883,16 @@
},
{
"name": "symfony/css-selector",
- "version": "v6.4.7",
+ "version": "v6.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "1c5d5c2103c3762aff27a27e1e2409e30a79083b"
+ "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c5d5c2103c3762aff27a27e1e2409e30a79083b",
- "reference": "1c5d5c2103c3762aff27a27e1e2409e30a79083b",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/4b61b02fe15db48e3687ce1c45ea385d1780fe08",
+ "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08",
"shasum": ""
},
"require": {
@@ -7816,7 +7928,7 @@
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/css-selector/tree/v6.4.7"
+ "source": "https://github.com/symfony/css-selector/tree/v6.4.8"
},
"funding": [
{
@@ -7832,20 +7944,20 @@
"type": "tidelift"
}
],
- "time": "2024-04-18T09:22:46+00:00"
+ "time": "2024-05-31T14:49:08+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v6.4.7",
+ "version": "v6.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "d84384f3f67de3cb650db64d685d70395dacfc3f"
+ "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d84384f3f67de3cb650db64d685d70395dacfc3f",
- "reference": "d84384f3f67de3cb650db64d685d70395dacfc3f",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/8d7507f02b06e06815e56bb39aa0128e3806208b",
+ "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b",
"shasum": ""
},
"require": {
@@ -7896,7 +8008,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.7"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.8"
},
"funding": [
{
@@ -7912,7 +8024,7 @@
"type": "tidelift"
}
],
- "time": "2024-04-18T09:22:46+00:00"
+ "time": "2024-05-31T14:49:08+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -7992,16 +8104,16 @@
},
{
"name": "symfony/finder",
- "version": "v6.4.7",
+ "version": "v6.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "511c48990be17358c23bf45c5d71ab85d40fb764"
+ "reference": "3ef977a43883215d560a2cecb82ec8e62131471c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/511c48990be17358c23bf45c5d71ab85d40fb764",
- "reference": "511c48990be17358c23bf45c5d71ab85d40fb764",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/3ef977a43883215d560a2cecb82ec8e62131471c",
+ "reference": "3ef977a43883215d560a2cecb82ec8e62131471c",
"shasum": ""
},
"require": {
@@ -8036,7 +8148,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v6.4.7"
+ "source": "https://github.com/symfony/finder/tree/v6.4.8"
},
"funding": [
{
@@ -8052,20 +8164,20 @@
"type": "tidelift"
}
],
- "time": "2024-04-23T10:36:43+00:00"
+ "time": "2024-05-31T14:49:08+00:00"
},
{
"name": "symfony/options-resolver",
- "version": "v6.4.7",
+ "version": "v6.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
- "reference": "9a3c92b490716ba6771f5beced13c6eda7183eed"
+ "reference": "22ab9e9101ab18de37839074f8a1197f55590c1b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/9a3c92b490716ba6771f5beced13c6eda7183eed",
- "reference": "9a3c92b490716ba6771f5beced13c6eda7183eed",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/22ab9e9101ab18de37839074f8a1197f55590c1b",
+ "reference": "22ab9e9101ab18de37839074f8a1197f55590c1b",
"shasum": ""
},
"require": {
@@ -8103,7 +8215,7 @@
"options"
],
"support": {
- "source": "https://github.com/symfony/options-resolver/tree/v6.4.7"
+ "source": "https://github.com/symfony/options-resolver/tree/v6.4.8"
},
"funding": [
{
@@ -8119,20 +8231,20 @@
"type": "tidelift"
}
],
- "time": "2024-04-18T09:22:46+00:00"
+ "time": "2024-05-31T14:49:08+00:00"
},
{
"name": "symfony/var-exporter",
- "version": "v6.4.7",
+ "version": "v6.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
- "reference": "825f9b00c37bbe1c1691cc1aff9b5451fc9b4405"
+ "reference": "792ca836f99b340f2e9ca9497c7953948c49a504"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-exporter/zipball/825f9b00c37bbe1c1691cc1aff9b5451fc9b4405",
- "reference": "825f9b00c37bbe1c1691cc1aff9b5451fc9b4405",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/792ca836f99b340f2e9ca9497c7953948c49a504",
+ "reference": "792ca836f99b340f2e9ca9497c7953948c49a504",
"shasum": ""
},
"require": {
@@ -8180,7 +8292,7 @@
"serialize"
],
"support": {
- "source": "https://github.com/symfony/var-exporter/tree/v6.4.7"
+ "source": "https://github.com/symfony/var-exporter/tree/v6.4.8"
},
"funding": [
{
@@ -8196,7 +8308,7 @@
"type": "tidelift"
}
],
- "time": "2024-04-18T09:22:46+00:00"
+ "time": "2024-05-31T14:49:08+00:00"
},
{
"name": "theseer/tokenizer",
diff --git a/db/migrations/1.2_step_102_datenfeldtypen.php b/db/migrations/1.2_step_102_datenfeldtypen.php
index 1e7b552..b639132 100644
--- a/db/migrations/1.2_step_102_datenfeldtypen.php
+++ b/db/migrations/1.2_step_102_datenfeldtypen.php
@@ -63,7 +63,7 @@ class Step102Datenfeldtypen extends Migration {
return;
}
- require_once 'lib/classes/DataFieldStructure.class.php';
+ require_once 'lib/classes/DataFieldStructure.php';
$ids = array_keys(DataFieldStructure::getDataFieldStructures());
diff --git a/db/migrations/1.6_step_25_raumzeit_db_conversion.php b/db/migrations/1.6_step_25_raumzeit_db_conversion.php
index 1dd8edf..9a8ffd7 100644
--- a/db/migrations/1.6_step_25_raumzeit_db_conversion.php
+++ b/db/migrations/1.6_step_25_raumzeit_db_conversion.php
@@ -55,8 +55,8 @@ class Step25RaumzeitDbConversion extends Migration
// include business logic
- require_once('lib/classes/Seminar.class.php');
- require_once('lib/resources/lib/VeranstaltungResourcesAssign.class.php');
+ require_once('lib/classes/Seminar.php');
+ require_once('lib/resources/lib/VeranstaltungResourcesAssign.php');
diff --git a/lib/admissionrules/conditionaladmission/ConditionalAdmission.class.php b/lib/admissionrules/conditionaladmission/ConditionalAdmission.class.php
deleted file mode 100644
index 7fdbc00..0000000
--- a/lib/admissionrules/conditionaladmission/ConditionalAdmission.class.php
+++ /dev/null
@@ -1,563 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-class ConditionalAdmission extends AdmissionRule
-{
- // --- ATTRIBUTES ---
-
- /**
- * All conditions that must be fulfilled for successful admission.
- */
- public $conditions = [];
-
- /**
- * Grouped conditions that must be fulfilled for successful admission.
- */
- public $conditiongroups = [];
-
- /**
- * Conditions that must be fulfilled for successful admission.
- */
- public $ungrouped_conditions = [];
-
- /**
- * Quota for grouped conditions
- */
- public $quota = [];
-
- /**
- * Are condition groups allowed?
- */
- public $conditiongroups_allowed = null;
-
- /**
- * courseset siblings of this rule
- */
- public $siblings = [];
-
- // --- OPERATIONS ---
-
- /**
- * Standard constructor.
- *
- * @param String $ruleId If this rule has been saved previously, it
- * will be loaded from database.
- * @return ConditionalAdmission the current object (this).
- */
- public function __construct($ruleId='', $courseSetId = '')
- {
- parent::__construct($ruleId, $courseSetId);
- $this->default_message = _('Sie erfüllen nicht die Bedingung: %s');
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('conditionaladmissions');
- }
- return $this;
- }
-
- /**
- * Adds a new UserFilter to this rule.
- *
- * @param UserFilter $condition
- * @param String $group
- * @param Int $quota
- * @return ConditionalAdmission
- */
- public function addCondition($condition, $group = '', $quota = 0)
- {
- if ($group) {
- $this->conditiongroups[$group][$condition->getId()] = $condition;
- $this->quota[$group] = $quota;
- } else {
- $this->ungrouped_conditions[$condition->getId()] = $condition;
- }
- return $this;
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete()
- {
- parent::delete();
- // Delete rule data.
- $stmt = DBManager::get()->prepare("DELETE FROM `conditionaladmissions`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- // Delete all associated conditions...
- foreach ($this->ungrouped_conditions as $condition) {
- $condition->delete();
- }
- foreach ($this->conditiongroups as $conditions) {
- foreach ($conditions as $condition) {
- $condition->delete();
- }
- }
- // ... and their connection to this rule.
- $stmt = DBManager::get()->prepare("DELETE `admission_condition`, `admission_conditiongroup` FROM `admission_condition`
- LEFT JOIN `admission_conditiongroup` USING( `conditiongroup_id`)
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- }
-
- /**
- * Gets all users that are matched by thís rule.
- *
- * @return Array An array containing IDs of users who are matched by
- * this rule.
- */
- public function getAffectedUsers()
- {
- $users = [];
- foreach ($this->ungrouped_conditions as $condition) {
- $users = array_intersect($users, $condition->getAffectedUsers());
- }
- foreach ($this->conditiongroups as $conditions) {
- foreach ($conditions as $condition) {
- $users = array_intersect($users, $condition->getAffectedUsers());
- }
- }
- return $users;
- }
-
- /**
- * Gets all defined conditions.
- *
- * @return Array
- */
- public function getConditions()
- {
- return $this->conditions;
- }
-
- /**
- * Gets all grouped conditiongroups.
- *
- * @return Array
- */
- public function getConditionGroups()
- {
- return $this->conditiongroups;
- }
-
- /**
- * Gets all grouped conditiongroups.
- *
- * @return Array
- */
- public function getUngroupedConditions()
- {
- return $this->ungrouped_conditions;
- }
-
- /**
- * Gets quota for given conditiongroup.
- *
- * @return Array
- */
- public function getQuota($group_id)
- {
- return $this->quota[$group_id];
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription()
- {
- return _('Über eine Menge von Bedingungen kann festgelegt werden, '.
- 'wer zur Anmeldung zu den Veranstaltungen des Anmeldesets '.
- 'zugelassen ist. Es muss nur eine der Bedingungen erfüllt sein, '.
- 'innerhalb einer Bedingung müssen aber alle Datenfelder '.
- 'zutreffen.');
- }
-
- /**
- * Return this rule's name.
- */
- public static function getName()
- {
- return _('Bedingte Anmeldung');
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- */
- public function getTemplate()
- {
- // Open generic admission rule template.
- $tpl = $GLOBALS['template_factory']->open('admission/rules/configure');
- $tpl->set_attribute('rule', $this);
- $factory = new Flexi\Factory(__DIR__ . '/templates/');
- // Now open specific template for this rule and insert base template.
- $tpl2 = $factory->open('configure');
- $tpl2->set_attribute('rule', $this);
- $tpl2->set_attribute('tpl', $tpl->render());
- return $tpl2->render();
- }
-
- /**
- * Helper function for loading data from DB. Generic AdmissionRule data is
- * loaded with the parent load() method.
- */
- public function load()
- {
- // Load basic data.
- $stmt = DBManager::get()->prepare("SELECT *
- FROM `conditionaladmissions` WHERE `rule_id`=? LIMIT 1");
- $stmt->execute([$this->id]);
- if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
- $this->message = $current['message'];
- $this->startTime = $current['start_time'];
- $this->endTime = $current['end_time'];
- // Retrieve conditions.
- $stmt = DBManager::get()->prepare("SELECT *
- FROM `admission_condition` LEFT JOIN `admission_conditiongroup` USING (`conditiongroup_id`) WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- $conditions = $stmt->fetchAll(PDO::FETCH_ASSOC);
- foreach ($conditions as $condition) {
- $currentCondition = new UserFilter($condition['filter_id']);
- if ($condition['conditiongroup_id']) {
- $this->conditiongroups[$condition['conditiongroup_id']][$condition['filter_id']] = $currentCondition;
- $this->quota[$condition['conditiongroup_id']] = $condition['quota'];
- } else {
- $this->ungrouped_conditions[$condition['filter_id']] = $currentCondition;
- }
- }
- }
- }
-
- /**
- * Checks if condition groups are allowed.
- *
- * @return Boolean
- */
- public function conditiongroupsAllowed()
- {
- if ($this->conditiongroups_allowed === null) {
- foreach ($this->getSiblings() as $rule) {
- if (get_class($rule) == 'ParticipantRestrictedAdmission') {
- if ($rule->getDistributionTime() > time()) {
- $this->conditiongroups_allowed = true;
- }
- }
- }
- }
- return $this->conditiongroups_allowed;
- }
-
- /**
- * Removes condition groups and sets all conditions as ungrouped.
- *
- */
- public function removeConditionGroups()
- {
- foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
- foreach ($conditions as $condition_id => $condition) {
- $this->ungrouped_conditions[$condition_id] = $condition;
- }
- }
- $this->conditiongroups = [];
- }
-
- /**
- * Removes the condition with the given ID from the rule.
- *
- * @param String $conditionId
- * @return ConditionalAdmission
- */
- public function removeCondition($conditionId)
- {
- if (isset($this->ungrouped_conditions[$conditionId])) {
- $this->ungrouped_conditions[$conditionId]->delete();
- unset($this->ungrouped_conditions[$conditionId]);
- } else {
- foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
- if (isset($conditions[$conditionId])) {
- $this->conditiongroups[$conditiongroup_id]->conditions[$conditionId]->delete();
- unset($this->conditiongroups[$conditiongroup_id]->conditions[$conditionId]);
- }
- }
- }
- return $this;
- }
-
- /**
- * Checks whether the given user fulfills the configured
- * admission conditions. Only one of the conditions needs to be
- * fulfilled (logical operator OR). The fields in a condition are
- * in conjunction (logical operator AND).
- *
- * @param String $userId
- * @param String $courseId
- * @return Array Array with conditions that have failed. If array
- * is empty, everything's all right.
- */
- public function ruleApplies($userId, $courseId)
- {
- $failed = [];
- // Check for rule validity time frame.
- if ($this->checkTimeFrame()) {
- // Check all configured conditions.
- foreach ($this->ungrouped_conditions as $condition) {
- if (!$condition->isFulfilled($userId)) {
- $failed[] = $this->getMessage($condition->toString());
- } else {
- $failed = [];
- break;
- }
- }
- foreach ($this->conditiongroups as $conditions) {
- foreach ($conditions as $condition) {
- if (!$condition->isFulfilled($userId)) {
- $failed[] = $this->getMessage($condition->toString());
- } else {
- $failed = [];
- break 2;
- }
- }
- }
- }
- return $failed;
- }
-
- /**
- * Uses the given data to fill the object values. This can be used
- * as a generic function for storing data if the concrete rule type
- * isn't known in advance.
- *
- * @param Array $data
- * @return AdmissionRule This object.
- */
- public function setAllData($data)
- {
- UserFilterField::getAvailableFilterFields();
- parent::setAllData($data);
- $this->conditions = [];
- $this->ungrouped_conditions = [];
- $this->conditiongroups = [];
- $this->quota = [];
- foreach ($data['conditions'] as $ser_con) {
- $condition = ObjectBuilder::build($ser_con, 'UserFilter');
- $this->addCondition($condition, $data['conditiongroup_'.$condition->getId()], $data['quota_'.$data['conditiongroup_'.$condition->getId()]] ?? 0);
- }
- foreach ($this->getConditiongroups() as $conditiongroup_id => $conditions) {
- if (mb_strlen($conditiongroup_id) < 32) {
- $group = md5(uniqid('conditiongroups' . microtime(), true));
-
- $this->conditiongroups[$group] = $this->conditiongroups[$conditiongroup_id];
- unset($this->conditiongroups[$conditiongroup_id]);
-
- $this->quota[$group] = $this->quota[$conditiongroup_id];
- unset($this->quota[$conditiongroup_id]);
- }
- }
- if (count($this->getConditiongroups()) && $data['conditiongroups_allowed']) {
- $this->conditiongroups_allowed = true;
- }
-
- return $this;
- }
-
- /**
- * Helper function for storing data to DB.
- */
- public function store()
- {
- // Store rule data.
- $stmt = DBManager::get()->prepare("INSERT INTO `conditionaladmissions`
- (`rule_id`, `message`, `start_time`, `end_time`, `mkdate`, `chdate`)
- VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
- `message`=VALUES(`message`),
- `start_time`=VALUES(`start_time`),
- `end_time`=VALUES(`end_time`),
- `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, $this->message, (int)$this->startTime,
- (int)$this->endTime, time(), time()]);
- // prepare condition data.
- $keys = array_keys($this->ungrouped_conditions);
- foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
- $keys = array_merge($keys, array_keys($conditions));
- }
-
- // Delete removed conditions from DB.
- $stmt = DBManager::get()->prepare("SELECT `filter_id`, `conditiongroup_id` FROM
- `admission_condition` WHERE `rule_id`=? AND `filter_id` NOT IN ('".
- implode("', '", $keys)."')");
- $stmt->execute([$this->id]);
- $groups = [];
- foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $entry) {
- $current = new UserFilter($entry['filter_id']);
- $current->delete();
- $groups[] = $entry['conditiongroup_id'];
- }
- DBManager::get()->exec("DELETE FROM `admission_condition`
- WHERE `rule_id`='".$this->id."' AND `filter_id` NOT IN ('".
- implode("', '", $keys)."')");
- // Store all conditions.
- $queries = [];
- $parameters = [];
- $groupqueries = [];
- $groupparameters = [];
- foreach ($this->ungrouped_conditions as $condition) {
- // Store each ungrouped condition...
- $condition->store();
- $queries[] = "(?, ?, ?, ?)";
- $parameters[] = $this->id;
- $parameters[] = $condition->getId();
- $parameters[] = '';
- $parameters[] = time();
- }
-
- foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
- $groupqueries[] = "(?, ?)";
- $groupparameters[] = $conditiongroup_id;
- $groupparameters[] = $this->quota[$conditiongroup_id];
- foreach ($conditions as $condition) {
- // Store each group of conditions...
- $condition->store();
- $queries[] = "(?, ?, ?, ?)";
- $parameters[] = $this->id;
- $parameters[] = $condition->getId();
- $parameters[] = $conditiongroup_id;
- $parameters[] = time();
- }
- }
-
- // Store all assignments between rule and condition.
- if (count($queries) > 0) {
- $stmt = DBManager::get()->prepare("INSERT INTO `admission_condition`
- (`rule_id`, `filter_id`, `conditiongroup_id`, `mkdate`)
- VALUES " . implode(',', $queries) . " ON DUPLICATE KEY UPDATE
- `filter_id`=VALUES(`filter_id`), `conditiongroup_id`=VALUES(`conditiongroup_id`)");
- $stmt->execute($parameters);
- }
-
- // Store all assignments between condition and conditiongroup.
- if (count($groupqueries) > 0) {
- $stmt = DBManager::get()->prepare("INSERT INTO `admission_conditiongroup`
- (`conditiongroup_id`, `quota`)
- VALUES " . implode(',', $groupqueries) . " ON DUPLICATE KEY UPDATE
- `quota`=VALUES(`quota`)");
- $stmt->execute($groupparameters);
- }
-
- return $this;
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- */
- public function toString()
- {
- $factory = new Flexi\Factory(__DIR__ . '/templates/');
- $tpl = $factory->open('info');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Validates if the given request data is sufficient to configure this rule
- * (e.g. if required values are present).
- *
- * @param Array $data Request data
- * @return Array Error messages.
- */
- public function validate($data)
- {
- $errors = parent::validate($data);
- if (!$data['conditions'] || !is_array($data['conditions'])) {
- $errors[] = _('Es muss mindestens eine Auswahlbedingung angegeben werden.');
- return $errors;
- }
- $quota = [];
- $quota_total = 0;
- $grouped = 0;
- $ungrouped = 0;
- $no_quota = 0;
- foreach ($data['conditions'] as $ser_con) {
- $condition = ObjectBuilder::build($ser_con, 'UserFilter');
- if ($data['conditiongroup_'.$condition->getId()]) {
- $grouped++;
- } else {
- $ungrouped++;
- }
- $quota[$data['conditiongroup_' . $condition->getId()]] = $data['quota_' . $data['conditiongroup_' . $condition->getId()]] ?? 0;
- if (!$quota[$data['conditiongroup_' . $condition->getId()]]) {
- $no_quota++;
- }
- }
- foreach ($quota as $part) {
- $quota_total += $part;
- }
- if ($grouped && $ungrouped) {
- $errors[] = sprintf(_('Es müssen entweder alle Bedingungen Teil einer Gruppe sein, oder keine. %s Bedingungen sind keiner Gruppe zugeordnet.'), $ungrouped);
- } elseif ($grouped && $quota_total !== 100) {
- $errors[] = _('Die Gesamtsumme der Kontingente muss 100 Prozent betragen.');
- } elseif ($grouped && $no_quota) {
- $errors[] = sprintf(_('Für %s Gruppen muss noch ein Kontingent festgelegt werden.'), $no_quota);
- }
- return $errors;
- }
-
- public function getMessage($condition = null)
- {
- $message = parent::getMessage();
- if ($condition) {
- return sprintf($message, $condition);
- } else {
- return $message;
- }
- }
-
- public function __clone()
- {
- $this->id = md5(uniqid(get_class($this)));
- $this->courseSetId = null;
- $cloned_conditions = [];
- foreach ($this->ungrouped_conditions as $condition) {
- $dolly = clone $condition;
- $cloned_conditions[$dolly->id] = $dolly;
- }
- $this->ungrouped_conditions = $cloned_conditions;
- $cloned_conditiongroups = [];
- $cloned_quota = [];
- foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
- $cloned_conditiongroup_id = md5(uniqid($conditiongroup_id));
- $cloned_quota[$cloned_conditiongroup_id] = $this->quota[$conditiongroup_id];
- foreach ($conditions as $condition) {
- $dolly = clone $condition;
- $cloned_conditiongroups[$cloned_conditiongroup_id][$dolly->id] = $dolly;
- }
- }
- $this->conditiongroups = $cloned_conditiongroups;
- $this->quota = $cloned_quota;
- }
-
- public function setSiblings($siblings = [])
- {
- parent::setSiblings($siblings);
- $this->conditiongroups_allowed = null;
- }
-}
diff --git a/lib/admissionrules/conditionaladmission/ConditionalAdmission.php b/lib/admissionrules/conditionaladmission/ConditionalAdmission.php
new file mode 100644
index 0000000..7fdbc00
--- /dev/null
+++ b/lib/admissionrules/conditionaladmission/ConditionalAdmission.php
@@ -0,0 +1,563 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+class ConditionalAdmission extends AdmissionRule
+{
+ // --- ATTRIBUTES ---
+
+ /**
+ * All conditions that must be fulfilled for successful admission.
+ */
+ public $conditions = [];
+
+ /**
+ * Grouped conditions that must be fulfilled for successful admission.
+ */
+ public $conditiongroups = [];
+
+ /**
+ * Conditions that must be fulfilled for successful admission.
+ */
+ public $ungrouped_conditions = [];
+
+ /**
+ * Quota for grouped conditions
+ */
+ public $quota = [];
+
+ /**
+ * Are condition groups allowed?
+ */
+ public $conditiongroups_allowed = null;
+
+ /**
+ * courseset siblings of this rule
+ */
+ public $siblings = [];
+
+ // --- OPERATIONS ---
+
+ /**
+ * Standard constructor.
+ *
+ * @param String $ruleId If this rule has been saved previously, it
+ * will be loaded from database.
+ * @return ConditionalAdmission the current object (this).
+ */
+ public function __construct($ruleId='', $courseSetId = '')
+ {
+ parent::__construct($ruleId, $courseSetId);
+ $this->default_message = _('Sie erfüllen nicht die Bedingung: %s');
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('conditionaladmissions');
+ }
+ return $this;
+ }
+
+ /**
+ * Adds a new UserFilter to this rule.
+ *
+ * @param UserFilter $condition
+ * @param String $group
+ * @param Int $quota
+ * @return ConditionalAdmission
+ */
+ public function addCondition($condition, $group = '', $quota = 0)
+ {
+ if ($group) {
+ $this->conditiongroups[$group][$condition->getId()] = $condition;
+ $this->quota[$group] = $quota;
+ } else {
+ $this->ungrouped_conditions[$condition->getId()] = $condition;
+ }
+ return $this;
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete()
+ {
+ parent::delete();
+ // Delete rule data.
+ $stmt = DBManager::get()->prepare("DELETE FROM `conditionaladmissions`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ // Delete all associated conditions...
+ foreach ($this->ungrouped_conditions as $condition) {
+ $condition->delete();
+ }
+ foreach ($this->conditiongroups as $conditions) {
+ foreach ($conditions as $condition) {
+ $condition->delete();
+ }
+ }
+ // ... and their connection to this rule.
+ $stmt = DBManager::get()->prepare("DELETE `admission_condition`, `admission_conditiongroup` FROM `admission_condition`
+ LEFT JOIN `admission_conditiongroup` USING( `conditiongroup_id`)
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ }
+
+ /**
+ * Gets all users that are matched by thís rule.
+ *
+ * @return Array An array containing IDs of users who are matched by
+ * this rule.
+ */
+ public function getAffectedUsers()
+ {
+ $users = [];
+ foreach ($this->ungrouped_conditions as $condition) {
+ $users = array_intersect($users, $condition->getAffectedUsers());
+ }
+ foreach ($this->conditiongroups as $conditions) {
+ foreach ($conditions as $condition) {
+ $users = array_intersect($users, $condition->getAffectedUsers());
+ }
+ }
+ return $users;
+ }
+
+ /**
+ * Gets all defined conditions.
+ *
+ * @return Array
+ */
+ public function getConditions()
+ {
+ return $this->conditions;
+ }
+
+ /**
+ * Gets all grouped conditiongroups.
+ *
+ * @return Array
+ */
+ public function getConditionGroups()
+ {
+ return $this->conditiongroups;
+ }
+
+ /**
+ * Gets all grouped conditiongroups.
+ *
+ * @return Array
+ */
+ public function getUngroupedConditions()
+ {
+ return $this->ungrouped_conditions;
+ }
+
+ /**
+ * Gets quota for given conditiongroup.
+ *
+ * @return Array
+ */
+ public function getQuota($group_id)
+ {
+ return $this->quota[$group_id];
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription()
+ {
+ return _('Über eine Menge von Bedingungen kann festgelegt werden, '.
+ 'wer zur Anmeldung zu den Veranstaltungen des Anmeldesets '.
+ 'zugelassen ist. Es muss nur eine der Bedingungen erfüllt sein, '.
+ 'innerhalb einer Bedingung müssen aber alle Datenfelder '.
+ 'zutreffen.');
+ }
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName()
+ {
+ return _('Bedingte Anmeldung');
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ */
+ public function getTemplate()
+ {
+ // Open generic admission rule template.
+ $tpl = $GLOBALS['template_factory']->open('admission/rules/configure');
+ $tpl->set_attribute('rule', $this);
+ $factory = new Flexi\Factory(__DIR__ . '/templates/');
+ // Now open specific template for this rule and insert base template.
+ $tpl2 = $factory->open('configure');
+ $tpl2->set_attribute('rule', $this);
+ $tpl2->set_attribute('tpl', $tpl->render());
+ return $tpl2->render();
+ }
+
+ /**
+ * Helper function for loading data from DB. Generic AdmissionRule data is
+ * loaded with the parent load() method.
+ */
+ public function load()
+ {
+ // Load basic data.
+ $stmt = DBManager::get()->prepare("SELECT *
+ FROM `conditionaladmissions` WHERE `rule_id`=? LIMIT 1");
+ $stmt->execute([$this->id]);
+ if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $this->message = $current['message'];
+ $this->startTime = $current['start_time'];
+ $this->endTime = $current['end_time'];
+ // Retrieve conditions.
+ $stmt = DBManager::get()->prepare("SELECT *
+ FROM `admission_condition` LEFT JOIN `admission_conditiongroup` USING (`conditiongroup_id`) WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ $conditions = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($conditions as $condition) {
+ $currentCondition = new UserFilter($condition['filter_id']);
+ if ($condition['conditiongroup_id']) {
+ $this->conditiongroups[$condition['conditiongroup_id']][$condition['filter_id']] = $currentCondition;
+ $this->quota[$condition['conditiongroup_id']] = $condition['quota'];
+ } else {
+ $this->ungrouped_conditions[$condition['filter_id']] = $currentCondition;
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if condition groups are allowed.
+ *
+ * @return Boolean
+ */
+ public function conditiongroupsAllowed()
+ {
+ if ($this->conditiongroups_allowed === null) {
+ foreach ($this->getSiblings() as $rule) {
+ if (get_class($rule) == 'ParticipantRestrictedAdmission') {
+ if ($rule->getDistributionTime() > time()) {
+ $this->conditiongroups_allowed = true;
+ }
+ }
+ }
+ }
+ return $this->conditiongroups_allowed;
+ }
+
+ /**
+ * Removes condition groups and sets all conditions as ungrouped.
+ *
+ */
+ public function removeConditionGroups()
+ {
+ foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
+ foreach ($conditions as $condition_id => $condition) {
+ $this->ungrouped_conditions[$condition_id] = $condition;
+ }
+ }
+ $this->conditiongroups = [];
+ }
+
+ /**
+ * Removes the condition with the given ID from the rule.
+ *
+ * @param String $conditionId
+ * @return ConditionalAdmission
+ */
+ public function removeCondition($conditionId)
+ {
+ if (isset($this->ungrouped_conditions[$conditionId])) {
+ $this->ungrouped_conditions[$conditionId]->delete();
+ unset($this->ungrouped_conditions[$conditionId]);
+ } else {
+ foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
+ if (isset($conditions[$conditionId])) {
+ $this->conditiongroups[$conditiongroup_id]->conditions[$conditionId]->delete();
+ unset($this->conditiongroups[$conditiongroup_id]->conditions[$conditionId]);
+ }
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Checks whether the given user fulfills the configured
+ * admission conditions. Only one of the conditions needs to be
+ * fulfilled (logical operator OR). The fields in a condition are
+ * in conjunction (logical operator AND).
+ *
+ * @param String $userId
+ * @param String $courseId
+ * @return Array Array with conditions that have failed. If array
+ * is empty, everything's all right.
+ */
+ public function ruleApplies($userId, $courseId)
+ {
+ $failed = [];
+ // Check for rule validity time frame.
+ if ($this->checkTimeFrame()) {
+ // Check all configured conditions.
+ foreach ($this->ungrouped_conditions as $condition) {
+ if (!$condition->isFulfilled($userId)) {
+ $failed[] = $this->getMessage($condition->toString());
+ } else {
+ $failed = [];
+ break;
+ }
+ }
+ foreach ($this->conditiongroups as $conditions) {
+ foreach ($conditions as $condition) {
+ if (!$condition->isFulfilled($userId)) {
+ $failed[] = $this->getMessage($condition->toString());
+ } else {
+ $failed = [];
+ break 2;
+ }
+ }
+ }
+ }
+ return $failed;
+ }
+
+ /**
+ * Uses the given data to fill the object values. This can be used
+ * as a generic function for storing data if the concrete rule type
+ * isn't known in advance.
+ *
+ * @param Array $data
+ * @return AdmissionRule This object.
+ */
+ public function setAllData($data)
+ {
+ UserFilterField::getAvailableFilterFields();
+ parent::setAllData($data);
+ $this->conditions = [];
+ $this->ungrouped_conditions = [];
+ $this->conditiongroups = [];
+ $this->quota = [];
+ foreach ($data['conditions'] as $ser_con) {
+ $condition = ObjectBuilder::build($ser_con, 'UserFilter');
+ $this->addCondition($condition, $data['conditiongroup_'.$condition->getId()], $data['quota_'.$data['conditiongroup_'.$condition->getId()]] ?? 0);
+ }
+ foreach ($this->getConditiongroups() as $conditiongroup_id => $conditions) {
+ if (mb_strlen($conditiongroup_id) < 32) {
+ $group = md5(uniqid('conditiongroups' . microtime(), true));
+
+ $this->conditiongroups[$group] = $this->conditiongroups[$conditiongroup_id];
+ unset($this->conditiongroups[$conditiongroup_id]);
+
+ $this->quota[$group] = $this->quota[$conditiongroup_id];
+ unset($this->quota[$conditiongroup_id]);
+ }
+ }
+ if (count($this->getConditiongroups()) && $data['conditiongroups_allowed']) {
+ $this->conditiongroups_allowed = true;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Helper function for storing data to DB.
+ */
+ public function store()
+ {
+ // Store rule data.
+ $stmt = DBManager::get()->prepare("INSERT INTO `conditionaladmissions`
+ (`rule_id`, `message`, `start_time`, `end_time`, `mkdate`, `chdate`)
+ VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
+ `message`=VALUES(`message`),
+ `start_time`=VALUES(`start_time`),
+ `end_time`=VALUES(`end_time`),
+ `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, $this->message, (int)$this->startTime,
+ (int)$this->endTime, time(), time()]);
+ // prepare condition data.
+ $keys = array_keys($this->ungrouped_conditions);
+ foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
+ $keys = array_merge($keys, array_keys($conditions));
+ }
+
+ // Delete removed conditions from DB.
+ $stmt = DBManager::get()->prepare("SELECT `filter_id`, `conditiongroup_id` FROM
+ `admission_condition` WHERE `rule_id`=? AND `filter_id` NOT IN ('".
+ implode("', '", $keys)."')");
+ $stmt->execute([$this->id]);
+ $groups = [];
+ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $entry) {
+ $current = new UserFilter($entry['filter_id']);
+ $current->delete();
+ $groups[] = $entry['conditiongroup_id'];
+ }
+ DBManager::get()->exec("DELETE FROM `admission_condition`
+ WHERE `rule_id`='".$this->id."' AND `filter_id` NOT IN ('".
+ implode("', '", $keys)."')");
+ // Store all conditions.
+ $queries = [];
+ $parameters = [];
+ $groupqueries = [];
+ $groupparameters = [];
+ foreach ($this->ungrouped_conditions as $condition) {
+ // Store each ungrouped condition...
+ $condition->store();
+ $queries[] = "(?, ?, ?, ?)";
+ $parameters[] = $this->id;
+ $parameters[] = $condition->getId();
+ $parameters[] = '';
+ $parameters[] = time();
+ }
+
+ foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
+ $groupqueries[] = "(?, ?)";
+ $groupparameters[] = $conditiongroup_id;
+ $groupparameters[] = $this->quota[$conditiongroup_id];
+ foreach ($conditions as $condition) {
+ // Store each group of conditions...
+ $condition->store();
+ $queries[] = "(?, ?, ?, ?)";
+ $parameters[] = $this->id;
+ $parameters[] = $condition->getId();
+ $parameters[] = $conditiongroup_id;
+ $parameters[] = time();
+ }
+ }
+
+ // Store all assignments between rule and condition.
+ if (count($queries) > 0) {
+ $stmt = DBManager::get()->prepare("INSERT INTO `admission_condition`
+ (`rule_id`, `filter_id`, `conditiongroup_id`, `mkdate`)
+ VALUES " . implode(',', $queries) . " ON DUPLICATE KEY UPDATE
+ `filter_id`=VALUES(`filter_id`), `conditiongroup_id`=VALUES(`conditiongroup_id`)");
+ $stmt->execute($parameters);
+ }
+
+ // Store all assignments between condition and conditiongroup.
+ if (count($groupqueries) > 0) {
+ $stmt = DBManager::get()->prepare("INSERT INTO `admission_conditiongroup`
+ (`conditiongroup_id`, `quota`)
+ VALUES " . implode(',', $groupqueries) . " ON DUPLICATE KEY UPDATE
+ `quota`=VALUES(`quota`)");
+ $stmt->execute($groupparameters);
+ }
+
+ return $this;
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ */
+ public function toString()
+ {
+ $factory = new Flexi\Factory(__DIR__ . '/templates/');
+ $tpl = $factory->open('info');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Validates if the given request data is sufficient to configure this rule
+ * (e.g. if required values are present).
+ *
+ * @param Array $data Request data
+ * @return Array Error messages.
+ */
+ public function validate($data)
+ {
+ $errors = parent::validate($data);
+ if (!$data['conditions'] || !is_array($data['conditions'])) {
+ $errors[] = _('Es muss mindestens eine Auswahlbedingung angegeben werden.');
+ return $errors;
+ }
+ $quota = [];
+ $quota_total = 0;
+ $grouped = 0;
+ $ungrouped = 0;
+ $no_quota = 0;
+ foreach ($data['conditions'] as $ser_con) {
+ $condition = ObjectBuilder::build($ser_con, 'UserFilter');
+ if ($data['conditiongroup_'.$condition->getId()]) {
+ $grouped++;
+ } else {
+ $ungrouped++;
+ }
+ $quota[$data['conditiongroup_' . $condition->getId()]] = $data['quota_' . $data['conditiongroup_' . $condition->getId()]] ?? 0;
+ if (!$quota[$data['conditiongroup_' . $condition->getId()]]) {
+ $no_quota++;
+ }
+ }
+ foreach ($quota as $part) {
+ $quota_total += $part;
+ }
+ if ($grouped && $ungrouped) {
+ $errors[] = sprintf(_('Es müssen entweder alle Bedingungen Teil einer Gruppe sein, oder keine. %s Bedingungen sind keiner Gruppe zugeordnet.'), $ungrouped);
+ } elseif ($grouped && $quota_total !== 100) {
+ $errors[] = _('Die Gesamtsumme der Kontingente muss 100 Prozent betragen.');
+ } elseif ($grouped && $no_quota) {
+ $errors[] = sprintf(_('Für %s Gruppen muss noch ein Kontingent festgelegt werden.'), $no_quota);
+ }
+ return $errors;
+ }
+
+ public function getMessage($condition = null)
+ {
+ $message = parent::getMessage();
+ if ($condition) {
+ return sprintf($message, $condition);
+ } else {
+ return $message;
+ }
+ }
+
+ public function __clone()
+ {
+ $this->id = md5(uniqid(get_class($this)));
+ $this->courseSetId = null;
+ $cloned_conditions = [];
+ foreach ($this->ungrouped_conditions as $condition) {
+ $dolly = clone $condition;
+ $cloned_conditions[$dolly->id] = $dolly;
+ }
+ $this->ungrouped_conditions = $cloned_conditions;
+ $cloned_conditiongroups = [];
+ $cloned_quota = [];
+ foreach ($this->conditiongroups as $conditiongroup_id => $conditions) {
+ $cloned_conditiongroup_id = md5(uniqid($conditiongroup_id));
+ $cloned_quota[$cloned_conditiongroup_id] = $this->quota[$conditiongroup_id];
+ foreach ($conditions as $condition) {
+ $dolly = clone $condition;
+ $cloned_conditiongroups[$cloned_conditiongroup_id][$dolly->id] = $dolly;
+ }
+ }
+ $this->conditiongroups = $cloned_conditiongroups;
+ $this->quota = $cloned_quota;
+ }
+
+ public function setSiblings($siblings = [])
+ {
+ parent::setSiblings($siblings);
+ $this->conditiongroups_allowed = null;
+ }
+}
diff --git a/lib/admissionrules/coursememberadmission/CourseMemberAdmission.class.php b/lib/admissionrules/coursememberadmission/CourseMemberAdmission.class.php
deleted file mode 100644
index a3309e8..0000000
--- a/lib/admissionrules/coursememberadmission/CourseMemberAdmission.class.php
+++ /dev/null
@@ -1,260 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-class CourseMemberAdmission extends AdmissionRule
-{
- const MODE_MUST_BE_IN_COURSES = 0;
- const MODE_MAY_NOT_BE_IN_COURSES = 1;
- // --- ATTRIBUTES ---
-
- /**
- * End of course admission.
- */
- public $courses_to_add = '[]';
- public $modus = '';
-
- // --- OPERATIONS ---
-
- /**
- * Standard constructor
- *
- * @param String $ruleId
- * @param String e
- */
- public function __construct($ruleId = '', $courseSetId = '')
- {
- parent::__construct($ruleId, $courseSetId);
-
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('coursememberadmissions');
- }
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete()
- {
- parent::delete();
-
- // Delete rule data.
- DBManager::get()->execute(
- "DELETE FROM `coursememberadmissions` WHERE `rule_id` = ?",
- [$this->id]
- );
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription()
- {
- return _("Anmelderegeln dieses Typs legen eine Veranstaltung fest, in der die Nutzer bereits eingetragen sein müssen, oder in der sie nicht eingetragen sein dürfen, um sich zu Veranstaltungen des Anmeldesets anmelden zu können.");
- }
-
- /**
- * Return this rule's name.
- */
- public static function getName()
- {
- return _("Veranstaltungsbezogene Anmeldung");
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- */
- public function getTemplate()
- {
- // Open generic admission rule template.
- $tpl = $GLOBALS['template_factory']->open('admission/rules/configure');
- $tpl->set_attribute('rule', $this);
-
- return $this->getTemplateFactory()->render('configure', [
- 'rule' => $this,
- 'tpl' => $tpl->render(),
- 'courses' => $this->getDecodedCourses(),
- ]);
- }
-
- /**
- * Helper function for loading rule definition from database.
- */
- public function load()
- {
- // Load data.
- $stmt = DBManager::get()->prepare("SELECT *
- FROM `coursememberadmissions` WHERE `rule_id`=? LIMIT 1");
- $stmt->execute([$this->id]);
- if ($current = $stmt->fetchOne()) {
- $this->message = $current['message'];
- $this->startTime = $current['start_time'];
- $this->endTime = $current['end_time'];
- $this->courses_to_add = $current['courses'];
- $this->modus = (int) $current['modus'];
- }
- }
-
- /**
- * Is admission allowed according to the defined time frame?
- *
- * @param String $userId
- * @param String $courseId
- * @return Array
- */
- public function ruleApplies($userId, $courseId)
- {
- $errors = [];
- if ($this->checkTimeFrame()) {
- $courses = $this->getDecodedCourses();
- foreach ($courses as $course) {
- $is_member = CourseMember::exists([$course->id, $userId]);
-
- if (($this->modus == self::MODE_MUST_BE_IN_COURSES && !$is_member)
- || ($this->modus == self::MODE_MAY_NOT_BE_IN_COURSES && $is_member)
- ) {
- $errors[] = $this->getMessage($course);
- }
- }
-
- // mode: "Mitgliedschaft ist in mindestens einer dieser Veranstaltungen notwendig"
- if ($this->modus == self::MODE_MUST_BE_IN_COURSES && count($errors) < count($courses)) {
- $errors = [];
- }
- }
-
- return $errors;
- }
-
- /**
- * Uses the given data to fill the object values. This can be used
- * as a generic function for storing data if the concrete rule type
- * isn't known in advance.
- *
- * @param Array $data
- * @return AdmissionRule This object.
- */
- public function setAllData($data)
- {
- parent::setAllData($data);
-
- $this->modus = (int) $data['modus'];
- $this->courses_to_add = json_encode(array_keys($data['courses_to_add']));
- return $this;
- }
-
- /**
- * Store rule definition to database.
- */
- public function store()
- {
- // Store data.
- $stmt = DBManager::get()->prepare("INSERT INTO `coursememberadmissions`
- (`rule_id`, `message`, `courses`, `modus`, `start_time`,
- `end_time`, `mkdate`, `chdate`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
- ON DUPLICATE KEY UPDATE `start_time`=VALUES(`start_time`),
- `end_time`=VALUES(`end_time`),message=VALUES(message),courses=VALUES(courses),modus=VALUES(modus), `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, $this->message, $this->courses_to_add, (int)$this->modus, (int)$this->startTime,
- (int)$this->endTime, time(), time()]);
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- */
- public function toString()
- {
- return $this->getTemplateFactory()->render('info', [
- 'courses' => $this->getDecodedCourses(),
- 'rule' => $this,
- 'modus' => $this->modus,
- ]);
- }
-
- /**
- * Validates if the given request data is sufficient to configure this rule
- * (e.g. if required values are present).
- *
- * @param Array $data Request data
- * @return Array Error messages.
- */
- public function validate($data)
- {
- $errors = parent::validate($data);
- if (!$data['courses_to_add']) {
- $errors[] = _('Bitte wählen Sie eine Veranstaltung aus.');
- }
- return $errors;
- }
-
- public function getMessage($course = null)
- {
- $message = parent::getMessage();
-
- if ($course) {
- return sprintf($message, $course->getFullName('number-name'));
- } else {
- return $message;
- }
- }
-
- private function getDecodedCourses()
- {
- $decoded_courses = json_decode($this->courses_to_add, true);
- if (!$decoded_courses) {
- return [];
- }
- return Course::findMany($decoded_courses);
- }
-
- public function getValidityPeriod(): string
- {
- if ($this->getStartTime() && $this->getEndTime()) {
- return sprintf(
- _('Diese Regel gilt von %s bis %s.'),
- strftime('%d.%m.%Y %H:%M', $this->getStartTime()),
- strftime('%d.%m.%Y %H:%M', $this->getEndTime())
- );
- }
-
- if ($this->getStartTime() && !$this->getEndTime()) {
- return sprintf(
- _('Diese Regel gilt ab %s.'),
- strftime('%d.%m.%Y %H:%M', $this->getStartTime())
- );
- }
-
- if (!$this->getStartTime() && $this->getEndTime()) {
- return sprintf(
- _('Diese Regel gilt bis %s.'),
- strftime('%d.%m.%Y %H:%M', $this->getEndTime())
- );
- }
-
- return '';
- }
-
- private function getTemplateFactory(): Flexi\Factory
- {
- return new Flexi\Factory(__DIR__ . '/templates/');
- }
-}
diff --git a/lib/admissionrules/coursememberadmission/CourseMemberAdmission.php b/lib/admissionrules/coursememberadmission/CourseMemberAdmission.php
new file mode 100644
index 0000000..a3309e8
--- /dev/null
+++ b/lib/admissionrules/coursememberadmission/CourseMemberAdmission.php
@@ -0,0 +1,260 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+class CourseMemberAdmission extends AdmissionRule
+{
+ const MODE_MUST_BE_IN_COURSES = 0;
+ const MODE_MAY_NOT_BE_IN_COURSES = 1;
+ // --- ATTRIBUTES ---
+
+ /**
+ * End of course admission.
+ */
+ public $courses_to_add = '[]';
+ public $modus = '';
+
+ // --- OPERATIONS ---
+
+ /**
+ * Standard constructor
+ *
+ * @param String $ruleId
+ * @param String e
+ */
+ public function __construct($ruleId = '', $courseSetId = '')
+ {
+ parent::__construct($ruleId, $courseSetId);
+
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('coursememberadmissions');
+ }
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete()
+ {
+ parent::delete();
+
+ // Delete rule data.
+ DBManager::get()->execute(
+ "DELETE FROM `coursememberadmissions` WHERE `rule_id` = ?",
+ [$this->id]
+ );
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription()
+ {
+ return _("Anmelderegeln dieses Typs legen eine Veranstaltung fest, in der die Nutzer bereits eingetragen sein müssen, oder in der sie nicht eingetragen sein dürfen, um sich zu Veranstaltungen des Anmeldesets anmelden zu können.");
+ }
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName()
+ {
+ return _("Veranstaltungsbezogene Anmeldung");
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ */
+ public function getTemplate()
+ {
+ // Open generic admission rule template.
+ $tpl = $GLOBALS['template_factory']->open('admission/rules/configure');
+ $tpl->set_attribute('rule', $this);
+
+ return $this->getTemplateFactory()->render('configure', [
+ 'rule' => $this,
+ 'tpl' => $tpl->render(),
+ 'courses' => $this->getDecodedCourses(),
+ ]);
+ }
+
+ /**
+ * Helper function for loading rule definition from database.
+ */
+ public function load()
+ {
+ // Load data.
+ $stmt = DBManager::get()->prepare("SELECT *
+ FROM `coursememberadmissions` WHERE `rule_id`=? LIMIT 1");
+ $stmt->execute([$this->id]);
+ if ($current = $stmt->fetchOne()) {
+ $this->message = $current['message'];
+ $this->startTime = $current['start_time'];
+ $this->endTime = $current['end_time'];
+ $this->courses_to_add = $current['courses'];
+ $this->modus = (int) $current['modus'];
+ }
+ }
+
+ /**
+ * Is admission allowed according to the defined time frame?
+ *
+ * @param String $userId
+ * @param String $courseId
+ * @return Array
+ */
+ public function ruleApplies($userId, $courseId)
+ {
+ $errors = [];
+ if ($this->checkTimeFrame()) {
+ $courses = $this->getDecodedCourses();
+ foreach ($courses as $course) {
+ $is_member = CourseMember::exists([$course->id, $userId]);
+
+ if (($this->modus == self::MODE_MUST_BE_IN_COURSES && !$is_member)
+ || ($this->modus == self::MODE_MAY_NOT_BE_IN_COURSES && $is_member)
+ ) {
+ $errors[] = $this->getMessage($course);
+ }
+ }
+
+ // mode: "Mitgliedschaft ist in mindestens einer dieser Veranstaltungen notwendig"
+ if ($this->modus == self::MODE_MUST_BE_IN_COURSES && count($errors) < count($courses)) {
+ $errors = [];
+ }
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Uses the given data to fill the object values. This can be used
+ * as a generic function for storing data if the concrete rule type
+ * isn't known in advance.
+ *
+ * @param Array $data
+ * @return AdmissionRule This object.
+ */
+ public function setAllData($data)
+ {
+ parent::setAllData($data);
+
+ $this->modus = (int) $data['modus'];
+ $this->courses_to_add = json_encode(array_keys($data['courses_to_add']));
+ return $this;
+ }
+
+ /**
+ * Store rule definition to database.
+ */
+ public function store()
+ {
+ // Store data.
+ $stmt = DBManager::get()->prepare("INSERT INTO `coursememberadmissions`
+ (`rule_id`, `message`, `courses`, `modus`, `start_time`,
+ `end_time`, `mkdate`, `chdate`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE `start_time`=VALUES(`start_time`),
+ `end_time`=VALUES(`end_time`),message=VALUES(message),courses=VALUES(courses),modus=VALUES(modus), `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, $this->message, $this->courses_to_add, (int)$this->modus, (int)$this->startTime,
+ (int)$this->endTime, time(), time()]);
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ */
+ public function toString()
+ {
+ return $this->getTemplateFactory()->render('info', [
+ 'courses' => $this->getDecodedCourses(),
+ 'rule' => $this,
+ 'modus' => $this->modus,
+ ]);
+ }
+
+ /**
+ * Validates if the given request data is sufficient to configure this rule
+ * (e.g. if required values are present).
+ *
+ * @param Array $data Request data
+ * @return Array Error messages.
+ */
+ public function validate($data)
+ {
+ $errors = parent::validate($data);
+ if (!$data['courses_to_add']) {
+ $errors[] = _('Bitte wählen Sie eine Veranstaltung aus.');
+ }
+ return $errors;
+ }
+
+ public function getMessage($course = null)
+ {
+ $message = parent::getMessage();
+
+ if ($course) {
+ return sprintf($message, $course->getFullName('number-name'));
+ } else {
+ return $message;
+ }
+ }
+
+ private function getDecodedCourses()
+ {
+ $decoded_courses = json_decode($this->courses_to_add, true);
+ if (!$decoded_courses) {
+ return [];
+ }
+ return Course::findMany($decoded_courses);
+ }
+
+ public function getValidityPeriod(): string
+ {
+ if ($this->getStartTime() && $this->getEndTime()) {
+ return sprintf(
+ _('Diese Regel gilt von %s bis %s.'),
+ strftime('%d.%m.%Y %H:%M', $this->getStartTime()),
+ strftime('%d.%m.%Y %H:%M', $this->getEndTime())
+ );
+ }
+
+ if ($this->getStartTime() && !$this->getEndTime()) {
+ return sprintf(
+ _('Diese Regel gilt ab %s.'),
+ strftime('%d.%m.%Y %H:%M', $this->getStartTime())
+ );
+ }
+
+ if (!$this->getStartTime() && $this->getEndTime()) {
+ return sprintf(
+ _('Diese Regel gilt bis %s.'),
+ strftime('%d.%m.%Y %H:%M', $this->getEndTime())
+ );
+ }
+
+ return '';
+ }
+
+ private function getTemplateFactory(): Flexi\Factory
+ {
+ return new Flexi\Factory(__DIR__ . '/templates/');
+ }
+}
diff --git a/lib/admissionrules/limitedadmission/LimitedAdmission.class.php b/lib/admissionrules/limitedadmission/LimitedAdmission.class.php
deleted file mode 100644
index a193bda..0000000
--- a/lib/admissionrules/limitedadmission/LimitedAdmission.class.php
+++ /dev/null
@@ -1,284 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-class LimitedAdmission extends AdmissionRule
-{
- // --- ATTRIBUTES ---
-
-
- /**
- * Maximal number of courses that a user can register for.
- */
- public $maxNumber = 1;
-
- // --- OPERATIONS ---
-
- /**
- * Standard constructor.
- *
- * @param String ruleId
- * @return LimitedAdmission
- */
- public function __construct($ruleId='', $courseSetId = '')
- {
- parent::__construct($ruleId, $courseSetId);
- $this->default_message = _('Sie haben sich bereits zur maximalen Anzahl von %s Veranstaltungen angemeldet.');
-
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('limitedadmissions');
- }
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete() {
- parent::delete();
- // Delete rule data.
- $stmt = DBManager::get()->prepare("DELETE FROM `limitedadmissions`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- // Delete all custom max numbers.
- $stmt = DBManager::get()->prepare("DELETE FROM `userlimits`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- }
-
- /**
- * Users can specify their own maximal number of courses they want
- * to be registered for. This method gets the specified value for the
- * given user or the max number that has been specified by the rule if no
- * custom number was set.
- *
- * @param userId
- * @return Integer
- */
- public function getCustomMaxNumber($userId)
- {
- // Initially we use the number given per admission rule.
- $maxNumber = $this->maxNumber;
- $stmt = DBManager::get()->prepare("SELECT `maxnumber`
- FROM `userlimits` WHERE rule_id=? AND user_id=?");
- $stmt->execute([$this->id, $userId]);
- // The user has given some custom number.
- if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
- // Custom number must be smaller than rule max number.
- $maxNumber = min($maxNumber, $current['maxnumber']);
- }
- return $maxNumber;
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription() {
- return _("Diese Art von Anmelderegel legt eine Maximalzahl von ".
- "Veranstaltungen fest, an denen Nutzer im aktuellen ".
- "Anmeldeset teilnehmen können.");
- }
-
- /**
- * Gets the maximal number of courses that users can be registered for.
- *
- * @return Integer
- */
- public function getMaxNumber()
- {
- return (int)$this->maxNumber;
- }
-
- public function getMaxNumberForUser($userId)
- {
- return min($this->maxNumber, $this->getCustomMaxNumber($userId));
- }
-
-
- /**
- * Return this rule's name.
- */
- public static function getName() {
- return _("Anmeldung zu maximal n Veranstaltungen");
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- */
- public function getTemplate() {
- // Open generic admission rule template.
- $tpl = $GLOBALS['template_factory']->open('admission/rules/configure');
- $tpl->set_attribute('rule', $this);
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- // Now open specific template for this rule and insert base template.
- $tpl2 = $factory->open('configure');
- $tpl2->set_attribute('rule', $this);
- $tpl2->set_attribute('tpl', $tpl->render());
- return $tpl2->render();
- }
-
- /**
- * Internal helper function for loading rule definition from database.
- */
- public function load() {
- $stmt = DBManager::get()->prepare("SELECT *
- FROM `limitedadmissions` WHERE `rule_id`=? LIMIT 1");
- $stmt->execute([$this->id]);
- if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
- $this->message = $current['message'];
- $this->startTime = $current['start_time'];
- $this->endTime = $current['end_time'];
- $this->maxNumber = $current['maxnumber'];
- }
- }
-
- /**
- * Does the current rule allow the given user to register as participant
- * in the given course? That only happens when the user has no more than
- * the given number of registrations at the other courses in the course set.
- *
- * @param String userId
- * @param String courseId
- * @return Array Any errors that occurred on admission.
- */
- public function ruleApplies($userId, $courseId)
- {
- $errors = [];
- // Check for rule validity time frame.
- if ($this->checkTimeFrame()) {
- // How many courses from this set has the user already registered for?
- $db = DBManager::get();
- $number = $db->fetchColumn("SELECT COUNT(*)
- FROM `seminar_user` WHERE `user_id`=? AND `status` IN ('user', 'autor') AND `Seminar_id` IN (
- SELECT `Seminar_id` FROM `seminar_courseset` WHERE `set_id`=?)", [$userId, $this->courseSetId]);
- $number += $db->fetchColumn("SELECT COUNT(*)
- FROM `admission_seminar_user` WHERE `user_id`=? AND `Seminar_id` IN (
- SELECT `Seminar_id` FROM `seminar_courseset` WHERE `set_id`=?)", [$userId, $this->courseSetId]);
- // Check if the number is smaller than admission rule limit
- if (!($number <
- $this->getMaxNumber())) {
- $errors[] = $this->getMessage($this->getMaxNumber());
- }
- }
- return $errors;
- }
-
- /**
- * Uses the given data to fill the object values. This can be used
- * as a generic function for storing data if the concrete rule type
- * isn't known in advance.
- *
- * @param Array $data
- * @return AdmissionRule This object.
- */
- public function setAllData($data) {
- parent::setAllData($data);
- $this->maxNumber = intval($data['maxnumber']);
- return $this;
- }
-
- /**
- * Sets a new maximal number of courses that the given user can
- * register for.
- *
- * @param String userId
- * @param Integer maxNumber
- * @return LimitedAdmission
- */
- public function setCustomMaxNumber($userId, $maxNumber)
- {
- $stmt = DBManager::get()->prepare("INSERT INTO `userlimits`
- (`rule_id`, `user_id`, `maxnumber`, `mkdate`, `chdate`)
- VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
- `maxnumber`=VALUES(`maxnumber`), `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, $userId,
- min($this->maxNumber, $maxNumber), time(), time()]);
- return $this;
- }
-
- /**
- * Sets a new maximal number of courses for registration of the same user.
- *
- * @param Integer newMaxNumber
- * @return LimitedAdmission
- */
- public function setMaxNumber($newMaxNumber)
- {
- $this->maxNumber = $newMaxNumber;
- return $this;
- }
-
- /**
- * Helper function for storing data to DB.
- */
- public function store() {
- // Store data.
- $stmt = DBManager::get()->prepare("INSERT INTO `limitedadmissions`
- (`rule_id`, `message`, `start_time`, `end_time`, `maxnumber`,
- `mkdate`, `chdate`)
- VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
- `message`=VALUES(`message`), `start_time`=VALUES(`start_time`),
- `end_time`=VALUES(`end_time`), `maxnumber`=VALUES(`maxnumber`),
- `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, $this->message, (int)$this->startTime,
- (int)$this->endTime, $this->maxNumber, time(), time()]);
- return $this;
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- */
- public function toString() {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- $tpl = $factory->open('info');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Validates if the given request data is sufficient to configure this rule
- * (e.g. if required values are present).
- *
- * @param Array Request data
- * @return Array Error messages.
- */
- public function validate($data)
- {
- $errors = parent::validate($data);
- if (!$data['maxnumber']) {
- $errors[] = _('Bitte geben Sie die maximale Anzahl erlaubter Anmeldungen an.');
- }
- return $errors;
- }
-
- public function getMessage($max_number = null)
- {
- $message = parent::getMessage();
- if (isset($max_number)) {
- return sprintf($message, $max_number);
- } else {
- return $message;
- }
- }
-} /* end of class LimitedAdmission */
-
-?>
diff --git a/lib/admissionrules/limitedadmission/LimitedAdmission.php b/lib/admissionrules/limitedadmission/LimitedAdmission.php
new file mode 100644
index 0000000..a193bda
--- /dev/null
+++ b/lib/admissionrules/limitedadmission/LimitedAdmission.php
@@ -0,0 +1,284 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+class LimitedAdmission extends AdmissionRule
+{
+ // --- ATTRIBUTES ---
+
+
+ /**
+ * Maximal number of courses that a user can register for.
+ */
+ public $maxNumber = 1;
+
+ // --- OPERATIONS ---
+
+ /**
+ * Standard constructor.
+ *
+ * @param String ruleId
+ * @return LimitedAdmission
+ */
+ public function __construct($ruleId='', $courseSetId = '')
+ {
+ parent::__construct($ruleId, $courseSetId);
+ $this->default_message = _('Sie haben sich bereits zur maximalen Anzahl von %s Veranstaltungen angemeldet.');
+
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('limitedadmissions');
+ }
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete() {
+ parent::delete();
+ // Delete rule data.
+ $stmt = DBManager::get()->prepare("DELETE FROM `limitedadmissions`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ // Delete all custom max numbers.
+ $stmt = DBManager::get()->prepare("DELETE FROM `userlimits`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ }
+
+ /**
+ * Users can specify their own maximal number of courses they want
+ * to be registered for. This method gets the specified value for the
+ * given user or the max number that has been specified by the rule if no
+ * custom number was set.
+ *
+ * @param userId
+ * @return Integer
+ */
+ public function getCustomMaxNumber($userId)
+ {
+ // Initially we use the number given per admission rule.
+ $maxNumber = $this->maxNumber;
+ $stmt = DBManager::get()->prepare("SELECT `maxnumber`
+ FROM `userlimits` WHERE rule_id=? AND user_id=?");
+ $stmt->execute([$this->id, $userId]);
+ // The user has given some custom number.
+ if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ // Custom number must be smaller than rule max number.
+ $maxNumber = min($maxNumber, $current['maxnumber']);
+ }
+ return $maxNumber;
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription() {
+ return _("Diese Art von Anmelderegel legt eine Maximalzahl von ".
+ "Veranstaltungen fest, an denen Nutzer im aktuellen ".
+ "Anmeldeset teilnehmen können.");
+ }
+
+ /**
+ * Gets the maximal number of courses that users can be registered for.
+ *
+ * @return Integer
+ */
+ public function getMaxNumber()
+ {
+ return (int)$this->maxNumber;
+ }
+
+ public function getMaxNumberForUser($userId)
+ {
+ return min($this->maxNumber, $this->getCustomMaxNumber($userId));
+ }
+
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName() {
+ return _("Anmeldung zu maximal n Veranstaltungen");
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ */
+ public function getTemplate() {
+ // Open generic admission rule template.
+ $tpl = $GLOBALS['template_factory']->open('admission/rules/configure');
+ $tpl->set_attribute('rule', $this);
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ // Now open specific template for this rule and insert base template.
+ $tpl2 = $factory->open('configure');
+ $tpl2->set_attribute('rule', $this);
+ $tpl2->set_attribute('tpl', $tpl->render());
+ return $tpl2->render();
+ }
+
+ /**
+ * Internal helper function for loading rule definition from database.
+ */
+ public function load() {
+ $stmt = DBManager::get()->prepare("SELECT *
+ FROM `limitedadmissions` WHERE `rule_id`=? LIMIT 1");
+ $stmt->execute([$this->id]);
+ if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $this->message = $current['message'];
+ $this->startTime = $current['start_time'];
+ $this->endTime = $current['end_time'];
+ $this->maxNumber = $current['maxnumber'];
+ }
+ }
+
+ /**
+ * Does the current rule allow the given user to register as participant
+ * in the given course? That only happens when the user has no more than
+ * the given number of registrations at the other courses in the course set.
+ *
+ * @param String userId
+ * @param String courseId
+ * @return Array Any errors that occurred on admission.
+ */
+ public function ruleApplies($userId, $courseId)
+ {
+ $errors = [];
+ // Check for rule validity time frame.
+ if ($this->checkTimeFrame()) {
+ // How many courses from this set has the user already registered for?
+ $db = DBManager::get();
+ $number = $db->fetchColumn("SELECT COUNT(*)
+ FROM `seminar_user` WHERE `user_id`=? AND `status` IN ('user', 'autor') AND `Seminar_id` IN (
+ SELECT `Seminar_id` FROM `seminar_courseset` WHERE `set_id`=?)", [$userId, $this->courseSetId]);
+ $number += $db->fetchColumn("SELECT COUNT(*)
+ FROM `admission_seminar_user` WHERE `user_id`=? AND `Seminar_id` IN (
+ SELECT `Seminar_id` FROM `seminar_courseset` WHERE `set_id`=?)", [$userId, $this->courseSetId]);
+ // Check if the number is smaller than admission rule limit
+ if (!($number <
+ $this->getMaxNumber())) {
+ $errors[] = $this->getMessage($this->getMaxNumber());
+ }
+ }
+ return $errors;
+ }
+
+ /**
+ * Uses the given data to fill the object values. This can be used
+ * as a generic function for storing data if the concrete rule type
+ * isn't known in advance.
+ *
+ * @param Array $data
+ * @return AdmissionRule This object.
+ */
+ public function setAllData($data) {
+ parent::setAllData($data);
+ $this->maxNumber = intval($data['maxnumber']);
+ return $this;
+ }
+
+ /**
+ * Sets a new maximal number of courses that the given user can
+ * register for.
+ *
+ * @param String userId
+ * @param Integer maxNumber
+ * @return LimitedAdmission
+ */
+ public function setCustomMaxNumber($userId, $maxNumber)
+ {
+ $stmt = DBManager::get()->prepare("INSERT INTO `userlimits`
+ (`rule_id`, `user_id`, `maxnumber`, `mkdate`, `chdate`)
+ VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
+ `maxnumber`=VALUES(`maxnumber`), `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, $userId,
+ min($this->maxNumber, $maxNumber), time(), time()]);
+ return $this;
+ }
+
+ /**
+ * Sets a new maximal number of courses for registration of the same user.
+ *
+ * @param Integer newMaxNumber
+ * @return LimitedAdmission
+ */
+ public function setMaxNumber($newMaxNumber)
+ {
+ $this->maxNumber = $newMaxNumber;
+ return $this;
+ }
+
+ /**
+ * Helper function for storing data to DB.
+ */
+ public function store() {
+ // Store data.
+ $stmt = DBManager::get()->prepare("INSERT INTO `limitedadmissions`
+ (`rule_id`, `message`, `start_time`, `end_time`, `maxnumber`,
+ `mkdate`, `chdate`)
+ VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
+ `message`=VALUES(`message`), `start_time`=VALUES(`start_time`),
+ `end_time`=VALUES(`end_time`), `maxnumber`=VALUES(`maxnumber`),
+ `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, $this->message, (int)$this->startTime,
+ (int)$this->endTime, $this->maxNumber, time(), time()]);
+ return $this;
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ */
+ public function toString() {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ $tpl = $factory->open('info');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Validates if the given request data is sufficient to configure this rule
+ * (e.g. if required values are present).
+ *
+ * @param Array Request data
+ * @return Array Error messages.
+ */
+ public function validate($data)
+ {
+ $errors = parent::validate($data);
+ if (!$data['maxnumber']) {
+ $errors[] = _('Bitte geben Sie die maximale Anzahl erlaubter Anmeldungen an.');
+ }
+ return $errors;
+ }
+
+ public function getMessage($max_number = null)
+ {
+ $message = parent::getMessage();
+ if (isset($max_number)) {
+ return sprintf($message, $max_number);
+ } else {
+ return $message;
+ }
+ }
+} /* end of class LimitedAdmission */
+
+?>
diff --git a/lib/admissionrules/lockedadmission/LockedAdmission.class.php b/lib/admissionrules/lockedadmission/LockedAdmission.class.php
deleted file mode 100644
index 0ca324b..0000000
--- a/lib/admissionrules/lockedadmission/LockedAdmission.class.php
+++ /dev/null
@@ -1,136 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-class LockedAdmission extends AdmissionRule
-{
- // --- ATTRIBUTES ---
-
- // --- OPERATIONS ---
-
- /**
- * Standard constructor.
- *
- * @param String ruleId
- */
- public function __construct($ruleId='', $courseSetId = '')
- {
- parent::__construct($ruleId, $courseSetId);
- $this->default_message = _('Die Anmeldung ist gesperrt.');
-
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('lockedadmissions');
- }
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete() {
- parent::delete();
- // Delete rule data.
- $stmt = DBManager::get()->prepare("DELETE FROM `lockedadmissions`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription() {
- return _("Diese Art von Anmelderegel sperrt die Anmeldung ".
- "zu allen zugehörigen Veranstaltungen, sodass sich niemand ".
- "eintragen kann.");
- }
-
- /**
- * Return this rule's name.
- */
- public static function getName() {
- return _("Anmeldung gesperrt");
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- */
- public function getTemplate() {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- // Now open specific template for this rule and insert base template.
- $tpl = $factory->open('configure');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Internal helper function for loading rule definition from database.
- */
- public function load() {
- $stmt = DBManager::get()->prepare("SELECT * FROM `lockedadmissions`
- WHERE `rule_id`=? LIMIT 1");
- $stmt->execute([$this->id]);
- if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
- $this->message = $current['message'];
- }
- }
-
- /**
- * Does the current rule allow the given user to register as participant
- * in the given course? Never happens here as admission is completely
- * locked.
- *
- * @param String userId
- * @param String courseId
- * @return Array Any errors that occurred on admission.
- */
- public function ruleApplies($userId, $courseId)
- {
- // YOU CANNOT PASS!
- return [$this->getMessage()];
- }
-
- /**
- * Helper function for storing data to DB.
- */
- public function store() {
- // Store data.
- $stmt = DBManager::get()->prepare("INSERT INTO `lockedadmissions`
- (`rule_id`, `message`, `mkdate`, `chdate`)
- VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE
- `message`=VALUES(`message`), `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, $this->message, time(), time()]);
- return $this;
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- */
- public function toString() {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- $tpl = $factory->open('info');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
-} /* end of class LockedAdmission */
-
-?>
diff --git a/lib/admissionrules/lockedadmission/LockedAdmission.php b/lib/admissionrules/lockedadmission/LockedAdmission.php
new file mode 100644
index 0000000..0ca324b
--- /dev/null
+++ b/lib/admissionrules/lockedadmission/LockedAdmission.php
@@ -0,0 +1,136 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+class LockedAdmission extends AdmissionRule
+{
+ // --- ATTRIBUTES ---
+
+ // --- OPERATIONS ---
+
+ /**
+ * Standard constructor.
+ *
+ * @param String ruleId
+ */
+ public function __construct($ruleId='', $courseSetId = '')
+ {
+ parent::__construct($ruleId, $courseSetId);
+ $this->default_message = _('Die Anmeldung ist gesperrt.');
+
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('lockedadmissions');
+ }
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete() {
+ parent::delete();
+ // Delete rule data.
+ $stmt = DBManager::get()->prepare("DELETE FROM `lockedadmissions`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription() {
+ return _("Diese Art von Anmelderegel sperrt die Anmeldung ".
+ "zu allen zugehörigen Veranstaltungen, sodass sich niemand ".
+ "eintragen kann.");
+ }
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName() {
+ return _("Anmeldung gesperrt");
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ */
+ public function getTemplate() {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ // Now open specific template for this rule and insert base template.
+ $tpl = $factory->open('configure');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Internal helper function for loading rule definition from database.
+ */
+ public function load() {
+ $stmt = DBManager::get()->prepare("SELECT * FROM `lockedadmissions`
+ WHERE `rule_id`=? LIMIT 1");
+ $stmt->execute([$this->id]);
+ if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $this->message = $current['message'];
+ }
+ }
+
+ /**
+ * Does the current rule allow the given user to register as participant
+ * in the given course? Never happens here as admission is completely
+ * locked.
+ *
+ * @param String userId
+ * @param String courseId
+ * @return Array Any errors that occurred on admission.
+ */
+ public function ruleApplies($userId, $courseId)
+ {
+ // YOU CANNOT PASS!
+ return [$this->getMessage()];
+ }
+
+ /**
+ * Helper function for storing data to DB.
+ */
+ public function store() {
+ // Store data.
+ $stmt = DBManager::get()->prepare("INSERT INTO `lockedadmissions`
+ (`rule_id`, `message`, `mkdate`, `chdate`)
+ VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE
+ `message`=VALUES(`message`), `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, $this->message, time(), time()]);
+ return $this;
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ */
+ public function toString() {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ $tpl = $factory->open('info');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+} /* end of class LockedAdmission */
+
+?>
diff --git a/lib/admissionrules/participantrestrictedadmission/ParticipantRestrictedAdmission.class.php b/lib/admissionrules/participantrestrictedadmission/ParticipantRestrictedAdmission.class.php
deleted file mode 100644
index 28a910d..0000000
--- a/lib/admissionrules/participantrestrictedadmission/ParticipantRestrictedAdmission.class.php
+++ /dev/null
@@ -1,230 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-class ParticipantRestrictedAdmission extends AdmissionRule
-{
- // --- ATTRIBUTES ---
-
- /**
- * Timestamp for execution of seat distribution algorithm
- */
- public $distributionTime = null;
-
- public $first_come_first_served_allowed = false;
-
- public $minimum_timespan_to_distribution_time = 120;
-
- public $prio_exists = false;
-
-
- // --- OPERATIONS ---
-
- /**
- * Standard constructor
- *
- * @param String $ruleId
- * @param String $courseSetId
- */
- public function __construct($ruleId = '', $courseSetId = '')
- {
- parent::__construct($ruleId, $courseSetId);
- $this->first_come_first_served_allowed = (bool)Config::get()->ENABLE_COURSESET_FCFS;
- $this->default_message = _('Es stehen keine weiteren Plätze zur Verfügung.');
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('participantrestrictedadmissions');
- }
- }
-
- public function isFCFSallowed()
- {
- return $this->first_come_first_served_allowed;
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete()
- {
- if ($this->prio_exists) {
- $set_id = DBManager::get()->fetchColumn("SELECT set_id FROM courseset_rule WHERE rule_id = ? LIMIT 1", [$this->id]);
- //Delete priorities
- AdmissionPriority::unsetAllPriorities($set_id);
- }
- parent::delete();
- // Delete rule data.
- $stmt = DBManager::get()->prepare("DELETE FROM `participantrestrictedadmissions`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription()
- {
- return _("Anmelderegeln dieses Typs legen fest, ob die zugeordneten Veranstaltungen eine maximale Teilnehmendenanzahl haben. Die Platzverteilung erfolgt automatisiert.");
- }
-
- /**
- * Gets the time for seat distribution algorithm.
- *
- * @return int
- */
- public function getDistributionTime()
- {
- return $this->distributionTime;
- }
-
- /**
- * Return this rule's name.
- */
- public static function getName()
- {
- return _("Beschränkte Teilnehmendenanzahl");
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- */
- public function getTemplate()
- {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- // Open specific template for this rule and insert base template.
- $tpl = $factory->open('configure');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Helper function for loading rule definition from database.
- */
- public function load()
- {
- // Load data.
- $stmt = DBManager::get()->prepare("SELECT *
- FROM `participantrestrictedadmissions` WHERE `rule_id`=? LIMIT 1");
- $stmt->execute([$this->id]);
- if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
- $this->message = $current['message'];
- $this->distributionTime = $current['distribution_time'];
- if ($current['distribution_time'] > 0) {
- $this->prio_exists = DBManager::get()->fetchColumn("SELECT 1 FROM courseset_rule INNER JOIN priorities USING(set_id) WHERE rule_id = ? LIMIT 1", [$this->id]);
- }
- }
- }
-
- /**
- * Uses the given data to fill the object values. This can be used
- * as a generic function for storing data if the concrete rule type
- * isn't known in advance.
- *
- * @param Array $data
- * @return AdmissionRule This object.
- */
- public function setAllData($data)
- {
- parent::setAllData($data);
- if (!empty($data['distributiondate'])) {
- if (!$data['distributiontime']) {
- $data['distributiontime'] = '23:59';
- }
- $ddate = strtotime($data['distributiondate'] . ' ' . $data['distributiontime']);
- $this->setDistributionTime($ddate);
- }
- if (!empty($data['enable_FCFS'])) {
- $this->setDistributionTime(0);
- }
- if (!empty($data['startdate'])) {
- $starttime = strtotime($data['startdate'] . ' ' . $data['starttime']);
- if ($starttime > time()) {
- $this->minimum_timespan_to_distribution_time = $this->minimum_timespan_to_distribution_time + (($starttime - time()) / 60);
- }
- }
-
- return $this;
- }
-
- /**
- * Sets a new timestamp for seat distribution algorithm execution.
- *
- * @param int $newDistributionTime
- * @return ParticipantRestrictedAdmission
- */
- public function setDistributionTime($newDistributionTime)
- {
- $this->distributionTime = $newDistributionTime;
- return $this;
- }
-
-
- /**
- * Store rule definition to database.
- */
- public function store()
- {
- // Store data.
- $stmt = DBManager::get()->prepare("INSERT INTO `participantrestrictedadmissions`
- (`rule_id`, `message`, `distribution_time`,
- `mkdate`, `chdate`) VALUES (?, ?, ?, ?, ?)
- ON DUPLICATE KEY UPDATE
- `distribution_time`=VALUES(`distribution_time`),
- message=VALUES(message), `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, (string)$this->message,
- (int)$this->distributionTime, time(), time()]);
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- */
- public function toString()
- {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- $tpl = $factory->open('info');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Validates if the given request data is sufficient to configure this rule
- * (e.g. if required values are present).
- *
- * @param Array $data Request data
- * @return Array Error messages.
- */
- public function validate($data)
- {
- $errors = parent::validate($data);
- if (!$data['distributiontime']) {
- $data['distributiontime'] = '23:59';
- }
- $ddate = strtotime($data['distributiondate'] . ' ' . $data['distributiontime']);
- if (empty($data['enable_FCFS']) && (empty($data['distributiondate']) || $ddate < (time() + $this->minimum_timespan_to_distribution_time * 60))) {
- $errors[] = sprintf(_('Bitte geben Sie für die Platzverteilung ein Datum an, das weiter in der Zukunft liegt. Das frühestmögliche Datum ist %s.'), strftime('%x %R', time() + $this->minimum_timespan_to_distribution_time*60));
- }
- if (!empty($data['enable_FCFS']) && $data['distributiondate']) {
- $errors[] = _('Sie können kein Datum für die automatische Platzverteilung einstellen und gleichzeitig die automatische Platzverteilung ausschalten.');
- }
- return $errors;
- }
-}
diff --git a/lib/admissionrules/participantrestrictedadmission/ParticipantRestrictedAdmission.php b/lib/admissionrules/participantrestrictedadmission/ParticipantRestrictedAdmission.php
new file mode 100644
index 0000000..28a910d
--- /dev/null
+++ b/lib/admissionrules/participantrestrictedadmission/ParticipantRestrictedAdmission.php
@@ -0,0 +1,230 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+class ParticipantRestrictedAdmission extends AdmissionRule
+{
+ // --- ATTRIBUTES ---
+
+ /**
+ * Timestamp for execution of seat distribution algorithm
+ */
+ public $distributionTime = null;
+
+ public $first_come_first_served_allowed = false;
+
+ public $minimum_timespan_to_distribution_time = 120;
+
+ public $prio_exists = false;
+
+
+ // --- OPERATIONS ---
+
+ /**
+ * Standard constructor
+ *
+ * @param String $ruleId
+ * @param String $courseSetId
+ */
+ public function __construct($ruleId = '', $courseSetId = '')
+ {
+ parent::__construct($ruleId, $courseSetId);
+ $this->first_come_first_served_allowed = (bool)Config::get()->ENABLE_COURSESET_FCFS;
+ $this->default_message = _('Es stehen keine weiteren Plätze zur Verfügung.');
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('participantrestrictedadmissions');
+ }
+ }
+
+ public function isFCFSallowed()
+ {
+ return $this->first_come_first_served_allowed;
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete()
+ {
+ if ($this->prio_exists) {
+ $set_id = DBManager::get()->fetchColumn("SELECT set_id FROM courseset_rule WHERE rule_id = ? LIMIT 1", [$this->id]);
+ //Delete priorities
+ AdmissionPriority::unsetAllPriorities($set_id);
+ }
+ parent::delete();
+ // Delete rule data.
+ $stmt = DBManager::get()->prepare("DELETE FROM `participantrestrictedadmissions`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription()
+ {
+ return _("Anmelderegeln dieses Typs legen fest, ob die zugeordneten Veranstaltungen eine maximale Teilnehmendenanzahl haben. Die Platzverteilung erfolgt automatisiert.");
+ }
+
+ /**
+ * Gets the time for seat distribution algorithm.
+ *
+ * @return int
+ */
+ public function getDistributionTime()
+ {
+ return $this->distributionTime;
+ }
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName()
+ {
+ return _("Beschränkte Teilnehmendenanzahl");
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ */
+ public function getTemplate()
+ {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ // Open specific template for this rule and insert base template.
+ $tpl = $factory->open('configure');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Helper function for loading rule definition from database.
+ */
+ public function load()
+ {
+ // Load data.
+ $stmt = DBManager::get()->prepare("SELECT *
+ FROM `participantrestrictedadmissions` WHERE `rule_id`=? LIMIT 1");
+ $stmt->execute([$this->id]);
+ if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $this->message = $current['message'];
+ $this->distributionTime = $current['distribution_time'];
+ if ($current['distribution_time'] > 0) {
+ $this->prio_exists = DBManager::get()->fetchColumn("SELECT 1 FROM courseset_rule INNER JOIN priorities USING(set_id) WHERE rule_id = ? LIMIT 1", [$this->id]);
+ }
+ }
+ }
+
+ /**
+ * Uses the given data to fill the object values. This can be used
+ * as a generic function for storing data if the concrete rule type
+ * isn't known in advance.
+ *
+ * @param Array $data
+ * @return AdmissionRule This object.
+ */
+ public function setAllData($data)
+ {
+ parent::setAllData($data);
+ if (!empty($data['distributiondate'])) {
+ if (!$data['distributiontime']) {
+ $data['distributiontime'] = '23:59';
+ }
+ $ddate = strtotime($data['distributiondate'] . ' ' . $data['distributiontime']);
+ $this->setDistributionTime($ddate);
+ }
+ if (!empty($data['enable_FCFS'])) {
+ $this->setDistributionTime(0);
+ }
+ if (!empty($data['startdate'])) {
+ $starttime = strtotime($data['startdate'] . ' ' . $data['starttime']);
+ if ($starttime > time()) {
+ $this->minimum_timespan_to_distribution_time = $this->minimum_timespan_to_distribution_time + (($starttime - time()) / 60);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets a new timestamp for seat distribution algorithm execution.
+ *
+ * @param int $newDistributionTime
+ * @return ParticipantRestrictedAdmission
+ */
+ public function setDistributionTime($newDistributionTime)
+ {
+ $this->distributionTime = $newDistributionTime;
+ return $this;
+ }
+
+
+ /**
+ * Store rule definition to database.
+ */
+ public function store()
+ {
+ // Store data.
+ $stmt = DBManager::get()->prepare("INSERT INTO `participantrestrictedadmissions`
+ (`rule_id`, `message`, `distribution_time`,
+ `mkdate`, `chdate`) VALUES (?, ?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE
+ `distribution_time`=VALUES(`distribution_time`),
+ message=VALUES(message), `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, (string)$this->message,
+ (int)$this->distributionTime, time(), time()]);
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ */
+ public function toString()
+ {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ $tpl = $factory->open('info');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Validates if the given request data is sufficient to configure this rule
+ * (e.g. if required values are present).
+ *
+ * @param Array $data Request data
+ * @return Array Error messages.
+ */
+ public function validate($data)
+ {
+ $errors = parent::validate($data);
+ if (!$data['distributiontime']) {
+ $data['distributiontime'] = '23:59';
+ }
+ $ddate = strtotime($data['distributiondate'] . ' ' . $data['distributiontime']);
+ if (empty($data['enable_FCFS']) && (empty($data['distributiondate']) || $ddate < (time() + $this->minimum_timespan_to_distribution_time * 60))) {
+ $errors[] = sprintf(_('Bitte geben Sie für die Platzverteilung ein Datum an, das weiter in der Zukunft liegt. Das frühestmögliche Datum ist %s.'), strftime('%x %R', time() + $this->minimum_timespan_to_distribution_time*60));
+ }
+ if (!empty($data['enable_FCFS']) && $data['distributiondate']) {
+ $errors[] = _('Sie können kein Datum für die automatische Platzverteilung einstellen und gleichzeitig die automatische Platzverteilung ausschalten.');
+ }
+ return $errors;
+ }
+}
diff --git a/lib/admissionrules/passwordadmission/PasswordAdmission.class.php b/lib/admissionrules/passwordadmission/PasswordAdmission.class.php
deleted file mode 100644
index 0650630..0000000
--- a/lib/admissionrules/passwordadmission/PasswordAdmission.class.php
+++ /dev/null
@@ -1,241 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-require_once 'vendor/phpass/PasswordHash.php';
-
-class PasswordAdmission extends AdmissionRule
-{
- // --- ATTRIBUTES ---
- /*
- * Password hasher (phpass library)
- */
- public $hasher = null;
-
- /*
- * Crypted password.
- */
- public $password = '';
-
- // --- OPERATIONS ---
-
- public $new = false;
- /**
- * Standard constructor.
- *
- * @param String ruleId
- */
- public function __construct($ruleId='', $courseSetId = '')
- {
- parent::__construct($ruleId, $courseSetId);
- $this->default_message = _('Das eingegebene Passwort ist falsch.');
- // Create a new bcrypt password hasher (exclude weaker algorithms).
- $this->hasher = new PasswordHash(8, false);
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('passwordadmissions');
- $this->new = true;
- }
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete() {
- parent::delete();
- // Delete rule data.
- $stmt = DBManager::get()->prepare("DELETE FROM `passwordadmissions`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription() {
- return _("Mit dieser Anmelderegel können Sie ein Passwort für Zugang ".
- "zu den zugeordneten Veranstaltungen vergeben. Die Anmeldung ist ".
- "dann nur für Personen möglich, die dieses Passwort kennen.");
- }
-
- /**
- * Shows an input form where the user can enter a password and try to get
- * past the holy gates.
- *
- * @return String A template-based input form.
- */
- public function getInput() {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- $tpl = $factory->open('input');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Return this rule's name.
- */
- public static function getName() {
- return _("Anmeldung mit Passwort");
- }
-
- /**
- * Gets the bcrypted hash of the current password.
- *
- * @return String
- */
- public function getPassword() {
- return $this->password;
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- */
- public function getTemplate() {
- // Open generic admission rule template.
- $tpl = $GLOBALS['template_factory']->open('admission/rules/configure');
- $tpl->set_attribute('rule', $this);
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- // Now open specific template for this rule and insert base template.
- $tpl2 = $factory->open('configure');
- $tpl2->set_attribute('rule', $this);
- $tpl2->set_attribute('tpl', $tpl->render());
- return $tpl2->render();
- }
-
- /**
- * Internal helper function for loading rule definition from database.
- */
- public function load() {
- $stmt = DBManager::get()->prepare("SELECT * FROM `passwordadmissions`
- WHERE `rule_id`=? LIMIT 1");
- $stmt->execute([$this->id]);
- if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
- $this->message = $current['message'];
- $this->startTime = $current['start_time'];
- $this->endTime = $current['end_time'];
- $this->password = $current['password'];
- }
- }
-
- /**
- * Does the current rule allow the given user to register as participant
- * in the given course? Here, a given password (via the getInput method) is
- * compared to the stored encrypted one.
- *
- * @param String userId
- * @param String courseId
- * @return array
- */
- public function ruleApplies($userId, $courseId)
- {
- $errors = [];
- if ($this->checkTimeFrame()) {
- if (Request::get('pwarule_password') === null) {
- $errors[] = _('Die Eingabe eines Passwortes ist erforderlich.');
- } else {
- CSRFProtection::verifyUnsafeRequest();
- $pwcheck = $this->hasher->CheckPassword(Request::get('pwarule_password'),
- $this->getPassword());
- //migrated passwords
- $pwcheck_m = $this->hasher->CheckPassword(md5(Request::get('pwarule_password')),
- $this->getPassword());
- if (!($pwcheck || $pwcheck_m)) {
- $errors[] = $this->getMessage();
- }
- }
- }
- return $errors;
- }
-
- /**
- * Uses the given data to fill the object values. This can be used
- * as a generic function for storing data if the concrete rule type
- * isn't known in advance.
- *
- * @param Array $data
- * @return AdmissionRule This object.
- */
- public function setAllData($data) {
- parent::setAllData($data);
- if ($this->new || $data['password1'] !== '') {
- $this->setPassword($data['password1']);
- }
- return $this;
- }
-
- /**
- * Sets the password by bcrypting the given clear text password.
- *
- * @param String $clearText The clear text password to be set.
- * @return PasswordAdmission
- */
- public function setPassword($clearText) {
- $this->password = $this->hasher->HashPassword($clearText);
- return $this;
- }
-
- /**
- * Helper function for storing data to DB.
- */
- public function store() {
- // Store data.
- $stmt = DBManager::get()->prepare("INSERT INTO `passwordadmissions`
- (`rule_id`, `message`, `start_time`, `end_time`, `password`,
- `mkdate`, `chdate`)
- VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
- `message`=VALUES(`message`), `start_time`=VALUES(`start_time`),
- `end_time`=VALUES(`end_time`), `password`=VALUES(`password`),
- `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, $this->message, (int)$this->startTime, (int)$this->endTime, $this->password,
- time(), time()]);
- return $this;
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- */
- public function toString() {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- $tpl = $factory->open('info');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Validates if the given request data is sufficient to configure this rule
- * (e.g. if required values are present).
- *
- * @param Array Request data
- * @return Array Error messages.
- */
- public function validate($data)
- {
- $errors = parent::validate($data);
- if ($data['password1'] === '' && $this->new) {
- $errors[] = _('Das Passwort darf nicht leer sein.');
- }
- if ($data['password1'] !== $data['password2']) {
- $errors[] = _('Das Passwort stimmt nicht mit der Wiederholung überein.');
- }
- return $errors;
- }
-}
diff --git a/lib/admissionrules/passwordadmission/PasswordAdmission.php b/lib/admissionrules/passwordadmission/PasswordAdmission.php
new file mode 100644
index 0000000..0650630
--- /dev/null
+++ b/lib/admissionrules/passwordadmission/PasswordAdmission.php
@@ -0,0 +1,241 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+require_once 'vendor/phpass/PasswordHash.php';
+
+class PasswordAdmission extends AdmissionRule
+{
+ // --- ATTRIBUTES ---
+ /*
+ * Password hasher (phpass library)
+ */
+ public $hasher = null;
+
+ /*
+ * Crypted password.
+ */
+ public $password = '';
+
+ // --- OPERATIONS ---
+
+ public $new = false;
+ /**
+ * Standard constructor.
+ *
+ * @param String ruleId
+ */
+ public function __construct($ruleId='', $courseSetId = '')
+ {
+ parent::__construct($ruleId, $courseSetId);
+ $this->default_message = _('Das eingegebene Passwort ist falsch.');
+ // Create a new bcrypt password hasher (exclude weaker algorithms).
+ $this->hasher = new PasswordHash(8, false);
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('passwordadmissions');
+ $this->new = true;
+ }
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete() {
+ parent::delete();
+ // Delete rule data.
+ $stmt = DBManager::get()->prepare("DELETE FROM `passwordadmissions`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription() {
+ return _("Mit dieser Anmelderegel können Sie ein Passwort für Zugang ".
+ "zu den zugeordneten Veranstaltungen vergeben. Die Anmeldung ist ".
+ "dann nur für Personen möglich, die dieses Passwort kennen.");
+ }
+
+ /**
+ * Shows an input form where the user can enter a password and try to get
+ * past the holy gates.
+ *
+ * @return String A template-based input form.
+ */
+ public function getInput() {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ $tpl = $factory->open('input');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName() {
+ return _("Anmeldung mit Passwort");
+ }
+
+ /**
+ * Gets the bcrypted hash of the current password.
+ *
+ * @return String
+ */
+ public function getPassword() {
+ return $this->password;
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ */
+ public function getTemplate() {
+ // Open generic admission rule template.
+ $tpl = $GLOBALS['template_factory']->open('admission/rules/configure');
+ $tpl->set_attribute('rule', $this);
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ // Now open specific template for this rule and insert base template.
+ $tpl2 = $factory->open('configure');
+ $tpl2->set_attribute('rule', $this);
+ $tpl2->set_attribute('tpl', $tpl->render());
+ return $tpl2->render();
+ }
+
+ /**
+ * Internal helper function for loading rule definition from database.
+ */
+ public function load() {
+ $stmt = DBManager::get()->prepare("SELECT * FROM `passwordadmissions`
+ WHERE `rule_id`=? LIMIT 1");
+ $stmt->execute([$this->id]);
+ if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $this->message = $current['message'];
+ $this->startTime = $current['start_time'];
+ $this->endTime = $current['end_time'];
+ $this->password = $current['password'];
+ }
+ }
+
+ /**
+ * Does the current rule allow the given user to register as participant
+ * in the given course? Here, a given password (via the getInput method) is
+ * compared to the stored encrypted one.
+ *
+ * @param String userId
+ * @param String courseId
+ * @return array
+ */
+ public function ruleApplies($userId, $courseId)
+ {
+ $errors = [];
+ if ($this->checkTimeFrame()) {
+ if (Request::get('pwarule_password') === null) {
+ $errors[] = _('Die Eingabe eines Passwortes ist erforderlich.');
+ } else {
+ CSRFProtection::verifyUnsafeRequest();
+ $pwcheck = $this->hasher->CheckPassword(Request::get('pwarule_password'),
+ $this->getPassword());
+ //migrated passwords
+ $pwcheck_m = $this->hasher->CheckPassword(md5(Request::get('pwarule_password')),
+ $this->getPassword());
+ if (!($pwcheck || $pwcheck_m)) {
+ $errors[] = $this->getMessage();
+ }
+ }
+ }
+ return $errors;
+ }
+
+ /**
+ * Uses the given data to fill the object values. This can be used
+ * as a generic function for storing data if the concrete rule type
+ * isn't known in advance.
+ *
+ * @param Array $data
+ * @return AdmissionRule This object.
+ */
+ public function setAllData($data) {
+ parent::setAllData($data);
+ if ($this->new || $data['password1'] !== '') {
+ $this->setPassword($data['password1']);
+ }
+ return $this;
+ }
+
+ /**
+ * Sets the password by bcrypting the given clear text password.
+ *
+ * @param String $clearText The clear text password to be set.
+ * @return PasswordAdmission
+ */
+ public function setPassword($clearText) {
+ $this->password = $this->hasher->HashPassword($clearText);
+ return $this;
+ }
+
+ /**
+ * Helper function for storing data to DB.
+ */
+ public function store() {
+ // Store data.
+ $stmt = DBManager::get()->prepare("INSERT INTO `passwordadmissions`
+ (`rule_id`, `message`, `start_time`, `end_time`, `password`,
+ `mkdate`, `chdate`)
+ VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
+ `message`=VALUES(`message`), `start_time`=VALUES(`start_time`),
+ `end_time`=VALUES(`end_time`), `password`=VALUES(`password`),
+ `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, $this->message, (int)$this->startTime, (int)$this->endTime, $this->password,
+ time(), time()]);
+ return $this;
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ */
+ public function toString() {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ $tpl = $factory->open('info');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Validates if the given request data is sufficient to configure this rule
+ * (e.g. if required values are present).
+ *
+ * @param Array Request data
+ * @return Array Error messages.
+ */
+ public function validate($data)
+ {
+ $errors = parent::validate($data);
+ if ($data['password1'] === '' && $this->new) {
+ $errors[] = _('Das Passwort darf nicht leer sein.');
+ }
+ if ($data['password1'] !== $data['password2']) {
+ $errors[] = _('Das Passwort stimmt nicht mit der Wiederholung überein.');
+ }
+ return $errors;
+ }
+}
diff --git a/lib/admissionrules/preferentialadmission/PreferentialAdmission.class.php b/lib/admissionrules/preferentialadmission/PreferentialAdmission.class.php
deleted file mode 100644
index bee3e19..0000000
--- a/lib/admissionrules/preferentialadmission/PreferentialAdmission.class.php
+++ /dev/null
@@ -1,540 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-require_once('lib/classes/admission/AdmissionRule.class.php');
-require_once('lib/classes/admission/UserFilter.class.php');
-
-class PreferentialAdmission extends AdmissionRule
-{
- // --- ATTRIBUTES ---
-
- /**
- * Stores IDs of userlists generated for representing the selected
- * conditions. These lists are created on seat distribution in the course
- * set and are deleted immediately after.
- */
- public $userlists = [];
-
- /**
- * Conditions for selecting the favored people in seat distribution.
- */
- public $conditions = [];
-
- /**
- * Should higher semesters of study be favored?
- */
- public $favorSemester = false;
-
- /**
- * If semesters are favored, which bonus difference shall be set between
- * each semester of study?
- */
- public $bonus_difference = 100;
-
- /**
- * The courseset this rule belongs to.
- */
- public $courseset = null;
-
- // --- OPERATIONS ---
-
- /**
- * Standard constructor.
- *
- * @param String ruleId If this rule has been saved previously, it
- * will be loaded from database.
- * @return AdmissionRule the current object (this).
- */
- public function __construct($ruleId='')
- {
- $this->id = $ruleId;
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('prefadmissions');
- }
- }
-
- /**
- * Adds a new UserFilter to this rule.
- *
- * @param UserFilter condition
- * @return PreferentialAdmission
- */
- public function addCondition($condition)
- {
- $this->conditions[$condition->getId()] = $condition;
- return $this;
- }
-
- /**
- * Hook that can be called after the seat distribution on the courseset
- * has completed. User lists that were generated before are removed now.
- */
- public function afterSeatDistribution($courseset)
- {
- foreach ($this->userlists as $id) {
- $current = new AdmissionUserList($id);
- $courseset->removeUserList($id);
- $current->delete();
- }
- }
-
- /**
- * Hook that can be called when the seat distribution on the courseset
- * starts. This type of admission rule gets all users that fulfill the
- * specified conditions and generates user lists with modified chances
- * in seat distribution.
- *
- * @param CourseSet The courseset this rule belongs to.
- */
- public function beforeSeatDistribution($courseset)
- {
- $this->courseset = $courseset;
- /*
- * First, we need to calculate the maximum of persons applying
- * for a single course as that number will influence the numbers
- * to set for preferation.
- */
- $this->bonus_difference = DBManager::get()->fetchColumn("SELECT MAX(users) FROM (
- SELECT `priority`, COUNT(DISTINCT `user_id`) AS users
- FROM `priorities`
- WHERE `set_id` = ?
- GROUP BY `priority`
- ) t", [$courseset->getId()]);
- $users = $this->getAffectedUsers();
-
- // No study semester variation, just put all users together.
- if (!$this->favorSemester) {
-
- $userlist = new AdmissionUserList();
- $userlist->setUsers($users)->setFactor($this->bonus_difference + 1)->store();
- $this->userlists[] = $userlist->getId();
- $courseset->addUserList($userlist->getId());
-
- // Study semesters need to be considered for differentiation...
- } else {
- /*
- * Build data grouped by semester of study for users affected
- * by given conditions.
- */
- if ($this->conditions) {
- $grouped = $this->getSemesterGroups($users, true);
-
- /*
- * Build data grouped by semester of study for all users
- * (excluding all users affected by given conditions).
- */
- $rest = $this->getSemesterGroups(
- array_keys(AdmissionPriority::getPriorities($courseset->getId())),
- false, $users);
-
- /*
- * Now set bonus factors to higher semesters. We are processing
- * users not affected by conditions first so that we get the
- * maximum bonus these users get and can build on top of that
- * for users affected by conditions.
- */
- $maxbonus = $this->setSemesterBonus($courseset, $rest);
-
- /*
- * Finally, set bonuses for the users affected by conditions.
- */
- $endbonus = $this->setSemesterBonus($courseset, $grouped, $maxbonus + 1);
- /*
- * No conditions given, just group all users
- * by their semester of study.
- */
- } else {
- // Build list of users by semester of study.
- $grouped = $this->getSemesterGroups(
- array_keys(AdmissionPriority::getPriorities($courseset->getId())),
- false);
-
- // Assign corresponding bonus to users.
- $maxbonus = $this->setSemesterBonus($courseset, $grouped);
- }
- }
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete()
- {
- parent::delete();
- // Delete rule data.
- $stmt = DBManager::get()->prepare("DELETE FROM `prefadmissions`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- // Delete all associated conditions...
- foreach ($this->conditions as $condition) {
- $condition->delete();
- }
- // ... and their connection to this rule.
- $stmt = DBManager::get()->prepare("DELETE FROM `prefadmission_condition`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- }
-
- /**
- * Gets all users that are matched by thís rule.
- *
- * @return Array An array containing IDs of users who are matched by
- * this rule.
- */
- public function getAffectedUsers()
- {
- $users = [];
- if ($this->conditions) {
- // Get users from all specified conditions.
- foreach ($this->conditions as $condition) {
- $users = array_unique(array_merge($users, $condition->getUsers()));
- }
- } else {
- $users = array_keys(AdmissionPriority::getPriorities($this->courseset->getId()));
- }
- return $users;
- }
-
- /**
- * Gets all defined conditions.
- *
- * @return Array
- */
- public function getConditions()
- {
- return $this->conditions;
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription()
- {
- return _('Sie können hier festlegen, dass bestimmte Studiengänge, '.
- 'Fachsemester etc. bei der Platzverteilung zu Veranstaltungen '.
- 'bevorzugt behandelt werden sollen.');
- }
-
- /**
- * Returns whether higher semesters of study should be favored.
- *
- * @return bool
- */
- public function getFavorSemester()
- {
- return $this->favorSemester;
- }
-
- /**
- * Return this rule's name.
- */
- public static function getName()
- {
- return _('Bevorzugte Anmeldung');
- }
-
- /**
- * Gets the semesters of study for the given users. If conditions are
- * set and should be considered, only the semesters of study belonging
- * to the given conditions are set.
- *
- * @param $users user IDs to process
- * @param $considerConditions should only the semesters of study belonging
- * to given conditions be considered?
- * @param array $exclude user IDs to exclude
- * @return array Users with their maximal semester of study.
- */
- public function getSemesterGroups($users, $considerConditions, $exclude = [])
- {
- /*
- * Get all selected condition values so that the study semester
- * can be matched against that data; we don't want some "general"
- * value for a user's study semester, but the one that is assigned
- * to a given subject and degree.
- */
- $queryParts = [];
- $values = [$users];
- if ($exclude) {
- $values[] = $exclude;
- }
- if ($considerConditions) {
- foreach ($this->conditions as $condition) {
- $queryPart = "";
- // Search for subject and degree entries.
- foreach ($condition->getFields() as $field) {
- switch (get_class($field)) {
- case 'DegreeCondition':
- if ($queryPart) {
- $queryPart .= " AND ";
- }
- $queryPart .= "`abschluss_id`".$field->getCompareOperator()."?";
- $values[] = $field->getValue() ?: '';
- break;
- case 'SubjectCondition':
- if ($queryPart) {
- $queryPart .= " AND ";
- }
- $queryPart .= "`fach_id`".$field->getCompareOperator()."?";
- $values[] = $field->getValue() ?: '';
- break;
- case 'SemesterOfStudyCondition':
- if ($queryPart) {
- $queryPart .= " AND ";
- }
- $queryPart .= "`semester`".$field->getCompareOperator()."?";
- $values[] = $field->getValue() ?: '';
- break;
- default:
- break;
- }
- }
- if ($queryPart) {
- $queryParts[] = $queryPart;
- }
- }
- }
- // Build SQL query with affected users and selected subjects and degrees.
- $query = "SELECT `user_id`, MAX(`semester`) AS semester
- FROM `user_studiengang`
- WHERE `user_id` IN (?)";
- if ($exclude) {
- $query .= " AND `user_id` NOT IN (?)";
- }
- if ($queryParts) {
- $query .= " AND ((".implode(") OR (", $queryParts)."))";
- }
- $query .= " GROUP BY `user_id` ORDER BY `semester`, `user_id`";
- $groups = [];
- foreach (DBManager::get()->fetchAll($query, $values) as $entry) {
- if (intval($entry['semester'])) {
- $groups[intval($entry['semester'])][] = $entry['user_id'];
- }
- }
- ksort($groups);
- return $groups;
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- */
- public function getTemplate()
- {
- $factory = new Flexi\Factory(__DIR__.'/templates/');
- // Now open specific template for this rule and insert base template.
- $tpl = $factory->open('configure');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Helper function for loading data from DB. Generic AdmissionRule data is
- * loaded with the parent load() method.
- */
- public function load()
- {
- // Load basic data.
- $stmt = DBManager::get()->prepare("SELECT *
- FROM `prefadmissions` WHERE `rule_id`=? LIMIT 1");
- $stmt->execute([$this->id]);
- if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
- $this->favorSemester = $current['favor_semester'];
- // Retrieve conditions.
- $stmt = DBManager::get()->prepare("SELECT *
- FROM `prefadmission_condition` WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- $conditions = $stmt->fetchAll(PDO::FETCH_ASSOC);
- foreach ($conditions as $condition) {
- $currentCondition = new UserFilter($condition['condition_id']);
- $this->conditions[$condition['condition_id']] = $currentCondition;
- }
- }
- }
-
- /**
- * Removes the condition with the given ID from the rule.
- *
- * @param String conditionId
- * @return PreferentialAdmission
- */
- public function removeCondition($conditionId)
- {
- $this->conditions[$conditionId]->delete();
- unset($this->conditions[$conditionId]);
- return $this;
- }
-
- /**
- * Admission is open for everyone. On seat distribution, the rule conditions
- * will be used to generate user lists with the specified chance.
- *
- * @param String $userId
- * @param String $courseId
- * @return Array Is the user allowed to register or are there any errors?
- */
- public function ruleApplies($userId, $courseId)
- {
- return [];
- }
-
- /**
- * Uses the given data to fill the object values. This can be used
- * as a generic function for storing data if the concrete rule type
- * isn't known in advance.
- *
- * @param Array $data
- * @return AdmissionRule This object.
- */
- public function setAllData($data)
- {
- UserFilterField::getAvailableFilterFields();
- parent::setAllData($data);
- $this->favorSemester = (bool) $data['favor_semester'];
- $this->conditions = [];
- if ($data['conditions']) {
- foreach ($data['conditions'] as $condition) {
- $this->addCondition(ObjectBuilder::build($condition, 'UserFilter'));
- }
- }
- return $this;
- }
-
- /**
- * New setting for favoring higher semesters of study.
- *
- * @param bool $newFavorSemester
- * @return PreferentialAdmission
- */
- public function setFavorSemester($newFavorSemester) {
- $this->favorSemester = $newFavorSemester;
- return $this;
- }
-
- /**
- * Create user lists and set bonus corresponding to
- * the maximal available semester of study for given users.
- *
- * @param $courseset CourseSet to add user lists to
- * @param $grouped associative array of users in the form
- * => array(, $members) {
- $userlist = new AdmissionUserList();
- $userlist->setUsers($members);
- $userlist->setFactor($bonus);
- $userlist->store();
- $bonus = $bonus + ($this->bonus_difference + 1);
- $courseset->addUserList($userlist->getId());
- $this->userlists[] = $userlist->getId();
- }
- return $bonus;
- }
-
- /**
- * Helper function for storing data to DB.
- */
- public function store()
- {
- // Store rule data.
- $stmt = DBManager::get()->prepare("INSERT INTO `prefadmissions`
- (`rule_id`, `favor_semester`, `mkdate`, `chdate`)
- VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE
- `favor_semester`=VALUES(`favor_semester`),
- `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, $this->favorSemester, time(), time()]);
- // Delete removed conditions from DB.
- $entries = DBManager::get()->fetchAll("SELECT `condition_id` FROM
- `prefadmission_condition` WHERE `rule_id`=? AND `condition_id` NOT IN (?)",
- [$this->id, array_keys($this->conditions)]);
- foreach ($entries as $entry) {
- $current = new UserFilter($entry['condition_id']);
- $current->delete();
- }
- DBManager::get()->execute("DELETE FROM `prefadmission_condition`
- WHERE `rule_id`=? AND `condition_id` NOT IN (?)", [$this->id, array_keys($this->conditions)]);
- // Store all conditions.
- $queries = [];
- $parameters = [];
- if ($this->conditions) {
- foreach ($this->conditions as $condition) {
- // Store each condition...
- $condition->store();
- $queries[] = "(?, ?, ?)";
- $parameters[] = $this->id;
- $parameters[] = $condition->getId();
- $parameters[] = time();
- }
- // Store all assignments between rule and condition.
- $stmt = DBManager::get()->execute("INSERT INTO `prefadmission_condition`
- (`rule_id`, `condition_id`, `mkdate`)
- VALUES ".implode(",", $queries)." ON DUPLICATE KEY UPDATE
- `condition_id`=VALUES(`condition_id`)", $parameters);
- }
- return $this;
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- */
- public function toString()
- {
- $factory = new Flexi\Factory(__DIR__.'/templates/');
- $tpl = $factory->open('info');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Validates if the given request data is sufficient to configure this rule
- * (e.g. if required values are present).
- *
- * @param Array Request data
- * @return Array Error messages.
- */
- public function validate($data)
- {
- $errors = parent::validate($data);
- if (!$data['conditions'] && !$data['favor_semester']) {
- $errors[] = _('Es muss mindestens eine Auswahlbedingung angegeben werden.');
- }
- return $errors;
- }
-
- public function __clone()
- {
- $this->id = md5(uniqid(get_class($this)));
- $this->courseSetId = null;
- $cloned_conditions = [];
- foreach ($this->conditions as $condition) {
- $dolly = clone $condition;
- $cloned_conditions[$dolly->id] = $dolly;
- }
- $this->conditions = $cloned_conditions;
- }
-
-} /* end of class PreferentialAdmission */
diff --git a/lib/admissionrules/preferentialadmission/PreferentialAdmission.php b/lib/admissionrules/preferentialadmission/PreferentialAdmission.php
new file mode 100644
index 0000000..bee3e19
--- /dev/null
+++ b/lib/admissionrules/preferentialadmission/PreferentialAdmission.php
@@ -0,0 +1,540 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+require_once('lib/classes/admission/AdmissionRule.class.php');
+require_once('lib/classes/admission/UserFilter.class.php');
+
+class PreferentialAdmission extends AdmissionRule
+{
+ // --- ATTRIBUTES ---
+
+ /**
+ * Stores IDs of userlists generated for representing the selected
+ * conditions. These lists are created on seat distribution in the course
+ * set and are deleted immediately after.
+ */
+ public $userlists = [];
+
+ /**
+ * Conditions for selecting the favored people in seat distribution.
+ */
+ public $conditions = [];
+
+ /**
+ * Should higher semesters of study be favored?
+ */
+ public $favorSemester = false;
+
+ /**
+ * If semesters are favored, which bonus difference shall be set between
+ * each semester of study?
+ */
+ public $bonus_difference = 100;
+
+ /**
+ * The courseset this rule belongs to.
+ */
+ public $courseset = null;
+
+ // --- OPERATIONS ---
+
+ /**
+ * Standard constructor.
+ *
+ * @param String ruleId If this rule has been saved previously, it
+ * will be loaded from database.
+ * @return AdmissionRule the current object (this).
+ */
+ public function __construct($ruleId='')
+ {
+ $this->id = $ruleId;
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('prefadmissions');
+ }
+ }
+
+ /**
+ * Adds a new UserFilter to this rule.
+ *
+ * @param UserFilter condition
+ * @return PreferentialAdmission
+ */
+ public function addCondition($condition)
+ {
+ $this->conditions[$condition->getId()] = $condition;
+ return $this;
+ }
+
+ /**
+ * Hook that can be called after the seat distribution on the courseset
+ * has completed. User lists that were generated before are removed now.
+ */
+ public function afterSeatDistribution($courseset)
+ {
+ foreach ($this->userlists as $id) {
+ $current = new AdmissionUserList($id);
+ $courseset->removeUserList($id);
+ $current->delete();
+ }
+ }
+
+ /**
+ * Hook that can be called when the seat distribution on the courseset
+ * starts. This type of admission rule gets all users that fulfill the
+ * specified conditions and generates user lists with modified chances
+ * in seat distribution.
+ *
+ * @param CourseSet The courseset this rule belongs to.
+ */
+ public function beforeSeatDistribution($courseset)
+ {
+ $this->courseset = $courseset;
+ /*
+ * First, we need to calculate the maximum of persons applying
+ * for a single course as that number will influence the numbers
+ * to set for preferation.
+ */
+ $this->bonus_difference = DBManager::get()->fetchColumn("SELECT MAX(users) FROM (
+ SELECT `priority`, COUNT(DISTINCT `user_id`) AS users
+ FROM `priorities`
+ WHERE `set_id` = ?
+ GROUP BY `priority`
+ ) t", [$courseset->getId()]);
+ $users = $this->getAffectedUsers();
+
+ // No study semester variation, just put all users together.
+ if (!$this->favorSemester) {
+
+ $userlist = new AdmissionUserList();
+ $userlist->setUsers($users)->setFactor($this->bonus_difference + 1)->store();
+ $this->userlists[] = $userlist->getId();
+ $courseset->addUserList($userlist->getId());
+
+ // Study semesters need to be considered for differentiation...
+ } else {
+ /*
+ * Build data grouped by semester of study for users affected
+ * by given conditions.
+ */
+ if ($this->conditions) {
+ $grouped = $this->getSemesterGroups($users, true);
+
+ /*
+ * Build data grouped by semester of study for all users
+ * (excluding all users affected by given conditions).
+ */
+ $rest = $this->getSemesterGroups(
+ array_keys(AdmissionPriority::getPriorities($courseset->getId())),
+ false, $users);
+
+ /*
+ * Now set bonus factors to higher semesters. We are processing
+ * users not affected by conditions first so that we get the
+ * maximum bonus these users get and can build on top of that
+ * for users affected by conditions.
+ */
+ $maxbonus = $this->setSemesterBonus($courseset, $rest);
+
+ /*
+ * Finally, set bonuses for the users affected by conditions.
+ */
+ $endbonus = $this->setSemesterBonus($courseset, $grouped, $maxbonus + 1);
+ /*
+ * No conditions given, just group all users
+ * by their semester of study.
+ */
+ } else {
+ // Build list of users by semester of study.
+ $grouped = $this->getSemesterGroups(
+ array_keys(AdmissionPriority::getPriorities($courseset->getId())),
+ false);
+
+ // Assign corresponding bonus to users.
+ $maxbonus = $this->setSemesterBonus($courseset, $grouped);
+ }
+ }
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete()
+ {
+ parent::delete();
+ // Delete rule data.
+ $stmt = DBManager::get()->prepare("DELETE FROM `prefadmissions`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ // Delete all associated conditions...
+ foreach ($this->conditions as $condition) {
+ $condition->delete();
+ }
+ // ... and their connection to this rule.
+ $stmt = DBManager::get()->prepare("DELETE FROM `prefadmission_condition`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ }
+
+ /**
+ * Gets all users that are matched by thís rule.
+ *
+ * @return Array An array containing IDs of users who are matched by
+ * this rule.
+ */
+ public function getAffectedUsers()
+ {
+ $users = [];
+ if ($this->conditions) {
+ // Get users from all specified conditions.
+ foreach ($this->conditions as $condition) {
+ $users = array_unique(array_merge($users, $condition->getUsers()));
+ }
+ } else {
+ $users = array_keys(AdmissionPriority::getPriorities($this->courseset->getId()));
+ }
+ return $users;
+ }
+
+ /**
+ * Gets all defined conditions.
+ *
+ * @return Array
+ */
+ public function getConditions()
+ {
+ return $this->conditions;
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription()
+ {
+ return _('Sie können hier festlegen, dass bestimmte Studiengänge, '.
+ 'Fachsemester etc. bei der Platzverteilung zu Veranstaltungen '.
+ 'bevorzugt behandelt werden sollen.');
+ }
+
+ /**
+ * Returns whether higher semesters of study should be favored.
+ *
+ * @return bool
+ */
+ public function getFavorSemester()
+ {
+ return $this->favorSemester;
+ }
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName()
+ {
+ return _('Bevorzugte Anmeldung');
+ }
+
+ /**
+ * Gets the semesters of study for the given users. If conditions are
+ * set and should be considered, only the semesters of study belonging
+ * to the given conditions are set.
+ *
+ * @param $users user IDs to process
+ * @param $considerConditions should only the semesters of study belonging
+ * to given conditions be considered?
+ * @param array $exclude user IDs to exclude
+ * @return array Users with their maximal semester of study.
+ */
+ public function getSemesterGroups($users, $considerConditions, $exclude = [])
+ {
+ /*
+ * Get all selected condition values so that the study semester
+ * can be matched against that data; we don't want some "general"
+ * value for a user's study semester, but the one that is assigned
+ * to a given subject and degree.
+ */
+ $queryParts = [];
+ $values = [$users];
+ if ($exclude) {
+ $values[] = $exclude;
+ }
+ if ($considerConditions) {
+ foreach ($this->conditions as $condition) {
+ $queryPart = "";
+ // Search for subject and degree entries.
+ foreach ($condition->getFields() as $field) {
+ switch (get_class($field)) {
+ case 'DegreeCondition':
+ if ($queryPart) {
+ $queryPart .= " AND ";
+ }
+ $queryPart .= "`abschluss_id`".$field->getCompareOperator()."?";
+ $values[] = $field->getValue() ?: '';
+ break;
+ case 'SubjectCondition':
+ if ($queryPart) {
+ $queryPart .= " AND ";
+ }
+ $queryPart .= "`fach_id`".$field->getCompareOperator()."?";
+ $values[] = $field->getValue() ?: '';
+ break;
+ case 'SemesterOfStudyCondition':
+ if ($queryPart) {
+ $queryPart .= " AND ";
+ }
+ $queryPart .= "`semester`".$field->getCompareOperator()."?";
+ $values[] = $field->getValue() ?: '';
+ break;
+ default:
+ break;
+ }
+ }
+ if ($queryPart) {
+ $queryParts[] = $queryPart;
+ }
+ }
+ }
+ // Build SQL query with affected users and selected subjects and degrees.
+ $query = "SELECT `user_id`, MAX(`semester`) AS semester
+ FROM `user_studiengang`
+ WHERE `user_id` IN (?)";
+ if ($exclude) {
+ $query .= " AND `user_id` NOT IN (?)";
+ }
+ if ($queryParts) {
+ $query .= " AND ((".implode(") OR (", $queryParts)."))";
+ }
+ $query .= " GROUP BY `user_id` ORDER BY `semester`, `user_id`";
+ $groups = [];
+ foreach (DBManager::get()->fetchAll($query, $values) as $entry) {
+ if (intval($entry['semester'])) {
+ $groups[intval($entry['semester'])][] = $entry['user_id'];
+ }
+ }
+ ksort($groups);
+ return $groups;
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ */
+ public function getTemplate()
+ {
+ $factory = new Flexi\Factory(__DIR__.'/templates/');
+ // Now open specific template for this rule and insert base template.
+ $tpl = $factory->open('configure');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Helper function for loading data from DB. Generic AdmissionRule data is
+ * loaded with the parent load() method.
+ */
+ public function load()
+ {
+ // Load basic data.
+ $stmt = DBManager::get()->prepare("SELECT *
+ FROM `prefadmissions` WHERE `rule_id`=? LIMIT 1");
+ $stmt->execute([$this->id]);
+ if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $this->favorSemester = $current['favor_semester'];
+ // Retrieve conditions.
+ $stmt = DBManager::get()->prepare("SELECT *
+ FROM `prefadmission_condition` WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ $conditions = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($conditions as $condition) {
+ $currentCondition = new UserFilter($condition['condition_id']);
+ $this->conditions[$condition['condition_id']] = $currentCondition;
+ }
+ }
+ }
+
+ /**
+ * Removes the condition with the given ID from the rule.
+ *
+ * @param String conditionId
+ * @return PreferentialAdmission
+ */
+ public function removeCondition($conditionId)
+ {
+ $this->conditions[$conditionId]->delete();
+ unset($this->conditions[$conditionId]);
+ return $this;
+ }
+
+ /**
+ * Admission is open for everyone. On seat distribution, the rule conditions
+ * will be used to generate user lists with the specified chance.
+ *
+ * @param String $userId
+ * @param String $courseId
+ * @return Array Is the user allowed to register or are there any errors?
+ */
+ public function ruleApplies($userId, $courseId)
+ {
+ return [];
+ }
+
+ /**
+ * Uses the given data to fill the object values. This can be used
+ * as a generic function for storing data if the concrete rule type
+ * isn't known in advance.
+ *
+ * @param Array $data
+ * @return AdmissionRule This object.
+ */
+ public function setAllData($data)
+ {
+ UserFilterField::getAvailableFilterFields();
+ parent::setAllData($data);
+ $this->favorSemester = (bool) $data['favor_semester'];
+ $this->conditions = [];
+ if ($data['conditions']) {
+ foreach ($data['conditions'] as $condition) {
+ $this->addCondition(ObjectBuilder::build($condition, 'UserFilter'));
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * New setting for favoring higher semesters of study.
+ *
+ * @param bool $newFavorSemester
+ * @return PreferentialAdmission
+ */
+ public function setFavorSemester($newFavorSemester) {
+ $this->favorSemester = $newFavorSemester;
+ return $this;
+ }
+
+ /**
+ * Create user lists and set bonus corresponding to
+ * the maximal available semester of study for given users.
+ *
+ * @param $courseset CourseSet to add user lists to
+ * @param $grouped associative array of users in the form
+ * => array(, $members) {
+ $userlist = new AdmissionUserList();
+ $userlist->setUsers($members);
+ $userlist->setFactor($bonus);
+ $userlist->store();
+ $bonus = $bonus + ($this->bonus_difference + 1);
+ $courseset->addUserList($userlist->getId());
+ $this->userlists[] = $userlist->getId();
+ }
+ return $bonus;
+ }
+
+ /**
+ * Helper function for storing data to DB.
+ */
+ public function store()
+ {
+ // Store rule data.
+ $stmt = DBManager::get()->prepare("INSERT INTO `prefadmissions`
+ (`rule_id`, `favor_semester`, `mkdate`, `chdate`)
+ VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE
+ `favor_semester`=VALUES(`favor_semester`),
+ `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, $this->favorSemester, time(), time()]);
+ // Delete removed conditions from DB.
+ $entries = DBManager::get()->fetchAll("SELECT `condition_id` FROM
+ `prefadmission_condition` WHERE `rule_id`=? AND `condition_id` NOT IN (?)",
+ [$this->id, array_keys($this->conditions)]);
+ foreach ($entries as $entry) {
+ $current = new UserFilter($entry['condition_id']);
+ $current->delete();
+ }
+ DBManager::get()->execute("DELETE FROM `prefadmission_condition`
+ WHERE `rule_id`=? AND `condition_id` NOT IN (?)", [$this->id, array_keys($this->conditions)]);
+ // Store all conditions.
+ $queries = [];
+ $parameters = [];
+ if ($this->conditions) {
+ foreach ($this->conditions as $condition) {
+ // Store each condition...
+ $condition->store();
+ $queries[] = "(?, ?, ?)";
+ $parameters[] = $this->id;
+ $parameters[] = $condition->getId();
+ $parameters[] = time();
+ }
+ // Store all assignments between rule and condition.
+ $stmt = DBManager::get()->execute("INSERT INTO `prefadmission_condition`
+ (`rule_id`, `condition_id`, `mkdate`)
+ VALUES ".implode(",", $queries)." ON DUPLICATE KEY UPDATE
+ `condition_id`=VALUES(`condition_id`)", $parameters);
+ }
+ return $this;
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ */
+ public function toString()
+ {
+ $factory = new Flexi\Factory(__DIR__.'/templates/');
+ $tpl = $factory->open('info');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Validates if the given request data is sufficient to configure this rule
+ * (e.g. if required values are present).
+ *
+ * @param Array Request data
+ * @return Array Error messages.
+ */
+ public function validate($data)
+ {
+ $errors = parent::validate($data);
+ if (!$data['conditions'] && !$data['favor_semester']) {
+ $errors[] = _('Es muss mindestens eine Auswahlbedingung angegeben werden.');
+ }
+ return $errors;
+ }
+
+ public function __clone()
+ {
+ $this->id = md5(uniqid(get_class($this)));
+ $this->courseSetId = null;
+ $cloned_conditions = [];
+ foreach ($this->conditions as $condition) {
+ $dolly = clone $condition;
+ $cloned_conditions[$dolly->id] = $dolly;
+ }
+ $this->conditions = $cloned_conditions;
+ }
+
+} /* end of class PreferentialAdmission */
diff --git a/lib/admissionrules/termsadmission/TermsAdmission.class.php b/lib/admissionrules/termsadmission/TermsAdmission.class.php
deleted file mode 100644
index 50c8712..0000000
--- a/lib/admissionrules/termsadmission/TermsAdmission.class.php
+++ /dev/null
@@ -1,171 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-require_once 'lib/classes/admission/AdmissionRule.class.php';
-
-class TermsAdmission extends AdmissionRule
-{
- // Terms of admission
- public $terms;
-
- /**
- * Standard constructor.
- *
- * @param String ruleId
- */
- public function __construct($ruleId = '', $courseSetId = '')
- {
- parent::__construct($ruleId, $courseSetId);
-
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('termsadmissions');
- }
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete()
- {
- parent::delete();
-
- $stmt = DBManager::get()->prepare('DELETE FROM termsadmissions WHERE rule_id = ?');
- $stmt->execute([$this->getId()]);
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription()
- {
- return _('Mit dieser Anmelderegel können Sie einen Kurs mit spezifischen Teilnahmebedingungen realisieren. '
- . 'Die Anmeldung ist erst möglich, nachdem diese akzeptiert wurden.');
- }
-
- /**
- * Shows an input form
- *
- * @return string A template-based input form.
- * @throws Flexi\TemplateNotFoundException
- */
- public function getInput()
- {
- $factory = new Flexi\Factory(__DIR__ . '/templates');
- $template = $factory->open('input');
- $template->rule = $this;
-
- return (string) MessageBox::info($template->render())->hideClose();
- }
-
- /**
- * Return this rule's name.
- */
- public static function getName() {
- return _('Kurs mit Teilnahmebedingungen');
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- * @throws Flexi\TemplateNotFoundException
- */
- public function getTemplate()
- {
- $factory = new Flexi\Factory(__DIR__ . '/templates');
- $template = $factory->open('configure');
- $template->rule = $this;
-
- return $template->render();
- }
-
- /**
- * Does the current rule allow the given user to register as participant
- * in the given course?
- *
- * @param String userId
- * @param String courseId
- * @return Array
- */
- public function ruleApplies($userId, $courseId)
- {
- $errors = [];
-
- // check if the user has accepted the terms
- if (Request::int('terms_accepted')) {
- $_SESSION['terms_accepted_' . $this->getId()] = true;
- }
- if (!$_SESSION['terms_accepted_' . $this->getId()]) {
- $errors[] = _('Um sich anzumelden, müssen Sie die Teilnahmebedingungen akzeptieren.');
- }
-
- return $errors;
- }
-
- /**
- * Uses the given data to fill the object values. This can be used
- * as a generic function for storing data if the concrete rule type
- * isn't known in advance.
- *
- * @param Array $data
- * @return AdmissionRule This object.
- */
- public function setAllData($data)
- {
- parent::setAllData($data);
- $this->terms = trim($data['terms']);
- return $this;
- }
-
- /**
- * Internal helper function for loading rule definition from database.
- */
- public function load()
- {
- $rule = DBManager::get()->fetchOne('SELECT * FROM termsadmissions WHERE rule_id = ?', [$this->getId()]);
- $this->terms = $rule['terms'];
- return $this;
- }
-
- /**
- * Store rule definition to database.
- */
- public function store()
- {
- // Store data.
- $stmt = DBManager::get()->prepare('INSERT INTO termsadmissions (rule_id, terms, mkdate, chdate) VALUES (?, ?, ?, ?)
- ON DUPLICATE KEY UPDATE terms = VALUES(terms), chdate = VALUES(chdate)');
- $stmt->execute([$this->id, $this->terms, time(), time()]);
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- * @throws Flexi\TemplateNotFoundException
- */
- public function toString()
- {
- $factory = new Flexi\Factory(__DIR__ . '/templates/');
- $template = $factory->open('info');
- $template->rule = $this;
-
- return $template->render();
- }
-}
diff --git a/lib/admissionrules/termsadmission/TermsAdmission.php b/lib/admissionrules/termsadmission/TermsAdmission.php
new file mode 100644
index 0000000..50c8712
--- /dev/null
+++ b/lib/admissionrules/termsadmission/TermsAdmission.php
@@ -0,0 +1,171 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+require_once 'lib/classes/admission/AdmissionRule.class.php';
+
+class TermsAdmission extends AdmissionRule
+{
+ // Terms of admission
+ public $terms;
+
+ /**
+ * Standard constructor.
+ *
+ * @param String ruleId
+ */
+ public function __construct($ruleId = '', $courseSetId = '')
+ {
+ parent::__construct($ruleId, $courseSetId);
+
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('termsadmissions');
+ }
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete()
+ {
+ parent::delete();
+
+ $stmt = DBManager::get()->prepare('DELETE FROM termsadmissions WHERE rule_id = ?');
+ $stmt->execute([$this->getId()]);
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription()
+ {
+ return _('Mit dieser Anmelderegel können Sie einen Kurs mit spezifischen Teilnahmebedingungen realisieren. '
+ . 'Die Anmeldung ist erst möglich, nachdem diese akzeptiert wurden.');
+ }
+
+ /**
+ * Shows an input form
+ *
+ * @return string A template-based input form.
+ * @throws Flexi\TemplateNotFoundException
+ */
+ public function getInput()
+ {
+ $factory = new Flexi\Factory(__DIR__ . '/templates');
+ $template = $factory->open('input');
+ $template->rule = $this;
+
+ return (string) MessageBox::info($template->render())->hideClose();
+ }
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName() {
+ return _('Kurs mit Teilnahmebedingungen');
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ * @throws Flexi\TemplateNotFoundException
+ */
+ public function getTemplate()
+ {
+ $factory = new Flexi\Factory(__DIR__ . '/templates');
+ $template = $factory->open('configure');
+ $template->rule = $this;
+
+ return $template->render();
+ }
+
+ /**
+ * Does the current rule allow the given user to register as participant
+ * in the given course?
+ *
+ * @param String userId
+ * @param String courseId
+ * @return Array
+ */
+ public function ruleApplies($userId, $courseId)
+ {
+ $errors = [];
+
+ // check if the user has accepted the terms
+ if (Request::int('terms_accepted')) {
+ $_SESSION['terms_accepted_' . $this->getId()] = true;
+ }
+ if (!$_SESSION['terms_accepted_' . $this->getId()]) {
+ $errors[] = _('Um sich anzumelden, müssen Sie die Teilnahmebedingungen akzeptieren.');
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Uses the given data to fill the object values. This can be used
+ * as a generic function for storing data if the concrete rule type
+ * isn't known in advance.
+ *
+ * @param Array $data
+ * @return AdmissionRule This object.
+ */
+ public function setAllData($data)
+ {
+ parent::setAllData($data);
+ $this->terms = trim($data['terms']);
+ return $this;
+ }
+
+ /**
+ * Internal helper function for loading rule definition from database.
+ */
+ public function load()
+ {
+ $rule = DBManager::get()->fetchOne('SELECT * FROM termsadmissions WHERE rule_id = ?', [$this->getId()]);
+ $this->terms = $rule['terms'];
+ return $this;
+ }
+
+ /**
+ * Store rule definition to database.
+ */
+ public function store()
+ {
+ // Store data.
+ $stmt = DBManager::get()->prepare('INSERT INTO termsadmissions (rule_id, terms, mkdate, chdate) VALUES (?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE terms = VALUES(terms), chdate = VALUES(chdate)');
+ $stmt->execute([$this->id, $this->terms, time(), time()]);
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ * @throws Flexi\TemplateNotFoundException
+ */
+ public function toString()
+ {
+ $factory = new Flexi\Factory(__DIR__ . '/templates/');
+ $template = $factory->open('info');
+ $template->rule = $this;
+
+ return $template->render();
+ }
+}
diff --git a/lib/admissionrules/timedadmission/TimedAdmission.class.php b/lib/admissionrules/timedadmission/TimedAdmission.class.php
deleted file mode 100644
index e167bd8..0000000
--- a/lib/admissionrules/timedadmission/TimedAdmission.class.php
+++ /dev/null
@@ -1,243 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-class TimedAdmission extends AdmissionRule
-{
- // --- ATTRIBUTES ---
-
- /**
- * End of course admission.
- */
- public $endTime = 0;
-
- /**
- * Start of course admission.
- */
- public $startTime = 0;
-
- // --- OPERATIONS ---
-
- /**
- * Standard constructor
- *
- * @param String ruleId
- */
- public function __construct($ruleId='', $courseSetId = '')
- {
- parent::__construct($ruleId, $courseSetId);
- $this->default_message = _('Sie befinden sich nicht innerhalb des Anmeldezeitraums.');
- if ($ruleId) {
- $this->load();
- } else {
- $this->id = $this->generateId('timedadmissions');
- }
- }
-
- /**
- * Deletes the admission rule and all associated data.
- */
- public function delete() {
- parent::delete();
- // Delete rule data.
- $stmt = DBManager::get()->prepare("DELETE FROM `timedadmissions`
- WHERE `rule_id`=?");
- $stmt->execute([$this->id]);
- }
-
- /**
- * Gets some text that describes what this AdmissionRule (or respective
- * subclass) does.
- */
- public static function getDescription() {
- return _("Anmelderegeln dieses Typs legen ein Zeitfenster fest, in ".
- "dem die Anmeldung zu Veranstaltungen möglich ist. Es kann auch ".
- "nur ein Start- oder Endzeitpunkt angegeben werden.");
- }
-
- /**
- * Gets the end of course admission.
- *
- * @return int
- */
- public function getEndTime()
- {
- return $this->endTime;
- }
-
- /**
- * Return this rule's name.
- */
- public static function getName() {
- return _("Zeitgesteuerte Anmeldung");
- }
-
- /**
- * Gets the start of course admission.
- *
- * @return int
- */
- public function getStartTime()
- {
- return $this->startTime;
- }
-
- /**
- * Gets the template that provides a configuration GUI for this rule.
- *
- * @return String
- */
- public function getTemplate() {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- // Open specific template for this rule and insert base template.
- $tpl = $factory->open('configure');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Helper function for loading rule definition from database.
- */
- public function load() {
- // Load data.
- $stmt = DBManager::get()->prepare("SELECT *
- FROM `timedadmissions` WHERE `rule_id`=? LIMIT 1");
- $stmt->execute([$this->id]);
- if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
- $this->message = $current['message'];
- $this->startTime = $current['start_time'];
- $this->endTime = $current['end_time'];
- }
- }
-
- /**
- * Is admission allowed according to the defined time frame?
- *
- * @param String userId
- * @param String courseId
- * @return Array
- */
- public function ruleApplies($userId, $courseId) {
- $errors = [];
- if (!$this->checkTimeFrame()) {
- $errors[] = $this->getMessage();
- }
- return $errors;
- }
-
- /**
- * Uses the given data to fill the object values. This can be used
- * as a generic function for storing data if the concrete rule type
- * isn't known in advance.
- *
- * @param Array $data
- * @return AdmissionRule This object.
- */
- public function setAllData($data) {
- parent::setAllData($data);
- if ($data['startdate']) {
- $sdate = $data['startdate'];
- $stime = $data['starttime'];
- $parsed = date_parse($sdate.' '.$stime);
- $timestamp = mktime($parsed['hour'], $parsed['minute'], 0, $parsed['month'], $parsed['day'], $parsed['year']);
- $this->setStartTime($timestamp);
- }
- if ($data['enddate']) {
- $edate = $data['enddate'];
- $etime = $data['endtime'];
- if (!$etime) {
- $etime = '23:59';
- }
- $parsed = date_parse($edate.' '.$etime);
- $timestamp = mktime($parsed['hour'], $parsed['minute'], 0, $parsed['month'], $parsed['day'], $parsed['year']);
- $this->setEndTime($timestamp);
- }
- return $this;
- }
-
- /**
- * Sets a new end timestamp for course admission.
- *
- * @param int newEndTime
- * @return TimedAdmission
- */
- public function setEndTime($newEndTime)
- {
- $this->endTime = $newEndTime;
- return $this;
- }
-
- /**
- * Sets a new start timestamp for course admission.
- *
- * @param int newStartTime
- * @return TimedAdmission
- */
- public function setStartTime($newStartTime)
- {
- $this->startTime = $newStartTime;
- return $this;
- }
-
- /**
- * Store rule definition to database.
- */
- public function store() {
- // Store data.
- $stmt = DBManager::get()->prepare("INSERT INTO `timedadmissions`
- (`rule_id`, `message`, `start_time`,
- `end_time`, `mkdate`, `chdate`) VALUES (?, ?, ?, ?, ?, ?)
- ON DUPLICATE KEY UPDATE `start_time`=VALUES(`start_time`),
- `end_time`=VALUES(`end_time`),message=VALUES(message), `chdate`=VALUES(`chdate`)");
- $stmt->execute([$this->id, $this->message, (int)$this->startTime,
- (int)$this->endTime, time(), time()]);
- }
-
- /**
- * A textual description of the current rule.
- *
- * @return String
- */
- public function toString()
- {
- $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
- $tpl = $factory->open('info');
- $tpl->set_attribute('rule', $this);
- return $tpl->render();
- }
-
- /**
- * Validates if the given request data is sufficient to configure this rule
- * (e.g. if required values are present).
- *
- * @param Array Request data
- * @return Array Error messages.
- */
- public function validate($data)
- {
- $errors = parent::validate($data);
- if (!$data['startdate'] && !$data['enddate']) {
- $errors[] = _('Bitte geben Sie entweder ein Start- oder Enddatum an.');
- }
- if ($data['startdate'] && $data['enddate'] && strtotime($data['enddate'] . ' ' . $data['endtime']) < strtotime($data['startdate']. ' ' . $data['starttime'])) {
- $errors[] = _('Das Enddatum darf nicht vor dem Startdatum liegen.');
- }
- return $errors;
- }
-
-} /* end of class TimedAdmission */
-
-?>
diff --git a/lib/admissionrules/timedadmission/TimedAdmission.php b/lib/admissionrules/timedadmission/TimedAdmission.php
new file mode 100644
index 0000000..e167bd8
--- /dev/null
+++ b/lib/admissionrules/timedadmission/TimedAdmission.php
@@ -0,0 +1,243 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+class TimedAdmission extends AdmissionRule
+{
+ // --- ATTRIBUTES ---
+
+ /**
+ * End of course admission.
+ */
+ public $endTime = 0;
+
+ /**
+ * Start of course admission.
+ */
+ public $startTime = 0;
+
+ // --- OPERATIONS ---
+
+ /**
+ * Standard constructor
+ *
+ * @param String ruleId
+ */
+ public function __construct($ruleId='', $courseSetId = '')
+ {
+ parent::__construct($ruleId, $courseSetId);
+ $this->default_message = _('Sie befinden sich nicht innerhalb des Anmeldezeitraums.');
+ if ($ruleId) {
+ $this->load();
+ } else {
+ $this->id = $this->generateId('timedadmissions');
+ }
+ }
+
+ /**
+ * Deletes the admission rule and all associated data.
+ */
+ public function delete() {
+ parent::delete();
+ // Delete rule data.
+ $stmt = DBManager::get()->prepare("DELETE FROM `timedadmissions`
+ WHERE `rule_id`=?");
+ $stmt->execute([$this->id]);
+ }
+
+ /**
+ * Gets some text that describes what this AdmissionRule (or respective
+ * subclass) does.
+ */
+ public static function getDescription() {
+ return _("Anmelderegeln dieses Typs legen ein Zeitfenster fest, in ".
+ "dem die Anmeldung zu Veranstaltungen möglich ist. Es kann auch ".
+ "nur ein Start- oder Endzeitpunkt angegeben werden.");
+ }
+
+ /**
+ * Gets the end of course admission.
+ *
+ * @return int
+ */
+ public function getEndTime()
+ {
+ return $this->endTime;
+ }
+
+ /**
+ * Return this rule's name.
+ */
+ public static function getName() {
+ return _("Zeitgesteuerte Anmeldung");
+ }
+
+ /**
+ * Gets the start of course admission.
+ *
+ * @return int
+ */
+ public function getStartTime()
+ {
+ return $this->startTime;
+ }
+
+ /**
+ * Gets the template that provides a configuration GUI for this rule.
+ *
+ * @return String
+ */
+ public function getTemplate() {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ // Open specific template for this rule and insert base template.
+ $tpl = $factory->open('configure');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Helper function for loading rule definition from database.
+ */
+ public function load() {
+ // Load data.
+ $stmt = DBManager::get()->prepare("SELECT *
+ FROM `timedadmissions` WHERE `rule_id`=? LIMIT 1");
+ $stmt->execute([$this->id]);
+ if ($current = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $this->message = $current['message'];
+ $this->startTime = $current['start_time'];
+ $this->endTime = $current['end_time'];
+ }
+ }
+
+ /**
+ * Is admission allowed according to the defined time frame?
+ *
+ * @param String userId
+ * @param String courseId
+ * @return Array
+ */
+ public function ruleApplies($userId, $courseId) {
+ $errors = [];
+ if (!$this->checkTimeFrame()) {
+ $errors[] = $this->getMessage();
+ }
+ return $errors;
+ }
+
+ /**
+ * Uses the given data to fill the object values. This can be used
+ * as a generic function for storing data if the concrete rule type
+ * isn't known in advance.
+ *
+ * @param Array $data
+ * @return AdmissionRule This object.
+ */
+ public function setAllData($data) {
+ parent::setAllData($data);
+ if ($data['startdate']) {
+ $sdate = $data['startdate'];
+ $stime = $data['starttime'];
+ $parsed = date_parse($sdate.' '.$stime);
+ $timestamp = mktime($parsed['hour'], $parsed['minute'], 0, $parsed['month'], $parsed['day'], $parsed['year']);
+ $this->setStartTime($timestamp);
+ }
+ if ($data['enddate']) {
+ $edate = $data['enddate'];
+ $etime = $data['endtime'];
+ if (!$etime) {
+ $etime = '23:59';
+ }
+ $parsed = date_parse($edate.' '.$etime);
+ $timestamp = mktime($parsed['hour'], $parsed['minute'], 0, $parsed['month'], $parsed['day'], $parsed['year']);
+ $this->setEndTime($timestamp);
+ }
+ return $this;
+ }
+
+ /**
+ * Sets a new end timestamp for course admission.
+ *
+ * @param int newEndTime
+ * @return TimedAdmission
+ */
+ public function setEndTime($newEndTime)
+ {
+ $this->endTime = $newEndTime;
+ return $this;
+ }
+
+ /**
+ * Sets a new start timestamp for course admission.
+ *
+ * @param int newStartTime
+ * @return TimedAdmission
+ */
+ public function setStartTime($newStartTime)
+ {
+ $this->startTime = $newStartTime;
+ return $this;
+ }
+
+ /**
+ * Store rule definition to database.
+ */
+ public function store() {
+ // Store data.
+ $stmt = DBManager::get()->prepare("INSERT INTO `timedadmissions`
+ (`rule_id`, `message`, `start_time`,
+ `end_time`, `mkdate`, `chdate`) VALUES (?, ?, ?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE `start_time`=VALUES(`start_time`),
+ `end_time`=VALUES(`end_time`),message=VALUES(message), `chdate`=VALUES(`chdate`)");
+ $stmt->execute([$this->id, $this->message, (int)$this->startTime,
+ (int)$this->endTime, time(), time()]);
+ }
+
+ /**
+ * A textual description of the current rule.
+ *
+ * @return String
+ */
+ public function toString()
+ {
+ $factory = new Flexi\Factory(dirname(__FILE__).'/templates/');
+ $tpl = $factory->open('info');
+ $tpl->set_attribute('rule', $this);
+ return $tpl->render();
+ }
+
+ /**
+ * Validates if the given request data is sufficient to configure this rule
+ * (e.g. if required values are present).
+ *
+ * @param Array Request data
+ * @return Array Error messages.
+ */
+ public function validate($data)
+ {
+ $errors = parent::validate($data);
+ if (!$data['startdate'] && !$data['enddate']) {
+ $errors[] = _('Bitte geben Sie entweder ein Start- oder Enddatum an.');
+ }
+ if ($data['startdate'] && $data['enddate'] && strtotime($data['enddate'] . ' ' . $data['endtime']) < strtotime($data['startdate']. ' ' . $data['starttime'])) {
+ $errors[] = _('Das Enddatum darf nicht vor dem Startdatum liegen.');
+ }
+ return $errors;
+ }
+
+} /* end of class TimedAdmission */
+
+?>
diff --git a/lib/bootstrap-autoload.php b/lib/bootstrap-autoload.php
index e2554c7..7d99f86 100644
--- a/lib/bootstrap-autoload.php
+++ b/lib/bootstrap-autoload.php
@@ -3,102 +3,21 @@
require 'lib/classes/StudipAutoloader.php';
StudipAutoloader::register();
-// General classes folders
-StudipAutoloader::addAutoloadPath('lib/models');
-StudipAutoloader::addAutoloadPath('lib/models/calendar');
-StudipAutoloader::addAutoloadPath('lib/models/resources');
-StudipAutoloader::addAutoloadPath('lib/classes');
-StudipAutoloader::addAutoloadPath('lib/classes', 'Studip');
-
-// Plugins
-StudipAutoloader::addAutoloadPath('lib/plugins/core');
-StudipAutoloader::addAutoloadPath('lib/plugins/db');
-StudipAutoloader::addAutoloadPath('lib/plugins/engine');
-
-// Specialized folders
-StudipAutoloader::addAutoloadPath('lib/classes/admission');
-StudipAutoloader::addAutoloadPath('lib/classes/admission/userfilter');
-StudipAutoloader::addAutoloadPath('lib/classes/auth_plugins');
-StudipAutoloader::addAutoloadPath('lib/classes/calendar');
-
-StudipAutoloader::addAutoloadPath('lib/classes/cache', 'Studip\\Cache');
class_alias(\Studip\Cache\Factory::class, 'StudipCacheFactory');
class_alias(\Studip\Cache\Cache::class, 'StudipCache');
-
-StudipAutoloader::addAutoloadPath('lib/classes/exportdocument');
-StudipAutoloader::addAutoloadPath('lib/classes/forms');
-StudipAutoloader::addAutoloadPath('lib/classes/globalsearch');
-StudipAutoloader::addAutoloadPath('lib/classes/helpbar');
-StudipAutoloader::addAutoloadPath('lib/classes/librarysearch/resultparsers');
-StudipAutoloader::addAutoloadPath('lib/classes/librarysearch/searchmodules');
-StudipAutoloader::addAutoloadPath('lib/classes/librarysearch');
-StudipAutoloader::addAutoloadPath('lib/classes/searchtypes');
-StudipAutoloader::addAutoloadPath('lib/classes/sidebar');
-StudipAutoloader::addAutoloadPath('lib/classes/visibility');
-StudipAutoloader::addAutoloadPath('lib/classes/coursewizardsteps');
-StudipAutoloader::addAutoloadPath('lib/classes/wiki');
-
-StudipAutoloader::addAutoloadPath('lib/calendar');
-StudipAutoloader::addAutoloadPath('lib/calendar', 'Studip\\Calendar');
-StudipAutoloader::addAutoloadPath('lib/exceptions');
-StudipAutoloader::addAutoloadPath('lib/exceptions/resources');
-StudipAutoloader::addAutoloadPath('lib/exTpl', 'exTpl');
-StudipAutoloader::addAutoloadPath('lib/filesystem');
-StudipAutoloader::addAutoloadPath('lib/migrations');
-StudipAutoloader::addAutoloadPath('lib/modules');
-StudipAutoloader::addAutoloadPath('lib/navigation');
-StudipAutoloader::addAutoloadPath('lib/phplib');
-StudipAutoloader::addAutoloadPath('lib/raumzeit');
-StudipAutoloader::addAutoloadPath('lib/resources');
-StudipAutoloader::addAutoloadPath('lib/activities', 'Studip\\Activity');
-
-StudipAutoloader::addAutoloadPath('lib/calendar/lib');
-StudipAutoloader::addAutoloadPath('lib/elearning');
-StudipAutoloader::addAutoloadPath('lib/extern');
-StudipAutoloader::addAutoloadPath('lib/ilias_interface');
-
-// Flexi
-StudipAutoloader::addAutoloadPath('lib/flexi', 'Flexi');
class_alias(Flexi\PhpTemplate::class, 'Flexi_PhpTemplate');
class_alias(Flexi\Template::class, 'Flexi_Template');
class_alias(Flexi\Factory::class, 'Flexi_TemplateFactory');
class_alias(Flexi\TemplateNotFoundException::class, 'Flexi_TemplateNotFoundException');
-
-// Trails
-StudipAutoloader::addAutoloadPath('lib/trails', 'Trails');
class_alias(Trails\Controller::class, 'Trails_Controller');
class_alias(Trails\Dispatcher::class, 'Trails_Dispatcher');
class_alias(Trails\Exception::class, 'Trails_Exception');
class_alias(Trails\Flash::class, 'Trails_Flash');
class_alias(Trails\Inflector::class, 'Trails_Inflector');
class_alias(Trails\Response::class, 'Trails_Response');
-
class_alias(Trails\Exceptions\DoubleRenderError::class, 'Trails_DoubleRenderError');
class_alias(Trails\Exceptions\MissingFile::class, 'Trails_MissingFile');
class_alias(Trails\Exceptions\RoutingError::class, 'Trails_RoutingError');
class_alias(Trails\Exceptions\SessionRequiredException::class, 'Trails_SessionRequiredException');
class_alias(Trails\Exceptions\UnknownAction::class, 'Trails_UnknownAction');
class_alias(Trails\Exceptions\UnknownController::class, 'Trails_UnknownController');
-
-// Messy file names
-StudipAutoloader::addClassLookups([
- 'email_validation_class' => 'lib/phplib/email_validation.class.php',
- 'messaging' => 'lib/messaging.inc.php',
- 'StudipPlugin' => 'lib/plugins/core/StudIPPlugin.class.php',
- 'MVVController' => 'app/controllers/module/mvv_controller.php'
-]);
-
-// Vendor
-StudipAutoloader::addClassLookups([
- 'PasswordHash' => 'vendor/phpass/PasswordHash.php',
-]);
-
-// XMLRpc
-StudipAutoloader::addClassLookup(
- ['xmlrpcval', 'xmlrpcmsg', 'xmlrpcresp', 'xmlrpc_client'],
- 'composer/phpxmlrpc/phpxmlrpc/lib/xmlrpc.inc'
-);
-StudipAutoloader::addClassLookup(
- ['xmlrpc_server'],
- 'composer/phpxmlrpc/phpxmlrpc/lib/xmlrpcs.inc'
-);
diff --git a/lib/bootstrap.php b/lib/bootstrap.php
index 8c618ff..478ca0e 100644
--- a/lib/bootstrap.php
+++ b/lib/bootstrap.php
@@ -224,7 +224,7 @@ if (Config::get()->CALENDAR_ENABLE) {
}
if (Config::get()->SOAP_ENABLE) {
- require_once 'lib/soap/StudipSoapClient' . (Config::get()->SOAP_USE_PHP5 ? '_PHP5' : '' ) . '.class.php';
+ require_once 'lib/soap/StudipSoapClient' . (Config::get()->SOAP_USE_PHP5 ? '_PHP5' : '' ) . '.php';
}
if (Config::Get()->ILIAS_INTERFACE_ENABLE) {
diff --git a/lib/calendar/CalendarColumn.class.php b/lib/calendar/CalendarColumn.class.php
deleted file mode 100644
index cc3abea..0000000
--- a/lib/calendar/CalendarColumn.class.php
+++ /dev/null
@@ -1,371 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- *
- * @deprecated since Stud.IP 5.5
- */
-
-class CalendarColumn
-{
- protected static $number = 0;
- protected $title = "";
- protected $id = "";
- public $entries = [];
- protected $url = "";
- protected $grouped = false;
- protected $sorted_entries = null;
-
- /**
- * creates instance of type CalendarColumn
- *
- * @param string $id necessary if you want JavaScript enabled for this calendar
- * @return CalendarColumn
- */
- static public function create($id = null) {
- $column = new CalendarColumn($id);
- return $column;
- }
-
- /**
- * constructor
- *
- * @param string $id necessary if you want JavaScript enabled for this column
- */
- public function __construct($id = null) {
- $id !== null || $id = md5(uniqid("CalendarColumn_".self::$number++));
- $this->setId($id);
- }
-
- /**
- * returns the id of the column
- *
- * @return string
- */
- public function getId() {
- return $this->id;
- }
-
- /**
- * sets the id for this column, which is only necessary if you want
- * Javascript to be enabled for this calendar
- *
- * @param string $id new id for this column
- * @return CalendarColumn
- */
- public function setId($id) {
- $this->id = $id;
- return $this;
- }
-
- /**
- * sets a title like "monday" for this column, which will be displayed in the calendar
- *
- * @param string $new_title new title
- * @return CalendarColumn
- */
- public function setTitle($new_title) {
- $this->title = $new_title;
- return $this;
- }
-
- /**
- * returns the title of this column like "monday"
- *
- * @return string title of column
- */
- public function getTitle() {
- return $this->title;
- }
-
- /**
- * sets the url to be directed to when clicking on the title of the column.
- * Usually this is a single-day-view of the calendar.
- *
- * @param string $new_url an url
- * @return CalendarColumn
- */
- public function setURL($new_url) {
- $this->url = $new_url;
- return $this;
- }
-
- /**
- * returns the URL of the column (see setURL)
- *
- * @return string an url
- */
- public function getURL() {
- return $this->url;
- }
-
- /**
- * adds a new entry in the column. The entry needs to be an associative array
- * with parameters as follows:
- *
- * @param array $entry_array associative array for an entry in the column like
- * array (
- * 'color' => the color in hex (css-like, without the #)
- * 'start' => the (start hour * 100) + (start minute)
- * 'end' => the (end hour * 100) + (end minute)
- * 'title' => the entry`s title
- * 'content' => whatever shall be the content of the entry as a string
- * )
- */
- public function addEntry($entry_array) {
- if (!isset($entry_array['start']) || !isset($entry_array['end'])
- || !isset($entry_array['title']) ) {
- throw new InvalidArgumentException('The entry '. print_r($entry_array, true) .' does not follow the specifications!');
- } else {
- $this->entries[] = $entry_array;
- }
- return $this;
- }
-
- /**
- * adds many entries to the column. For the syntax of an entry see addEntry()
- *
- * @param array $entries_array
- * @return CalendarColumn
- */
- public function addEntries($entries_array = []) {
- foreach ($entries_array as $entry_array) {
- $this->addEntry($entry_array);
- }
- return $this;
- }
-
- /**
- * returns all entries of this column
- *
- * @return array of arrays like
- * array (
- * 'color' => the color in hex (css-like, without the #)
- * 'start' => the (start hour * 100) + (start minute)
- * 'end' => the (end hour * 100) + (end minute)
- * 'title' => the entry`s title
- * 'content' => whatever shall be the content of the entry as a string
- * )
- */
- public function getEntries() {
- return $this->entries;
- }
-
- /**
- * deletes all entries of this column. So the only way to edit an entry is
- * getting all entries with getEntries, edit this entry, eraseEntries() and
- * addEntries(). Not very short, but at least it works.
- *
- * @return CalendarColumn
- */
- public function eraseEntries() {
- $this->entries = [];
- return $this;
- }
-
- /**
- * Returns an array of calendar-entries, grouped by day and additionally grouped by same start and end
- * if groupEntries(true) has been called.
- *
- * @return mixed the (double-)grouped entries
- */
- public function getGroupedEntries()
- {
- if (empty($this->sorted_entries)) {
- if ($this->isGrouped()) {
- $this->sorted_entries = $this->sortAndGroupEntries();
- } else {
- $this->sorted_entries = $this->sortEntries();
- }
- }
-
- return $this->sorted_entries;
- }
-
- /**
- * sorts and groups entries and returns them
- * only used by columns with grouped entries like instituteschedules
- *
- * @return array
- */
- public function sortAndGroupEntries()
- {
- $day = $this->getTitle();
-
- $entries_for_column = $this->getEntries();
- $result = [];
- $new_entries = [];
-
- // 1st step - group all entries with the same duration
- foreach ($entries_for_column as $entry_id => $entry) {
- $new_entries[$entry['start'] .'_'. $entry['end']][] = $entry;
- }
-
- $column = 0;
-
- // 2nd step - optimize the groups
- while (sizeof($new_entries) > 0) {
- $lstart = 2399; $lend = 0;
-
- foreach ($new_entries as $time => $grouped_entries) {
- list($start, $end) = explode('_', $time);
- if ($start < $lstart /*&& ($end - $start) >= ($lend - $lstart)*/ ) {
- $lstart = $start;
- $lend = $end;
- }
- }
-
- $result['col_'. $column][] = $new_entries[$lstart .'_'. $lend];
- unset($new_entries[$lstart .'_'. $lend]);
-
- $hit = true;
-
- while ($hit) {
- $hit = false;
- $hstart = 2399; $hend = 2399;
-
- // check, if there is something, that can be placed after
- foreach ($new_entries as $time => $grouped_entries) {
- list($start, $end) = explode('_', $time);
-
- if ( ($start >= $lend) && ($start < $hstart) ) {
- $hstart = $start;
- $hend = $end;
- $hit = true;
- }
- }
-
- if ($hit) {
- $lend = $hend;
- $result['col_'. $column][] = $new_entries[$hstart .'_'. $hend];
- unset($new_entries[$hstart .'_'. $hend]);
- }
- }
-
- $column++;
- } // 2nd step
-
- return $result;
-
- }
-
- /**
- * sorts entries and returns them
- *
- * @return array
- */
- public function sortEntries()
- {
- $entries_for_column = $this->getEntries();
-
- $result = [];
- $column = 0;
-
- // 2nd step - optimize the groups
- while (sizeof($entries_for_column) > 0) {
- $lstart = 2399; $lend = 0; $lkey = null;
-
- foreach ($entries_for_column as $entry_key => $entry) {
- if ($entry['start'] < $lstart /*&& ($end - $start) >= ($lend - $lstart)*/ ) {
- $lstart = $entry['start'];
- $lend = $entry['end'];
- $lkey = $entry_key;
- }
- }
-
- $result['col_'. $column][] = $entries_for_column[$lkey];
- unset($entries_for_column[$lkey]);
-
- $hit = true;
-
- while ($hit) {
- $hit = false;
- $hstart = 2399; $hend = 2399; $hkey = null;
-
- // check, if there is something, that can be placed after
- foreach ($entries_for_column as $entry_key => $entry) {
- if ( ($entry['start'] >= $lend) && ($entry['start'] < $hstart) ) {
- // && (($end - $start) > ($hend - $hstart)) ) {
- $hstart = $entry['start'];
- $hend = $entry['end'];
- $hkey = $entry_key;
- $hit = true;
- }
- }
-
- if ($hit) {
- $lend = $hend;
- $result['col_'. $column][] = $entries_for_column[$hkey];
- unset($entries_for_column[$hkey]);
- }
- }
-
- $column++;
- } // 2nd step
- return $result;
-
- }
-
- /**
- * returns a matrix that tells the number of entries for a given timeslot
- *
- * @return array
- */
- public function getMatrix() {
- $group_matrix = [];
- foreach ($this->getGroupedEntries() as $groups) {
- foreach ($groups as $group) {
- if (isset($group[0]) && is_array($group[0])) {
- $data = $group[0];
- } else {
- $data = $group;
- }
-
- for ($i = floor($data['start'] / 100); $i <= floor($data['end'] / 100); $i++) {
- for ($j = 0; $j < 60; $j++) {
- if (($i * 100) + $j >= $data['start'] && ($i * 100) + $j < $data['end']) {
- if (!isset($group_matrix[$i * 100 + $j])) {
- $group_matrix[$i * 100 + $j] = 0;
- }
- $group_matrix[$i * 100 + $j]++;
- }
- }
- }
- }
- }
- return $group_matrix;
- }
-
- /**
- * check, if a grouped view of the entries is requested
- *
- * @return bool true if grouped, false otherwise
- */
- public function isGrouped()
- {
- return $this->grouped;
- }
-
- /**
- * Call this function th enable/disable the grouping of entries with the same start and end.
- *
- * @param bool $group optional, defaults to true
- * @return void
- */
- public function groupEntries($grouped = true)
- {
- $this->grouped = $grouped;
- }
-
-}
diff --git a/lib/calendar/CalendarColumn.php b/lib/calendar/CalendarColumn.php
new file mode 100644
index 0000000..cc3abea
--- /dev/null
+++ b/lib/calendar/CalendarColumn.php
@@ -0,0 +1,371 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ *
+ * @deprecated since Stud.IP 5.5
+ */
+
+class CalendarColumn
+{
+ protected static $number = 0;
+ protected $title = "";
+ protected $id = "";
+ public $entries = [];
+ protected $url = "";
+ protected $grouped = false;
+ protected $sorted_entries = null;
+
+ /**
+ * creates instance of type CalendarColumn
+ *
+ * @param string $id necessary if you want JavaScript enabled for this calendar
+ * @return CalendarColumn
+ */
+ static public function create($id = null) {
+ $column = new CalendarColumn($id);
+ return $column;
+ }
+
+ /**
+ * constructor
+ *
+ * @param string $id necessary if you want JavaScript enabled for this column
+ */
+ public function __construct($id = null) {
+ $id !== null || $id = md5(uniqid("CalendarColumn_".self::$number++));
+ $this->setId($id);
+ }
+
+ /**
+ * returns the id of the column
+ *
+ * @return string
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * sets the id for this column, which is only necessary if you want
+ * Javascript to be enabled for this calendar
+ *
+ * @param string $id new id for this column
+ * @return CalendarColumn
+ */
+ public function setId($id) {
+ $this->id = $id;
+ return $this;
+ }
+
+ /**
+ * sets a title like "monday" for this column, which will be displayed in the calendar
+ *
+ * @param string $new_title new title
+ * @return CalendarColumn
+ */
+ public function setTitle($new_title) {
+ $this->title = $new_title;
+ return $this;
+ }
+
+ /**
+ * returns the title of this column like "monday"
+ *
+ * @return string title of column
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * sets the url to be directed to when clicking on the title of the column.
+ * Usually this is a single-day-view of the calendar.
+ *
+ * @param string $new_url an url
+ * @return CalendarColumn
+ */
+ public function setURL($new_url) {
+ $this->url = $new_url;
+ return $this;
+ }
+
+ /**
+ * returns the URL of the column (see setURL)
+ *
+ * @return string an url
+ */
+ public function getURL() {
+ return $this->url;
+ }
+
+ /**
+ * adds a new entry in the column. The entry needs to be an associative array
+ * with parameters as follows:
+ *
+ * @param array $entry_array associative array for an entry in the column like
+ * array (
+ * 'color' => the color in hex (css-like, without the #)
+ * 'start' => the (start hour * 100) + (start minute)
+ * 'end' => the (end hour * 100) + (end minute)
+ * 'title' => the entry`s title
+ * 'content' => whatever shall be the content of the entry as a string
+ * )
+ */
+ public function addEntry($entry_array) {
+ if (!isset($entry_array['start']) || !isset($entry_array['end'])
+ || !isset($entry_array['title']) ) {
+ throw new InvalidArgumentException('The entry '. print_r($entry_array, true) .' does not follow the specifications!');
+ } else {
+ $this->entries[] = $entry_array;
+ }
+ return $this;
+ }
+
+ /**
+ * adds many entries to the column. For the syntax of an entry see addEntry()
+ *
+ * @param array $entries_array
+ * @return CalendarColumn
+ */
+ public function addEntries($entries_array = []) {
+ foreach ($entries_array as $entry_array) {
+ $this->addEntry($entry_array);
+ }
+ return $this;
+ }
+
+ /**
+ * returns all entries of this column
+ *
+ * @return array of arrays like
+ * array (
+ * 'color' => the color in hex (css-like, without the #)
+ * 'start' => the (start hour * 100) + (start minute)
+ * 'end' => the (end hour * 100) + (end minute)
+ * 'title' => the entry`s title
+ * 'content' => whatever shall be the content of the entry as a string
+ * )
+ */
+ public function getEntries() {
+ return $this->entries;
+ }
+
+ /**
+ * deletes all entries of this column. So the only way to edit an entry is
+ * getting all entries with getEntries, edit this entry, eraseEntries() and
+ * addEntries(). Not very short, but at least it works.
+ *
+ * @return CalendarColumn
+ */
+ public function eraseEntries() {
+ $this->entries = [];
+ return $this;
+ }
+
+ /**
+ * Returns an array of calendar-entries, grouped by day and additionally grouped by same start and end
+ * if groupEntries(true) has been called.
+ *
+ * @return mixed the (double-)grouped entries
+ */
+ public function getGroupedEntries()
+ {
+ if (empty($this->sorted_entries)) {
+ if ($this->isGrouped()) {
+ $this->sorted_entries = $this->sortAndGroupEntries();
+ } else {
+ $this->sorted_entries = $this->sortEntries();
+ }
+ }
+
+ return $this->sorted_entries;
+ }
+
+ /**
+ * sorts and groups entries and returns them
+ * only used by columns with grouped entries like instituteschedules
+ *
+ * @return array
+ */
+ public function sortAndGroupEntries()
+ {
+ $day = $this->getTitle();
+
+ $entries_for_column = $this->getEntries();
+ $result = [];
+ $new_entries = [];
+
+ // 1st step - group all entries with the same duration
+ foreach ($entries_for_column as $entry_id => $entry) {
+ $new_entries[$entry['start'] .'_'. $entry['end']][] = $entry;
+ }
+
+ $column = 0;
+
+ // 2nd step - optimize the groups
+ while (sizeof($new_entries) > 0) {
+ $lstart = 2399; $lend = 0;
+
+ foreach ($new_entries as $time => $grouped_entries) {
+ list($start, $end) = explode('_', $time);
+ if ($start < $lstart /*&& ($end - $start) >= ($lend - $lstart)*/ ) {
+ $lstart = $start;
+ $lend = $end;
+ }
+ }
+
+ $result['col_'. $column][] = $new_entries[$lstart .'_'. $lend];
+ unset($new_entries[$lstart .'_'. $lend]);
+
+ $hit = true;
+
+ while ($hit) {
+ $hit = false;
+ $hstart = 2399; $hend = 2399;
+
+ // check, if there is something, that can be placed after
+ foreach ($new_entries as $time => $grouped_entries) {
+ list($start, $end) = explode('_', $time);
+
+ if ( ($start >= $lend) && ($start < $hstart) ) {
+ $hstart = $start;
+ $hend = $end;
+ $hit = true;
+ }
+ }
+
+ if ($hit) {
+ $lend = $hend;
+ $result['col_'. $column][] = $new_entries[$hstart .'_'. $hend];
+ unset($new_entries[$hstart .'_'. $hend]);
+ }
+ }
+
+ $column++;
+ } // 2nd step
+
+ return $result;
+
+ }
+
+ /**
+ * sorts entries and returns them
+ *
+ * @return array
+ */
+ public function sortEntries()
+ {
+ $entries_for_column = $this->getEntries();
+
+ $result = [];
+ $column = 0;
+
+ // 2nd step - optimize the groups
+ while (sizeof($entries_for_column) > 0) {
+ $lstart = 2399; $lend = 0; $lkey = null;
+
+ foreach ($entries_for_column as $entry_key => $entry) {
+ if ($entry['start'] < $lstart /*&& ($end - $start) >= ($lend - $lstart)*/ ) {
+ $lstart = $entry['start'];
+ $lend = $entry['end'];
+ $lkey = $entry_key;
+ }
+ }
+
+ $result['col_'. $column][] = $entries_for_column[$lkey];
+ unset($entries_for_column[$lkey]);
+
+ $hit = true;
+
+ while ($hit) {
+ $hit = false;
+ $hstart = 2399; $hend = 2399; $hkey = null;
+
+ // check, if there is something, that can be placed after
+ foreach ($entries_for_column as $entry_key => $entry) {
+ if ( ($entry['start'] >= $lend) && ($entry['start'] < $hstart) ) {
+ // && (($end - $start) > ($hend - $hstart)) ) {
+ $hstart = $entry['start'];
+ $hend = $entry['end'];
+ $hkey = $entry_key;
+ $hit = true;
+ }
+ }
+
+ if ($hit) {
+ $lend = $hend;
+ $result['col_'. $column][] = $entries_for_column[$hkey];
+ unset($entries_for_column[$hkey]);
+ }
+ }
+
+ $column++;
+ } // 2nd step
+ return $result;
+
+ }
+
+ /**
+ * returns a matrix that tells the number of entries for a given timeslot
+ *
+ * @return array
+ */
+ public function getMatrix() {
+ $group_matrix = [];
+ foreach ($this->getGroupedEntries() as $groups) {
+ foreach ($groups as $group) {
+ if (isset($group[0]) && is_array($group[0])) {
+ $data = $group[0];
+ } else {
+ $data = $group;
+ }
+
+ for ($i = floor($data['start'] / 100); $i <= floor($data['end'] / 100); $i++) {
+ for ($j = 0; $j < 60; $j++) {
+ if (($i * 100) + $j >= $data['start'] && ($i * 100) + $j < $data['end']) {
+ if (!isset($group_matrix[$i * 100 + $j])) {
+ $group_matrix[$i * 100 + $j] = 0;
+ }
+ $group_matrix[$i * 100 + $j]++;
+ }
+ }
+ }
+ }
+ }
+ return $group_matrix;
+ }
+
+ /**
+ * check, if a grouped view of the entries is requested
+ *
+ * @return bool true if grouped, false otherwise
+ */
+ public function isGrouped()
+ {
+ return $this->grouped;
+ }
+
+ /**
+ * Call this function th enable/disable the grouping of entries with the same start and end.
+ *
+ * @param bool $group optional, defaults to true
+ * @return void
+ */
+ public function groupEntries($grouped = true)
+ {
+ $this->grouped = $grouped;
+ }
+
+}
diff --git a/lib/calendar/CalendarView.class.php b/lib/calendar/CalendarView.class.php
deleted file mode 100644
index 3d89ef1..0000000
--- a/lib/calendar/CalendarView.class.php
+++ /dev/null
@@ -1,344 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-/**
- * Kind of bean class for the calendar view.
- *
- * Example of use:
- *
- * // create a calendar-view and add a column
- * $plan = new CalendarView();
- * $plan->addColumn(_('Spalte 1'))
- * ->addEntry(array(
- * 'id' => 1,
- * 'color' => '#5C2D64',
- * 'start' => '0930',
- * 'end' => '1100',
- * 'title' => 'Mathe 2',
- * 'content' => 'Die Mathematiker kreiden sich mal wieder was an.'
- * )
- * );
- *
- * // display the calendar (containing one column)
- * print $plan->render();
- *
- * @since 2.0
- *
- * @deprecated since Stud.IP 5.5
- */
-
-class CalendarView
-{
-
- protected $entries = [];
- protected $entry_columns = [];
- protected $height = 40;
- protected $grouped = false;
- protected $start_hour = 8;
- protected $end_hour = 21;
- protected $insertFunction = "";
- protected $templates = [];
- protected $read_only = false;
-
- protected static $number_of_instances = 1;
- protected $view_id;
-
- public $sorted_entries = [];
-
-
- /**
- * You need to pass an instance of this class to the template. The constructor
- * expects an array of entries of the following type:
- * array(
- * $day_number => array(array (
- * 'color' => the color in hex (css-like, without the #)
- * 'start' => the (start hour * 100) + (start minute)
- * 'end' => the (end hour * 100) + (end minute)
- * //'day' => day of week (0 = Sunday, ... , 6 = Saturday)
- * 'title' => the entry`s title
- * 'content' => whatever shall be the content of the entry as a string
- * ) ...) ...
- * )
- *
- * @param mixed $entries an array of entries (see above)
- * @param string $controller the name of the controller. Used to create links.
- */
- public function __construct($entries = [])
- {
- if (!is_array($entries)) {
- throw new Exception('You need to pass some entries to the CalendarView!');
- }
- $this->view_id = self::$number_of_instances++;
- $this->checkEntries($entries);
- $this->entries = $entries;
- }
-
- /**
- * set the height for one hour. This value is used to calculate the whole height of the schedule.
- *
- * @param int $entry_height the height of one hour in the schedule
- */
- public function setHeight($height)
- {
- $this->height = $height;
- }
-
- /**
- * set the range of hours to be displayed. the start_hour has to be smaller than the end_hour
- *
- * @param int $start_hour the hour to start displaying at
- * @param int $end_hour the hour to stop displaying at
- */
- public function setRange($start_hour, $end_hour)
- {
- $this->start_hour = $start_hour;
- $this->end_hour = $end_hour;
- }
-
- /**
- * does some plausability checks on an array of calendar-entries
- *
- * @param mixed $entries an array of calendar-entries
- *
- * @return bool false if check failed, true otherwise
- */
- protected function checkEntries($entries)
- {
- foreach ($entries as $column) {
- if (!$column instanceof CalendarColumn) {
- throw new Exception('A column of the entries in the CalenarView is not of type CalendarColumn.');
- }
- }
- return true;
- }
-
- /**
- * adds a new column to this view. All entries created with addEntry will be
- * added to this column.
- *
- * @param string $title like "monday" to be displayed on top of the column
- * @param string $url to be called when clicked on the title of the column
- * @param string $id any kind of id of the column
- * @return CalendarView
- */
- public function addColumn($title, $url = "", $id = null)
- {
- $this->entries[] = CalendarColumn::create($id)
- ->setTitle($title)
- ->setURL($url);
- return $this;
- }
-
-
- /**
- * adds a new entry to the last current column. The entry needs to be an
- * associative array with parameters as follows:
- * @param array $entry_array: associative array for an entry in the column like
- * array (
- * 'color' => the color in hex (css-like, without the #)
- * 'start' => the (start hour * 100) + (start minute)
- * 'end' => the (end hour * 100) + (end minute)
- * 'title' => the entry`s title
- * 'content' => whatever shall be the content of the entry as a string
- * )
- * @return CalendarView
- */
- public function addEntry($entry_array)
- {
- if (count($this->entries)) {
- $this->entries[count($this->entries)-1]->addEntry($entry_array);
- } else {
- throw new InvalidArgumentException(_("Es existiert noch keine Spalte in der Ansicht, zu der der Eintrag hinzugefügt werden kann."));
- }
- return $this;
- }
-
-
- /**
- * Call this function to enable/disable the grouping of entries with the same start and end.
- *
- * @param bool $group optional, defaults to true
- */
- public function groupEntries($grouped = true)
- {
- $this->grouped = $grouped;
- foreach($this->getColumns() as $entry_column) {
- $entry_column->groupEntries();
- }
- }
-
- /**
- * When a column is clicked at no entry this function is called.
- * First the templates generates a new entry at the clicked time. Then this
- * js-function is called which gets the parameters
- * "function (new_entry_dom_object, column_id, hour) { ... }"
- * with new_entry_dom_object: a real dom-object of the div of the entry
- * column_id: id of the column
- * hour: integer number from 0 to 23
- * If js_function_object is an empty string, nothing will be done.
- *
- * @param string $js_function_object name of js-function or anonymous js-function
- * @return CalendarView
- */
- public function setInsertFunction($js_function_object)
- {
- $this->insertFunction = $js_function_object;
- return $this;
- }
-
- /**
- * outputs the CalendarView with all (grouped) dates in columns.
- *
- * @param array $params you can pass some additional variables to the templates
- *
- * @return string
- */
- public function render($params = [])
- {
- $style_parameters = [
- 'whole_height' => $this->getOverallHeight(),
- 'entry_height' => $this->getHeight()
- ];
- $factory = new Flexi\Factory(dirname(__file__).'/../../app/views');
- PageLayout::addStyle($factory->render('calendar/schedule/stylesheet', $style_parameters));
-
- $template = $GLOBALS['template_factory']->open("calendar/calendar_view.php");
- $template->set_attribute("calendar_view", $this);
- $template->set_attribute("view_id", $this->view_id);
- return $template->render($params);
- }
-
-
- /* * * * * * * * * * * * * * *
- * * * G E T T E R S * * *
- * * * * * * * * * * * * * * */
-
- /**
- * Returns an array of calendar-entries, grouped by day and additionally grouped by same start and end
- * if groupEntries(true) has been called.
- *
- * @return mixed the (double-)grouped entries
- */
- public function getEntries()
- {
- $this->sorted_entries = [];
- foreach ($this->getColumns() as $entry_column) {
- $this->sorted_entries['day_'. $entry_column->getId()] = $entry_column->getGroupedEntries();
- }
- return $this->sorted_entries;
- }
-
- /**
- * Returns an array where for each hour the number of concurrent entries is denoted.
- * Used by the calendar to display the entries in parallel.
- *
- * @return mixed concurrent entries at each hour
- */
- public function getMatrix()
- {
- $matrix = [];
- foreach ($this->getColumns() as $day => $entry_column) {
- $matrix['day_'.$day] = $entry_column->getMatrix();
- }
- return $matrix;
- }
-
-
- /**
- * returns the previously set start- and end-hour, denoting the
- * range of entries to be displayed in the current calendar-view
- *
- * @return array consisting of the start and end hour
- */
- public function getRange()
- {
- return [$this->start_hour, $this->end_hour];
- }
-
- /**
- * the calendar can be used in two modes. Use this function to check,
- * if the grouping-mode is enabled for the current calendar-view
- *
- * @return bool true if grouped, false otherwise
- */
- public function isGrouped()
- {
- return $this->grouped;
- }
-
- /**
- * returns the previously set height for one hour
- *
- * @return mixed the height
- */
- public function getHeight()
- {
- return $this->height;
- }
-
- /**
- * returns the overall height of the calendar
- *
- * @return mixed the overall height
- */
- public function getOverallHeight()
- {
- return $this->height * ($this->end_hour - $this->start_hour) + $this->height
- + 2 + ($this->end_hour - $this->start_hour) * 2;
- }
-
- /**
- * returns the previously set javasscript insert-function
- *
- * @return string name of js-function or anonymous js-function
- */
- public function getInsertFunction() {
- return $this->insertFunction;
- }
-
- /**
- * returns all columns of the calendar-view
- *
- * @return array of CalendarColumn
- */
- public function getColumns() {
- return $this->entries;
- }
-
- /**
- * Set the read-only status of the calendar
- *
- * @param bool $readonly true to make it read only, false otherwise
- *
- * @return void
- */
- public function setReadOnly($readonly = true)
- {
- $this->read_only = $readonly;
- }
-
- /**
- * Return the read-only status of this calendar
- *
- * @return bool the read-only status of this calendar
- */
- public function getReadOnly() {
- return $this->read_only;
- }
-
-}
diff --git a/lib/calendar/CalendarView.php b/lib/calendar/CalendarView.php
new file mode 100644
index 0000000..3d89ef1
--- /dev/null
+++ b/lib/calendar/CalendarView.php
@@ -0,0 +1,344 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+/**
+ * Kind of bean class for the calendar view.
+ *
+ * Example of use:
+ *
+ * // create a calendar-view and add a column
+ * $plan = new CalendarView();
+ * $plan->addColumn(_('Spalte 1'))
+ * ->addEntry(array(
+ * 'id' => 1,
+ * 'color' => '#5C2D64',
+ * 'start' => '0930',
+ * 'end' => '1100',
+ * 'title' => 'Mathe 2',
+ * 'content' => 'Die Mathematiker kreiden sich mal wieder was an.'
+ * )
+ * );
+ *
+ * // display the calendar (containing one column)
+ * print $plan->render();
+ *
+ * @since 2.0
+ *
+ * @deprecated since Stud.IP 5.5
+ */
+
+class CalendarView
+{
+
+ protected $entries = [];
+ protected $entry_columns = [];
+ protected $height = 40;
+ protected $grouped = false;
+ protected $start_hour = 8;
+ protected $end_hour = 21;
+ protected $insertFunction = "";
+ protected $templates = [];
+ protected $read_only = false;
+
+ protected static $number_of_instances = 1;
+ protected $view_id;
+
+ public $sorted_entries = [];
+
+
+ /**
+ * You need to pass an instance of this class to the template. The constructor
+ * expects an array of entries of the following type:
+ * array(
+ * $day_number => array(array (
+ * 'color' => the color in hex (css-like, without the #)
+ * 'start' => the (start hour * 100) + (start minute)
+ * 'end' => the (end hour * 100) + (end minute)
+ * //'day' => day of week (0 = Sunday, ... , 6 = Saturday)
+ * 'title' => the entry`s title
+ * 'content' => whatever shall be the content of the entry as a string
+ * ) ...) ...
+ * )
+ *
+ * @param mixed $entries an array of entries (see above)
+ * @param string $controller the name of the controller. Used to create links.
+ */
+ public function __construct($entries = [])
+ {
+ if (!is_array($entries)) {
+ throw new Exception('You need to pass some entries to the CalendarView!');
+ }
+ $this->view_id = self::$number_of_instances++;
+ $this->checkEntries($entries);
+ $this->entries = $entries;
+ }
+
+ /**
+ * set the height for one hour. This value is used to calculate the whole height of the schedule.
+ *
+ * @param int $entry_height the height of one hour in the schedule
+ */
+ public function setHeight($height)
+ {
+ $this->height = $height;
+ }
+
+ /**
+ * set the range of hours to be displayed. the start_hour has to be smaller than the end_hour
+ *
+ * @param int $start_hour the hour to start displaying at
+ * @param int $end_hour the hour to stop displaying at
+ */
+ public function setRange($start_hour, $end_hour)
+ {
+ $this->start_hour = $start_hour;
+ $this->end_hour = $end_hour;
+ }
+
+ /**
+ * does some plausability checks on an array of calendar-entries
+ *
+ * @param mixed $entries an array of calendar-entries
+ *
+ * @return bool false if check failed, true otherwise
+ */
+ protected function checkEntries($entries)
+ {
+ foreach ($entries as $column) {
+ if (!$column instanceof CalendarColumn) {
+ throw new Exception('A column of the entries in the CalenarView is not of type CalendarColumn.');
+ }
+ }
+ return true;
+ }
+
+ /**
+ * adds a new column to this view. All entries created with addEntry will be
+ * added to this column.
+ *
+ * @param string $title like "monday" to be displayed on top of the column
+ * @param string $url to be called when clicked on the title of the column
+ * @param string $id any kind of id of the column
+ * @return CalendarView
+ */
+ public function addColumn($title, $url = "", $id = null)
+ {
+ $this->entries[] = CalendarColumn::create($id)
+ ->setTitle($title)
+ ->setURL($url);
+ return $this;
+ }
+
+
+ /**
+ * adds a new entry to the last current column. The entry needs to be an
+ * associative array with parameters as follows:
+ * @param array $entry_array: associative array for an entry in the column like
+ * array (
+ * 'color' => the color in hex (css-like, without the #)
+ * 'start' => the (start hour * 100) + (start minute)
+ * 'end' => the (end hour * 100) + (end minute)
+ * 'title' => the entry`s title
+ * 'content' => whatever shall be the content of the entry as a string
+ * )
+ * @return CalendarView
+ */
+ public function addEntry($entry_array)
+ {
+ if (count($this->entries)) {
+ $this->entries[count($this->entries)-1]->addEntry($entry_array);
+ } else {
+ throw new InvalidArgumentException(_("Es existiert noch keine Spalte in der Ansicht, zu der der Eintrag hinzugefügt werden kann."));
+ }
+ return $this;
+ }
+
+
+ /**
+ * Call this function to enable/disable the grouping of entries with the same start and end.
+ *
+ * @param bool $group optional, defaults to true
+ */
+ public function groupEntries($grouped = true)
+ {
+ $this->grouped = $grouped;
+ foreach($this->getColumns() as $entry_column) {
+ $entry_column->groupEntries();
+ }
+ }
+
+ /**
+ * When a column is clicked at no entry this function is called.
+ * First the templates generates a new entry at the clicked time. Then this
+ * js-function is called which gets the parameters
+ * "function (new_entry_dom_object, column_id, hour) { ... }"
+ * with new_entry_dom_object: a real dom-object of the div of the entry
+ * column_id: id of the column
+ * hour: integer number from 0 to 23
+ * If js_function_object is an empty string, nothing will be done.
+ *
+ * @param string $js_function_object name of js-function or anonymous js-function
+ * @return CalendarView
+ */
+ public function setInsertFunction($js_function_object)
+ {
+ $this->insertFunction = $js_function_object;
+ return $this;
+ }
+
+ /**
+ * outputs the CalendarView with all (grouped) dates in columns.
+ *
+ * @param array $params you can pass some additional variables to the templates
+ *
+ * @return string
+ */
+ public function render($params = [])
+ {
+ $style_parameters = [
+ 'whole_height' => $this->getOverallHeight(),
+ 'entry_height' => $this->getHeight()
+ ];
+ $factory = new Flexi\Factory(dirname(__file__).'/../../app/views');
+ PageLayout::addStyle($factory->render('calendar/schedule/stylesheet', $style_parameters));
+
+ $template = $GLOBALS['template_factory']->open("calendar/calendar_view.php");
+ $template->set_attribute("calendar_view", $this);
+ $template->set_attribute("view_id", $this->view_id);
+ return $template->render($params);
+ }
+
+
+ /* * * * * * * * * * * * * * *
+ * * * G E T T E R S * * *
+ * * * * * * * * * * * * * * */
+
+ /**
+ * Returns an array of calendar-entries, grouped by day and additionally grouped by same start and end
+ * if groupEntries(true) has been called.
+ *
+ * @return mixed the (double-)grouped entries
+ */
+ public function getEntries()
+ {
+ $this->sorted_entries = [];
+ foreach ($this->getColumns() as $entry_column) {
+ $this->sorted_entries['day_'. $entry_column->getId()] = $entry_column->getGroupedEntries();
+ }
+ return $this->sorted_entries;
+ }
+
+ /**
+ * Returns an array where for each hour the number of concurrent entries is denoted.
+ * Used by the calendar to display the entries in parallel.
+ *
+ * @return mixed concurrent entries at each hour
+ */
+ public function getMatrix()
+ {
+ $matrix = [];
+ foreach ($this->getColumns() as $day => $entry_column) {
+ $matrix['day_'.$day] = $entry_column->getMatrix();
+ }
+ return $matrix;
+ }
+
+
+ /**
+ * returns the previously set start- and end-hour, denoting the
+ * range of entries to be displayed in the current calendar-view
+ *
+ * @return array consisting of the start and end hour
+ */
+ public function getRange()
+ {
+ return [$this->start_hour, $this->end_hour];
+ }
+
+ /**
+ * the calendar can be used in two modes. Use this function to check,
+ * if the grouping-mode is enabled for the current calendar-view
+ *
+ * @return bool true if grouped, false otherwise
+ */
+ public function isGrouped()
+ {
+ return $this->grouped;
+ }
+
+ /**
+ * returns the previously set height for one hour
+ *
+ * @return mixed the height
+ */
+ public function getHeight()
+ {
+ return $this->height;
+ }
+
+ /**
+ * returns the overall height of the calendar
+ *
+ * @return mixed the overall height
+ */
+ public function getOverallHeight()
+ {
+ return $this->height * ($this->end_hour - $this->start_hour) + $this->height
+ + 2 + ($this->end_hour - $this->start_hour) * 2;
+ }
+
+ /**
+ * returns the previously set javasscript insert-function
+ *
+ * @return string name of js-function or anonymous js-function
+ */
+ public function getInsertFunction() {
+ return $this->insertFunction;
+ }
+
+ /**
+ * returns all columns of the calendar-view
+ *
+ * @return array of CalendarColumn
+ */
+ public function getColumns() {
+ return $this->entries;
+ }
+
+ /**
+ * Set the read-only status of the calendar
+ *
+ * @param bool $readonly true to make it read only, false otherwise
+ *
+ * @return void
+ */
+ public function setReadOnly($readonly = true)
+ {
+ $this->read_only = $readonly;
+ }
+
+ /**
+ * Return the read-only status of this calendar
+ *
+ * @return bool the read-only status of this calendar
+ */
+ public function getReadOnly() {
+ return $this->read_only;
+ }
+
+}
diff --git a/lib/calendar/CalendarWeekView.class.php b/lib/calendar/CalendarWeekView.class.php
deleted file mode 100644
index c1e0f24..0000000
--- a/lib/calendar/CalendarWeekView.class.php
+++ /dev/null
@@ -1,124 +0,0 @@
- & Rasmus Fuhse
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-/**
- * Kind of bean class for the calendar view.
- *
- * @since 2.0
- *
- * @deprecated since Stud.IP 5.5
- */
-
-class CalendarWeekView extends CalendarView
-{
- protected $days = [1,2,3,4,5];
- protected $context;
-
-
- /**
- * You need to pass an instance of this class to the template. The constructor
- * expects an array of entries of the following type:
- * array(
- * $day_number => array(array (
- * 'color' => the color in hex (css-like, without the #)
- * 'start' => the (start hour * 100) + (start minute)
- * 'end' => the (end hour * 100) + (end minute)
- * //'day' => day of week (0 = Sunday, ... , 6 = Saturday)
- * 'title' => the entry`s title
- * 'content' => whatever shall be the content of the entry as a string
- * ) ...) ...
- * )
- *
- * @param mixed $entries an array of entries (see above)
- * @param string $controller the name of the controller. Used to create links.
- */
- public function __construct($entries, $controller)
- {
- parent::__construct($entries);
- $this->context = $controller;
- }
-
- /**
- * Call this function th enable/disable the grouping of entries with the same start and end.
- *
- * @param bool $group optional, defaults to true
- */
- public function groupEntries($grouped = true)
- {
- $this->grouped = $grouped;
- foreach($this->entries as $entry_column) {
- $entry_column->groupEntries();
- }
- }
-
- /* * * * * * * * * * * * * * *
- * * * G E T T E R S * * *
- * * * * * * * * * * * * * * */
-
- /**
- * @return mixed the context
- */
- public function getContext()
- {
- return $this->context;
- }
-
- /**
- * @return mixed the days
- */
- public function getDays()
- {
- return $this->days;
- }
-
- /**
- * returns the previously set javasscript insert-function only
- * if read_only is not set.
- *
- * @return string name of js-function or anonymous js-function
- */
- public function getInsertFunction() {
- if (!$this->read_only) {
- return parent::getInsertFunction();
- }
-
- return false;
- }
-
- /**
- * returns all columns of the calendar-view nad removes the url if
- * read_only is set
- *
- * @return array of CalendarColumn
- */
- public function getColumns() {
- // remove links and urls if calendar-view is read-only
- if ($this->read_only) {
- foreach ($this->entries as $column) {
- $column->setURL(false);
- foreach ($column->entries as $key => $entry) {
- unset($column->entries[$key]['url']);
- unset($column->entries[$key]['onClick']);
- unset($column->entries[$key]['icons']);
- }
- }
- }
-
- return parent::getColumns();
- }
-}
diff --git a/lib/calendar/CalendarWeekView.php b/lib/calendar/CalendarWeekView.php
new file mode 100644
index 0000000..c1e0f24
--- /dev/null
+++ b/lib/calendar/CalendarWeekView.php
@@ -0,0 +1,124 @@
+ & Rasmus Fuhse
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+/**
+ * Kind of bean class for the calendar view.
+ *
+ * @since 2.0
+ *
+ * @deprecated since Stud.IP 5.5
+ */
+
+class CalendarWeekView extends CalendarView
+{
+ protected $days = [1,2,3,4,5];
+ protected $context;
+
+
+ /**
+ * You need to pass an instance of this class to the template. The constructor
+ * expects an array of entries of the following type:
+ * array(
+ * $day_number => array(array (
+ * 'color' => the color in hex (css-like, without the #)
+ * 'start' => the (start hour * 100) + (start minute)
+ * 'end' => the (end hour * 100) + (end minute)
+ * //'day' => day of week (0 = Sunday, ... , 6 = Saturday)
+ * 'title' => the entry`s title
+ * 'content' => whatever shall be the content of the entry as a string
+ * ) ...) ...
+ * )
+ *
+ * @param mixed $entries an array of entries (see above)
+ * @param string $controller the name of the controller. Used to create links.
+ */
+ public function __construct($entries, $controller)
+ {
+ parent::__construct($entries);
+ $this->context = $controller;
+ }
+
+ /**
+ * Call this function th enable/disable the grouping of entries with the same start and end.
+ *
+ * @param bool $group optional, defaults to true
+ */
+ public function groupEntries($grouped = true)
+ {
+ $this->grouped = $grouped;
+ foreach($this->entries as $entry_column) {
+ $entry_column->groupEntries();
+ }
+ }
+
+ /* * * * * * * * * * * * * * *
+ * * * G E T T E R S * * *
+ * * * * * * * * * * * * * * */
+
+ /**
+ * @return mixed the context
+ */
+ public function getContext()
+ {
+ return $this->context;
+ }
+
+ /**
+ * @return mixed the days
+ */
+ public function getDays()
+ {
+ return $this->days;
+ }
+
+ /**
+ * returns the previously set javasscript insert-function only
+ * if read_only is not set.
+ *
+ * @return string name of js-function or anonymous js-function
+ */
+ public function getInsertFunction() {
+ if (!$this->read_only) {
+ return parent::getInsertFunction();
+ }
+
+ return false;
+ }
+
+ /**
+ * returns all columns of the calendar-view nad removes the url if
+ * read_only is set
+ *
+ * @return array of CalendarColumn
+ */
+ public function getColumns() {
+ // remove links and urls if calendar-view is read-only
+ if ($this->read_only) {
+ foreach ($this->entries as $column) {
+ $column->setURL(false);
+ foreach ($column->entries as $key => $entry) {
+ unset($column->entries[$key]['url']);
+ unset($column->entries[$key]['onClick']);
+ unset($column->entries[$key]['icons']);
+ }
+ }
+ }
+
+ return parent::getColumns();
+ }
+}
diff --git a/lib/classes/AdminCourseFilter.class.php b/lib/classes/AdminCourseFilter.class.php
deleted file mode 100644
index f643e09..0000000
--- a/lib/classes/AdminCourseFilter.class.php
+++ /dev/null
@@ -1,220 +0,0 @@
-cfg->getValue("LECTURESHIP_FILTER")) {
- * $filter->query->join('lehrauftrag', 'seminare.Seminar_id = lehrauftrag.seminar_id');
- * }
- * }
- *
- * Within this method you alter the public $filter->query object. That query object is of type SQLQuery.
- *
- */
-class AdminCourseFilter
-{
- static protected $instance = null;
-
- /** @var SQLQuery|null */
- public $query = null;
- public $max_show_courses = 500;
- public $settings = [];
-
- /**
- * returns an AdminCourseFilter singleton object
- * @return AdminCourseFilter or derived-class object
- */
- static public function get($reset_settings = false)
- {
- if (!self::$instance) {
- $class = get_called_class();
- self::$instance = new $class($reset_settings);
- }
- return self::$instance;
- }
-
- /**
- * Constructor of the singleton-object.
- */
- public function __construct()
- {
- $this->initSettings();
- }
-
- protected function initSettings()
- {
- $this->query = SQLQuery::table('seminare');
- $this->query->join('sem_types', 'sem_types', 'sem_types.id = seminare.status');
- $this->query->join('sem_classes', 'sem_classes', 'sem_classes.id = sem_types.class');
- $this->query->where("sem_classes.studygroup_mode = '0'");
- $this->query->groupBy('seminare.Seminar_id');
-
- if ($GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT) {
- $this->query->join('teachers_su', 'seminar_user', "teachers_su.Seminar_id = seminare.Seminar_id AND teachers_su.status = 'dozent'");
- $this->query->join('teachers', 'auth_user_md5', 'teachers.user_id = teachers_su.user_id');
- $this->query->where(
- 'search',
- "(seminare.name LIKE :search OR seminare.VeranstaltungsNummer LIKE :search OR seminare.untertitel LIKE :search OR CONCAT(teachers.Vorname, ' ', teachers.Nachname) LIKE :search)",
- ['search' => '%'.$GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT.'%']
- );
- }
- if (Request::option('course_id')) {
- $this->query->where('course_id', 'seminare.Seminar_id = :course_id', ['course_id' => Request::option('course_id')]);
- }
- $inst_ids = [];
-
- if (
- !$GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT
- || $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === 'all'
- ) {
- $inst = new SimpleCollection(Institute::getMyInstitutes($GLOBALS['user']->id));
- $inst_ids = $inst->map(function ($a) {
- return $a['Institut_id'];
- });
- } else {
-
- $inst_id = $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT;
- $inst_ids[] = $inst_id;
-
- if ($GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN) {
- $inst = Institute::find($inst_id);
- if ($inst && $inst->isFaculty()) {
- foreach ($inst->sub_institutes->pluck('Institut_id') as $institut_id) {
- $inst_ids[] = $institut_id;
- }
- }
- }
- }
-
- if (Config::get()->ALLOW_ADMIN_RELATED_INST) {
- $this->query->where('seminar_inst', 'EXISTS (SELECT 1 FROM seminar_inst WHERE seminar_id = seminare.Seminar_id AND institut_id IN (:institut_ids))');
- } else {
- $this->query->where("seminar_inst", "seminare.institut_id IN (:institut_ids)");
- }
- $this->query->parameter('institut_ids', $inst_ids);
-
- if ($GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE) {
- $this->query->join('semester_courses', 'semester_courses.course_id = seminare.Seminar_id', 'LEFT JOIN');
- $this->query->where('semester_id', '(semester_courses.semester_id = :semester_id OR semester_courses.semester_id IS NULL)', [
- 'semester_id' => $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE
- ]);
- }
-
- if ($GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER && $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER !== 'all') {
- if (str_contains($GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER, '_')) {
- list($sem_class_id, $sem_type_id) = explode('_', $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER);
- $this->query->where('course_type', 'seminare.status = :course_type', ['course_type' => $sem_type_id]);
- } else {
- //sem class
- $this->query->where('course_class', 'sem_types.class = :course_class', [
- 'course_class' => $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER
- ]);
- }
-
- }
-
- if ($GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL) {
- $this->query->join('mvv_lvgruppe_seminar', '`mvv_lvgruppe_seminar`.`seminar_id` = `seminare`.`Seminar_id`');
- $this->query->join('mvv_lvgruppe_modulteil', '`mvv_lvgruppe_modulteil`.`lvgruppe_id` = `mvv_lvgruppe_seminar`.`lvgruppe_id`');
- $this->query->join('mvv_modulteil', '`mvv_modulteil`.`modulteil_id` = `mvv_lvgruppe_modulteil`.`modulteil_id`');
- $this->query->join('mvv_modul', '`mvv_modul`.`modul_id` = `mvv_modulteil`.`modul_id`');
- $this->query->join('mvv_stgteilabschnitt_modul', '`mvv_stgteilabschnitt_modul`.`modul_id` = `mvv_modul`.`modul_id`');
- $this->query->join('mvv_stgteilabschnitt', '`mvv_stgteilabschnitt`.`abschnitt_id` = `mvv_stgteilabschnitt_modul`.`abschnitt_id`');
- $this->query->join('mvv_stgteilversion', '`mvv_stgteilversion`.`version_id` = `mvv_stgteilabschnitt`.`version_id`');
- $this->query->where('stgteil', 'mvv_stgteilversion.stgteil_id = :stgteil_id', [
- 'stgteil_id' => $GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL
- ]);
- }
-
- if ($GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER) {
- $this->query->join('teachers_su', 'seminar_user', "teachers_su.Seminar_id = seminare.Seminar_id AND teachers_su.status = 'dozent'");
- $this->query->where(
- 'teacher_filter',
- "teachers_su.user_id = :teacher_id",
- ['teacher_id' => $GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER]
- );
- }
-
-
-
- $datafields_filters = $GLOBALS['user']->cfg->ADMIN_COURSES_DATAFIELDS_FILTERS;
- foreach ($datafields_filters as $datafield_id => $value) {
- $this->query->join('de_'.$datafield_id, 'datafields_entries', 'de_'.$datafield_id.'.range_id = seminare.Seminar_id AND `de_'.$datafield_id.'`.datafield_id = :de_'.$datafield_id.'_id');
- $this->query->where('de_' . $datafield_id . '_contents', 'de_' . $datafield_id . '.`content` LIKE :de_' . $datafield_id . '_content',
- [
- 'de_' . $datafield_id . '_id' => $datafield_id,
- 'de_' . $datafield_id . '_content' => '%' . $value . '%'
- ]);
- }
- }
-
- /**
- * Returns the data of the resultset of the AdminCourseFilter.
- * Also saves the settings in the session.
- * Note that a notification AdminCourseFilterWillQuery will be posted, before the result is computed.
- * Plugins may register at this event to fully alter this AdminCourseFilter-object and so the resultset.
- * @return array associative array with seminar_ids as keys and seminar-data-arrays as values.
- */
- public function getCourses()
- {
- NotificationCenter::postNotification("AdminCourseFilterWillQuery", $this);
- return $this->query->fetchAll(Course::class);
- }
-
- /**
- * @return integer number of courses that this filter would return
- */
- public function countCourses()
- {
- NotificationCenter::postNotification("AdminCourseFilterWillQuery", $this);
- return $this->query->count();
- }
-
- /**
- * @param int|null $limit
- * @return Course[]
- */
- public function fetchCourses(?int $limit = null): array
- {
- NotificationCenter::postNotification('AdminCourseFilterWillQuery', $this);
- return $this->query->fetchAll(Course::class, $limit);
- }
-
- /**
- * Returns the data of the resultset of the AdminCourseFilter.
- *
- * @param string $order_by possible values name or number
- *
- * Note that a notification AdminCourseFilterWillQuery will be posted, before the result is computed.
- * Plugins may register at this event to fully alter this AdminCourseFilter-object and so the resultset.
- * @return array associative array with seminar_ids as keys and seminar-data-arrays as values.
- */
- public function getCoursesForAdminWidget(string $order_by = 'name')
- {
- try {
- $order = 'seminare.name';
- if ($order_by === 'number') {
- $order = 'seminare.veranstaltungsnummer, seminare.name';
- }
- $this->query->orderBy($order);
- return $this->fetchCourses($this->max_show_courses);
- } catch (OverflowException $e) {
- return [];
- }
- }
-
-}
diff --git a/lib/classes/AdminCourseFilter.php b/lib/classes/AdminCourseFilter.php
new file mode 100644
index 0000000..f643e09
--- /dev/null
+++ b/lib/classes/AdminCourseFilter.php
@@ -0,0 +1,220 @@
+cfg->getValue("LECTURESHIP_FILTER")) {
+ * $filter->query->join('lehrauftrag', 'seminare.Seminar_id = lehrauftrag.seminar_id');
+ * }
+ * }
+ *
+ * Within this method you alter the public $filter->query object. That query object is of type SQLQuery.
+ *
+ */
+class AdminCourseFilter
+{
+ static protected $instance = null;
+
+ /** @var SQLQuery|null */
+ public $query = null;
+ public $max_show_courses = 500;
+ public $settings = [];
+
+ /**
+ * returns an AdminCourseFilter singleton object
+ * @return AdminCourseFilter or derived-class object
+ */
+ static public function get($reset_settings = false)
+ {
+ if (!self::$instance) {
+ $class = get_called_class();
+ self::$instance = new $class($reset_settings);
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Constructor of the singleton-object.
+ */
+ public function __construct()
+ {
+ $this->initSettings();
+ }
+
+ protected function initSettings()
+ {
+ $this->query = SQLQuery::table('seminare');
+ $this->query->join('sem_types', 'sem_types', 'sem_types.id = seminare.status');
+ $this->query->join('sem_classes', 'sem_classes', 'sem_classes.id = sem_types.class');
+ $this->query->where("sem_classes.studygroup_mode = '0'");
+ $this->query->groupBy('seminare.Seminar_id');
+
+ if ($GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT) {
+ $this->query->join('teachers_su', 'seminar_user', "teachers_su.Seminar_id = seminare.Seminar_id AND teachers_su.status = 'dozent'");
+ $this->query->join('teachers', 'auth_user_md5', 'teachers.user_id = teachers_su.user_id');
+ $this->query->where(
+ 'search',
+ "(seminare.name LIKE :search OR seminare.VeranstaltungsNummer LIKE :search OR seminare.untertitel LIKE :search OR CONCAT(teachers.Vorname, ' ', teachers.Nachname) LIKE :search)",
+ ['search' => '%'.$GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT.'%']
+ );
+ }
+ if (Request::option('course_id')) {
+ $this->query->where('course_id', 'seminare.Seminar_id = :course_id', ['course_id' => Request::option('course_id')]);
+ }
+ $inst_ids = [];
+
+ if (
+ !$GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT
+ || $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === 'all'
+ ) {
+ $inst = new SimpleCollection(Institute::getMyInstitutes($GLOBALS['user']->id));
+ $inst_ids = $inst->map(function ($a) {
+ return $a['Institut_id'];
+ });
+ } else {
+
+ $inst_id = $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT;
+ $inst_ids[] = $inst_id;
+
+ if ($GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN) {
+ $inst = Institute::find($inst_id);
+ if ($inst && $inst->isFaculty()) {
+ foreach ($inst->sub_institutes->pluck('Institut_id') as $institut_id) {
+ $inst_ids[] = $institut_id;
+ }
+ }
+ }
+ }
+
+ if (Config::get()->ALLOW_ADMIN_RELATED_INST) {
+ $this->query->where('seminar_inst', 'EXISTS (SELECT 1 FROM seminar_inst WHERE seminar_id = seminare.Seminar_id AND institut_id IN (:institut_ids))');
+ } else {
+ $this->query->where("seminar_inst", "seminare.institut_id IN (:institut_ids)");
+ }
+ $this->query->parameter('institut_ids', $inst_ids);
+
+ if ($GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE) {
+ $this->query->join('semester_courses', 'semester_courses.course_id = seminare.Seminar_id', 'LEFT JOIN');
+ $this->query->where('semester_id', '(semester_courses.semester_id = :semester_id OR semester_courses.semester_id IS NULL)', [
+ 'semester_id' => $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE
+ ]);
+ }
+
+ if ($GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER && $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER !== 'all') {
+ if (str_contains($GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER, '_')) {
+ list($sem_class_id, $sem_type_id) = explode('_', $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER);
+ $this->query->where('course_type', 'seminare.status = :course_type', ['course_type' => $sem_type_id]);
+ } else {
+ //sem class
+ $this->query->where('course_class', 'sem_types.class = :course_class', [
+ 'course_class' => $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER
+ ]);
+ }
+
+ }
+
+ if ($GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL) {
+ $this->query->join('mvv_lvgruppe_seminar', '`mvv_lvgruppe_seminar`.`seminar_id` = `seminare`.`Seminar_id`');
+ $this->query->join('mvv_lvgruppe_modulteil', '`mvv_lvgruppe_modulteil`.`lvgruppe_id` = `mvv_lvgruppe_seminar`.`lvgruppe_id`');
+ $this->query->join('mvv_modulteil', '`mvv_modulteil`.`modulteil_id` = `mvv_lvgruppe_modulteil`.`modulteil_id`');
+ $this->query->join('mvv_modul', '`mvv_modul`.`modul_id` = `mvv_modulteil`.`modul_id`');
+ $this->query->join('mvv_stgteilabschnitt_modul', '`mvv_stgteilabschnitt_modul`.`modul_id` = `mvv_modul`.`modul_id`');
+ $this->query->join('mvv_stgteilabschnitt', '`mvv_stgteilabschnitt`.`abschnitt_id` = `mvv_stgteilabschnitt_modul`.`abschnitt_id`');
+ $this->query->join('mvv_stgteilversion', '`mvv_stgteilversion`.`version_id` = `mvv_stgteilabschnitt`.`version_id`');
+ $this->query->where('stgteil', 'mvv_stgteilversion.stgteil_id = :stgteil_id', [
+ 'stgteil_id' => $GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL
+ ]);
+ }
+
+ if ($GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER) {
+ $this->query->join('teachers_su', 'seminar_user', "teachers_su.Seminar_id = seminare.Seminar_id AND teachers_su.status = 'dozent'");
+ $this->query->where(
+ 'teacher_filter',
+ "teachers_su.user_id = :teacher_id",
+ ['teacher_id' => $GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER]
+ );
+ }
+
+
+
+ $datafields_filters = $GLOBALS['user']->cfg->ADMIN_COURSES_DATAFIELDS_FILTERS;
+ foreach ($datafields_filters as $datafield_id => $value) {
+ $this->query->join('de_'.$datafield_id, 'datafields_entries', 'de_'.$datafield_id.'.range_id = seminare.Seminar_id AND `de_'.$datafield_id.'`.datafield_id = :de_'.$datafield_id.'_id');
+ $this->query->where('de_' . $datafield_id . '_contents', 'de_' . $datafield_id . '.`content` LIKE :de_' . $datafield_id . '_content',
+ [
+ 'de_' . $datafield_id . '_id' => $datafield_id,
+ 'de_' . $datafield_id . '_content' => '%' . $value . '%'
+ ]);
+ }
+ }
+
+ /**
+ * Returns the data of the resultset of the AdminCourseFilter.
+ * Also saves the settings in the session.
+ * Note that a notification AdminCourseFilterWillQuery will be posted, before the result is computed.
+ * Plugins may register at this event to fully alter this AdminCourseFilter-object and so the resultset.
+ * @return array associative array with seminar_ids as keys and seminar-data-arrays as values.
+ */
+ public function getCourses()
+ {
+ NotificationCenter::postNotification("AdminCourseFilterWillQuery", $this);
+ return $this->query->fetchAll(Course::class);
+ }
+
+ /**
+ * @return integer number of courses that this filter would return
+ */
+ public function countCourses()
+ {
+ NotificationCenter::postNotification("AdminCourseFilterWillQuery", $this);
+ return $this->query->count();
+ }
+
+ /**
+ * @param int|null $limit
+ * @return Course[]
+ */
+ public function fetchCourses(?int $limit = null): array
+ {
+ NotificationCenter::postNotification('AdminCourseFilterWillQuery', $this);
+ return $this->query->fetchAll(Course::class, $limit);
+ }
+
+ /**
+ * Returns the data of the resultset of the AdminCourseFilter.
+ *
+ * @param string $order_by possible values name or number
+ *
+ * Note that a notification AdminCourseFilterWillQuery will be posted, before the result is computed.
+ * Plugins may register at this event to fully alter this AdminCourseFilter-object and so the resultset.
+ * @return array associative array with seminar_ids as keys and seminar-data-arrays as values.
+ */
+ public function getCoursesForAdminWidget(string $order_by = 'name')
+ {
+ try {
+ $order = 'seminare.name';
+ if ($order_by === 'number') {
+ $order = 'seminare.veranstaltungsnummer, seminare.name';
+ }
+ $this->query->orderBy($order);
+ return $this->fetchCourses($this->max_show_courses);
+ } catch (OverflowException $e) {
+ return [];
+ }
+ }
+
+}
diff --git a/lib/classes/Assets.class.php b/lib/classes/Assets.class.php
deleted file mode 100644
index 928c47c..0000000
--- a/lib/classes/Assets.class.php
+++ /dev/null
@@ -1,439 +0,0 @@
-
- *
- * 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.
- */
-
-
-/**
- * This class is used to construct URLs for static content like images,
- * stylesheets or javascripts. As the URL to the "assets" directory is
- * configurable one always has to construct the above mentioned URLs
- * dynamically.
- *
- * Example:
- *
- * # construct the URL for the image "blank.gif"
- * $url = Assets::image_path('blank.gif');
- *
- * @package studip
- *
- * @author mlunzena
- * @copyright (c) Authors
- */
-class Assets
-{
-
- const NUMBER_OF_ALIASES = 2;
-
- /**
- * @ignore
- */
- private static $assets_url;
- private static $assets_path;
- private static $dynamic;
- private static $counter_cache;
-
- /**
- * This method sets the URL to your assets.
- *
- * @param string $path the path to the assets
- */
- public static function set_assets_path(string $path): void
- {
- self::$assets_path = $path;
- }
-
- /**
- * This method sets the URL to your assets.
- *
- * @param string the URL to the assets
- *
- * @return void
- */
- public static function set_assets_url($url)
- {
- self::$assets_url = $url;
- self::$counter_cache = NULL;
- self::$dynamic = mb_strpos($url, '%d') !== FALSE;
- }
-
- /**
- * This class method is an accessor to the URL "prefix" for all things "asset"
- * Prepend the return value of this method to the relative path of the wanted
- * static content.
- *
- * Additionally if the ASSETS_URL contains the string '%d', it will be
- * replaced with a random number between 0 and 3. If you passed an argument
- * this number will not be random but specific to that argument thus being
- * referentially transparent.
- *
- * Example:
- *
- * # static ASSETS_URL
- * $ASSETS_URL = 'http://www.example.com/public/';
- * echo Assets::url() . 'javascripts/prototype.js' . "\n";
- * echo Assets::url('javascripts/prototype.js') . "\n";
- *
- * # output
- * http://www.example.com/public/javascripts/prototype.js
- * http://www.example.com/public/javascripts/prototype.js
- *
- *
- * # dynamic ASSETS_URL
- * $ASSETS_URL = 'http://www%d.example.com/public/';
- * echo Assets::url() . 'javascripts/prototype.js' . "\n";
- * echo Assets::url() . 'javascripts/prototype.js' . "\n";
- * echo Assets::url() . 'javascripts/prototype.js' . "\n";
- * echo Assets::url('javascripts/prototype.js') . "\n";
- * echo Assets::url('javascripts/prototype.js') . "\n";
- * echo Assets::url('javascripts/prototype.js') . "\n";
- *
- * # output
- * http://www0.example.com/public/javascripts/prototype.js
- * http://www1.example.com/public/javascripts/prototype.js
- * http://www2.example.com/public/javascripts/prototype.js
- * http://www1.example.com/public/javascripts/prototype.js
- * http://www1.example.com/public/javascripts/prototype.js
- * http://www1.example.com/public/javascripts/prototype.js
- *
- *
- * @param string an optional suffix which is used to construct a number if
- * ASSETS_URL is dynamic (contains '%d')
- *
- * @return string the URL "prefix"
- */
- public static function url($to = '')
- {
- if (!self::$dynamic) {
- return self::$assets_url . $to;
- }
-
- # dynamic ASSETS_URL
- return sprintf(self::$assets_url,
- $to == ''
- ? self::$counter_cache++ % self::NUMBER_OF_ALIASES
- # alternative implementation
- # : hexdec(mb_substr(sha1($to),-1)) & 3)
- : ord($to[1]) & (self::NUMBER_OF_ALIASES - 1))
-
- . $to;
- }
-
- /**
- * This class method is an accessor to the path "prefix" for all things "asset"
- */
- public static function path($to = ''): string
- {
- return self::$assets_path . $to;
- }
-
- /**
- * Returns an image tag using options as html attributes on the
- * tag, but with these special cases:
- *
- * 'alt' - If no alt text is given, the file name part of the $source is used
- * (capitalized and without the extension)
- * * 'size' - Supplied as "X@Y", so "30@45" becomes width="30" and height="45"
- *
- * The source can be supplied as a...
- * * full path, like "/my_images/image.gif"
- * * file name, like "rss.png", that gets expanded to "/images/rss.png"
- * * file name without extension, like "logo", that gets expanded to "/images/logo.png"
- *
- * Do not use this to render icons. Use the more appropiate class
- * Icon for this.
- */
- public static function img($source, $opt = [])
- {
- if (!$source) {
- return '';
- }
-
- $size = $opt['size'] ?? null;
-
- $opt = self::parse_attributes($opt);
-
- $opt['src'] = self::image_path($source);
-
- if (!isset($opt['alt'])) {
- $opt['alt'] = ucfirst(current(explode('.', basename($opt['src']))));
- }
-
- if (isset($size) && !isset($opt['width'])) {
- $size = explode('@', $size, 2);
- $opt['width'] = $size[0];
- $opt['height'] = $size[1] ?? null;
-
- unset($opt['size']);
- }
-
- return self::tag('img', $opt);
- }
-
-
- /**
- * Returns an input tag using options as html attributes on the
- * tag, but with these special cases:
- *
- * * 'size' - Supplied as "X@Y", so "30@45" becomes width="30" and height="45"
- *
- * The source can be supplied as a...
- * * full path, like "/my_images/image.gif"
- * * file name, like "rss.png", that gets expanded to "/images/rss.png"
- * * file name without extension, like "logo", that gets expanded to "/images/logo.png"
- *
- * Do not use this to render icons. Use the more appropiate class
- * Icon for this.
- */
- public static function input($source, $opt = [])
- {
-
- if (!$source) {
- return '';
- }
-
- $parts = explode('/', $source);
-
- $size = $opt['size'];
-
- $opt = self::parse_attributes($opt);
-
- $opt['src'] = self::image_path($source);
- $opt['type'] = 'image';
-
- if (isset($size) && !isset($opt['width'])) {
- [$opt['width'], $opt['height']] = explode('@', $size, 2);
- unset($opt['size']);
- }
-
- return self::tag('input', $opt);
- }
-
- /**
- * Returns path to an image asset.
- *
- * Example:
- *
- * The src can be supplied as a...
- *
- * full path,
- * like "/my_images/image.gif"
- *
- * file name,
- * like "rss.png", that gets expanded to "/images/rss.png"
- *
- * file name without extension,
- * like "logo", that gets expanded to "/images/logo.png"
- *
- * Note: This function should be private/depracated for the use in other
- * scripts, as we would like to always generate the complete oder
- * tag. Please use Assets::img or Assets::input instead.
- */
- public static function image_path($source, $respect_retina = false)
- {
- $path = self::compute_public_path($source, 'images', 'png');
-
- return $path;
- }
-
- /**
- * Returns a script include tag per source given as argument.
- *
- * Examples:
- *
- * Assets::script('prototype') =>
- *
- *
- * Assets::script('common.javascript', '/elsewhere/cools') =>
- *
- *
- */
- public static function script($atLeastOneArgument)
- {
- $html = '';
- foreach (func_get_args() as $source) {
- $source = self::javascript_path($source);
- $html .= self::content_tag('script', '',
- ['src' => $source]);
- $html .= "\n";
- }
-
- return $html;
- }
-
-
- /**
- * Returns path to a javascript asset.
- *
- * Example:
- *
- * Assets::javascript_path('ajax') => /javascripts/ajax.js
- */
- public static function javascript_path($source)
- {
- return self::compute_public_path($source, 'javascripts', 'js');
- }
-
-
- /**
- * Returns a css link tag per source given as argument.
- *
- * Examples:
- *
- * Assets::stylesheet('style') =>
- *
- *
- * Assets::stylesheet('style', array('media' => 'all')) =>
- *
- *
- * Assets::stylesheet('random.styles', '/css/stylish') =>
- *
- *
- */
- public static function stylesheet($atLeastOneArgument)
- {
- $sources = func_get_args();
- $sourceOptions = (func_num_args() > 1 &&
- is_array($sources[func_num_args() - 1]))
- ? array_pop($sources)
- : [];
-
- $html = '';
- foreach ($sources as $source) {
- $source = self::stylesheet_path($source);
- $opt = array_merge(['rel' => 'stylesheet',
- 'media' => 'screen',
- 'href' => $source],
- $sourceOptions);
- $html .= self::tag('link', $opt) . "\n";
- }
-
- return $html;
- }
-
-
- /**
- * Returns path to a stylesheet asset.
- *
- * Example:
- *
- * stylesheet_path('style') => /stylesheets/style.css
- */
- public static function stylesheet_path($source)
- {
- return self::compute_public_path($source, 'stylesheets', 'css');
- }
-
-
- /**
- * This function computes the public path to the given source by using default
- * dir and ext if not specified by the source. If source is not an absolute
- * URL, the assets url is incorporated.
- *
- * @ignore
- */
- private static function compute_public_path($source, $dir, $ext)
- {
-
- # add extension if not present
- if ('' == mb_substr(mb_strrchr($source, "."), 1))
- $source .= ".$ext";
-
- # if source is not absolute
- if (FALSE === mb_strpos($source, ':')) {
-
- # add dir if url does not contain a path
- if ('/' !== $source[0])
- $source = "$dir/$source";
-
- # consider asset host
- $source = self::url(ltrim($source, '/'));
- }
-
- return $source;
- }
-
-
- /**
- * Constructs an html tag.
- *
- * @ignore
- *
- * @param string tag name
- * @param array tag options
- * @param boolean true to leave tag open
- *
- * @return string
- */
- private static function tag($name, $options = [], $open = FALSE)
- {
- if (!$name) {
- return '';
- }
-
- ksort($options);
- return '<' . $name . ' ' . arrayToHtmlAttributes($options) . ($open ? '>' : '>');
- }
-
-
- /**
- * Helper function for content tags.
- *
- * @param string $name tag name
- * @param string $content tag content
- * @param array $options tag options
- *
- * @return string
- */
- private static function content_tag($name, $content = '', $options = [])
- {
- if (!$name) {
- return '';
- }
- return '<' . $name . ' ' . arrayToHtmlAttributes($options) . '>' .
- $content .
- '' . $name . '>';
- }
-
- /**
- * Parse a HTML attribute string into an array.
- *
- * @ignore
- */
- private static function parse_attributes($stringOrArray)
- {
-
- if (is_array($stringOrArray))
- return $stringOrArray;
-
- preg_match_all('/
- \s*(\w+) # key \\1
- \s*=\s* # =
- (\'|")? # values may be included in \' or " \\2
- (.*?) # value \\3
- (?(2) \\2) # matching \' or " if needed \\4
- \s*(?:
- (?=\w+\s*=) | \s*$ # followed by another key= or the end of the string
- )
- /x', $stringOrArray, $matches, PREG_SET_ORDER);
-
- $attributes = [];
- foreach ($matches as $val)
- $attributes[$val[1]] = $val[3];
-
- return $attributes;
- }
-}
diff --git a/lib/classes/Assets.php b/lib/classes/Assets.php
new file mode 100644
index 0000000..928c47c
--- /dev/null
+++ b/lib/classes/Assets.php
@@ -0,0 +1,439 @@
+
+ *
+ * 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.
+ */
+
+
+/**
+ * This class is used to construct URLs for static content like images,
+ * stylesheets or javascripts. As the URL to the "assets" directory is
+ * configurable one always has to construct the above mentioned URLs
+ * dynamically.
+ *
+ * Example:
+ *
+ * # construct the URL for the image "blank.gif"
+ * $url = Assets::image_path('blank.gif');
+ *
+ * @package studip
+ *
+ * @author mlunzena
+ * @copyright (c) Authors
+ */
+class Assets
+{
+
+ const NUMBER_OF_ALIASES = 2;
+
+ /**
+ * @ignore
+ */
+ private static $assets_url;
+ private static $assets_path;
+ private static $dynamic;
+ private static $counter_cache;
+
+ /**
+ * This method sets the URL to your assets.
+ *
+ * @param string $path the path to the assets
+ */
+ public static function set_assets_path(string $path): void
+ {
+ self::$assets_path = $path;
+ }
+
+ /**
+ * This method sets the URL to your assets.
+ *
+ * @param string the URL to the assets
+ *
+ * @return void
+ */
+ public static function set_assets_url($url)
+ {
+ self::$assets_url = $url;
+ self::$counter_cache = NULL;
+ self::$dynamic = mb_strpos($url, '%d') !== FALSE;
+ }
+
+ /**
+ * This class method is an accessor to the URL "prefix" for all things "asset"
+ * Prepend the return value of this method to the relative path of the wanted
+ * static content.
+ *
+ * Additionally if the ASSETS_URL contains the string '%d', it will be
+ * replaced with a random number between 0 and 3. If you passed an argument
+ * this number will not be random but specific to that argument thus being
+ * referentially transparent.
+ *
+ * Example:
+ *
+ * # static ASSETS_URL
+ * $ASSETS_URL = 'http://www.example.com/public/';
+ * echo Assets::url() . 'javascripts/prototype.js' . "\n";
+ * echo Assets::url('javascripts/prototype.js') . "\n";
+ *
+ * # output
+ * http://www.example.com/public/javascripts/prototype.js
+ * http://www.example.com/public/javascripts/prototype.js
+ *
+ *
+ * # dynamic ASSETS_URL
+ * $ASSETS_URL = 'http://www%d.example.com/public/';
+ * echo Assets::url() . 'javascripts/prototype.js' . "\n";
+ * echo Assets::url() . 'javascripts/prototype.js' . "\n";
+ * echo Assets::url() . 'javascripts/prototype.js' . "\n";
+ * echo Assets::url('javascripts/prototype.js') . "\n";
+ * echo Assets::url('javascripts/prototype.js') . "\n";
+ * echo Assets::url('javascripts/prototype.js') . "\n";
+ *
+ * # output
+ * http://www0.example.com/public/javascripts/prototype.js
+ * http://www1.example.com/public/javascripts/prototype.js
+ * http://www2.example.com/public/javascripts/prototype.js
+ * http://www1.example.com/public/javascripts/prototype.js
+ * http://www1.example.com/public/javascripts/prototype.js
+ * http://www1.example.com/public/javascripts/prototype.js
+ *
+ *
+ * @param string an optional suffix which is used to construct a number if
+ * ASSETS_URL is dynamic (contains '%d')
+ *
+ * @return string the URL "prefix"
+ */
+ public static function url($to = '')
+ {
+ if (!self::$dynamic) {
+ return self::$assets_url . $to;
+ }
+
+ # dynamic ASSETS_URL
+ return sprintf(self::$assets_url,
+ $to == ''
+ ? self::$counter_cache++ % self::NUMBER_OF_ALIASES
+ # alternative implementation
+ # : hexdec(mb_substr(sha1($to),-1)) & 3)
+ : ord($to[1]) & (self::NUMBER_OF_ALIASES - 1))
+
+ . $to;
+ }
+
+ /**
+ * This class method is an accessor to the path "prefix" for all things "asset"
+ */
+ public static function path($to = ''): string
+ {
+ return self::$assets_path . $to;
+ }
+
+ /**
+ * Returns an image tag using options as html attributes on the
+ * tag, but with these special cases:
+ *
+ * 'alt' - If no alt text is given, the file name part of the $source is used
+ * (capitalized and without the extension)
+ * * 'size' - Supplied as "X@Y", so "30@45" becomes width="30" and height="45"
+ *
+ * The source can be supplied as a...
+ * * full path, like "/my_images/image.gif"
+ * * file name, like "rss.png", that gets expanded to "/images/rss.png"
+ * * file name without extension, like "logo", that gets expanded to "/images/logo.png"
+ *
+ * Do not use this to render icons. Use the more appropiate class
+ * Icon for this.
+ */
+ public static function img($source, $opt = [])
+ {
+ if (!$source) {
+ return '';
+ }
+
+ $size = $opt['size'] ?? null;
+
+ $opt = self::parse_attributes($opt);
+
+ $opt['src'] = self::image_path($source);
+
+ if (!isset($opt['alt'])) {
+ $opt['alt'] = ucfirst(current(explode('.', basename($opt['src']))));
+ }
+
+ if (isset($size) && !isset($opt['width'])) {
+ $size = explode('@', $size, 2);
+ $opt['width'] = $size[0];
+ $opt['height'] = $size[1] ?? null;
+
+ unset($opt['size']);
+ }
+
+ return self::tag('img', $opt);
+ }
+
+
+ /**
+ * Returns an input tag using options as html attributes on the
+ * tag, but with these special cases:
+ *
+ * * 'size' - Supplied as "X@Y", so "30@45" becomes width="30" and height="45"
+ *
+ * The source can be supplied as a...
+ * * full path, like "/my_images/image.gif"
+ * * file name, like "rss.png", that gets expanded to "/images/rss.png"
+ * * file name without extension, like "logo", that gets expanded to "/images/logo.png"
+ *
+ * Do not use this to render icons. Use the more appropiate class
+ * Icon for this.
+ */
+ public static function input($source, $opt = [])
+ {
+
+ if (!$source) {
+ return '';
+ }
+
+ $parts = explode('/', $source);
+
+ $size = $opt['size'];
+
+ $opt = self::parse_attributes($opt);
+
+ $opt['src'] = self::image_path($source);
+ $opt['type'] = 'image';
+
+ if (isset($size) && !isset($opt['width'])) {
+ [$opt['width'], $opt['height']] = explode('@', $size, 2);
+ unset($opt['size']);
+ }
+
+ return self::tag('input', $opt);
+ }
+
+ /**
+ * Returns path to an image asset.
+ *
+ * Example:
+ *
+ * The src can be supplied as a...
+ *
+ * full path,
+ * like "/my_images/image.gif"
+ *
+ * file name,
+ * like "rss.png", that gets expanded to "/images/rss.png"
+ *
+ * file name without extension,
+ * like "logo", that gets expanded to "/images/logo.png"
+ *
+ * Note: This function should be private/depracated for the use in other
+ * scripts, as we would like to always generate the complete oder
+ * tag. Please use Assets::img or Assets::input instead.
+ */
+ public static function image_path($source, $respect_retina = false)
+ {
+ $path = self::compute_public_path($source, 'images', 'png');
+
+ return $path;
+ }
+
+ /**
+ * Returns a script include tag per source given as argument.
+ *
+ * Examples:
+ *
+ * Assets::script('prototype') =>
+ *
+ *
+ * Assets::script('common.javascript', '/elsewhere/cools') =>
+ *
+ *
+ */
+ public static function script($atLeastOneArgument)
+ {
+ $html = '';
+ foreach (func_get_args() as $source) {
+ $source = self::javascript_path($source);
+ $html .= self::content_tag('script', '',
+ ['src' => $source]);
+ $html .= "\n";
+ }
+
+ return $html;
+ }
+
+
+ /**
+ * Returns path to a javascript asset.
+ *
+ * Example:
+ *
+ * Assets::javascript_path('ajax') => /javascripts/ajax.js
+ */
+ public static function javascript_path($source)
+ {
+ return self::compute_public_path($source, 'javascripts', 'js');
+ }
+
+
+ /**
+ * Returns a css link tag per source given as argument.
+ *
+ * Examples:
+ *
+ * Assets::stylesheet('style') =>
+ *
+ *
+ * Assets::stylesheet('style', array('media' => 'all')) =>
+ *
+ *
+ * Assets::stylesheet('random.styles', '/css/stylish') =>
+ *
+ *
+ */
+ public static function stylesheet($atLeastOneArgument)
+ {
+ $sources = func_get_args();
+ $sourceOptions = (func_num_args() > 1 &&
+ is_array($sources[func_num_args() - 1]))
+ ? array_pop($sources)
+ : [];
+
+ $html = '';
+ foreach ($sources as $source) {
+ $source = self::stylesheet_path($source);
+ $opt = array_merge(['rel' => 'stylesheet',
+ 'media' => 'screen',
+ 'href' => $source],
+ $sourceOptions);
+ $html .= self::tag('link', $opt) . "\n";
+ }
+
+ return $html;
+ }
+
+
+ /**
+ * Returns path to a stylesheet asset.
+ *
+ * Example:
+ *
+ * stylesheet_path('style') => /stylesheets/style.css
+ */
+ public static function stylesheet_path($source)
+ {
+ return self::compute_public_path($source, 'stylesheets', 'css');
+ }
+
+
+ /**
+ * This function computes the public path to the given source by using default
+ * dir and ext if not specified by the source. If source is not an absolute
+ * URL, the assets url is incorporated.
+ *
+ * @ignore
+ */
+ private static function compute_public_path($source, $dir, $ext)
+ {
+
+ # add extension if not present
+ if ('' == mb_substr(mb_strrchr($source, "."), 1))
+ $source .= ".$ext";
+
+ # if source is not absolute
+ if (FALSE === mb_strpos($source, ':')) {
+
+ # add dir if url does not contain a path
+ if ('/' !== $source[0])
+ $source = "$dir/$source";
+
+ # consider asset host
+ $source = self::url(ltrim($source, '/'));
+ }
+
+ return $source;
+ }
+
+
+ /**
+ * Constructs an html tag.
+ *
+ * @ignore
+ *
+ * @param string tag name
+ * @param array tag options
+ * @param boolean true to leave tag open
+ *
+ * @return string
+ */
+ private static function tag($name, $options = [], $open = FALSE)
+ {
+ if (!$name) {
+ return '';
+ }
+
+ ksort($options);
+ return '<' . $name . ' ' . arrayToHtmlAttributes($options) . ($open ? '>' : '>');
+ }
+
+
+ /**
+ * Helper function for content tags.
+ *
+ * @param string $name tag name
+ * @param string $content tag content
+ * @param array $options tag options
+ *
+ * @return string
+ */
+ private static function content_tag($name, $content = '', $options = [])
+ {
+ if (!$name) {
+ return '';
+ }
+ return '<' . $name . ' ' . arrayToHtmlAttributes($options) . '>' .
+ $content .
+ '' . $name . '>';
+ }
+
+ /**
+ * Parse a HTML attribute string into an array.
+ *
+ * @ignore
+ */
+ private static function parse_attributes($stringOrArray)
+ {
+
+ if (is_array($stringOrArray))
+ return $stringOrArray;
+
+ preg_match_all('/
+ \s*(\w+) # key \\1
+ \s*=\s* # =
+ (\'|")? # values may be included in \' or " \\2
+ (.*?) # value \\3
+ (?(2) \\2) # matching \' or " if needed \\4
+ \s*(?:
+ (?=\w+\s*=) | \s*$ # followed by another key= or the end of the string
+ )
+ /x', $stringOrArray, $matches, PREG_SET_ORDER);
+
+ $attributes = [];
+ foreach ($matches as $val)
+ $attributes[$val[1]] = $val[3];
+
+ return $attributes;
+ }
+}
diff --git a/lib/classes/AuthorObject.class.php b/lib/classes/AuthorObject.class.php
deleted file mode 100644
index b3e98e3..0000000
--- a/lib/classes/AuthorObject.class.php
+++ /dev/null
@@ -1,139 +0,0 @@
-
-// +--------------------------------------------------------------------------+
-// 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 any later version.
-// +--------------------------------------------------------------------------+
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// +--------------------------------------------------------------------------+
-
-
-define("ERROR_NORMAL", "1");
-define("ERROR_CRITICAL", "8");
-
-
-/**
- * AuthorObject.class.php
- *
- * Class to provide basic properties of an object in Stud.IP
- *
- * @author Alexander Willner
- * @copyright 2003 Stud.IP-Project
- * @access public
- * @package studip_core
- * @modulegroup core
- */
-class AuthorObject
-{
-
- /**
- * Holds the code and description of an internal error
- * @access private
- * @var array $errorArray
- */
- public $errorArray;
-
- /**
- * Holds the type of object. See INSTANCEOF_*
- * @access private
- * @var string $instanceof
- */
- public $instanceof;
-
- /**
- * Constructor
- * @access public
- */
- public function __construct()
- {
- $this->instanceof = 'AuthorObject';
- $this->errorArray = [];
- }
-
- /**
- * Gets the type of object
- * @access public
- * @return string The type of object. See INSTANCEOF_*
- */
- public function x_instanceof()
- {
- return $this->instanceof;
- }
-
- /**
- * Gives the internal errorcode
- * @access public
- * @return boolean True if an error exists
- */
- public function isError()
- {
- return count($this->errorArray) > 0;
- }
-
- /**
- * Gives the codes and descriptions of the internal errors
- * @access public
- * @return array The errors as an Array like "1" => "Could not open DB"
- */
- public function getErrors()
- {
- return $this->errorArray;
- }
-
- /**
- * Resets the errorcodes and descriptions
- * @access public
- */
- public function resetErrors()
- {
- $this->errorArray = [];
- }
-
- /**
- * Sets the errorcode (internal)
- * @access public
- * @param integer $errcode The code of the error
- * @param string $errstring The description of the error
- */
- public function throwError($errcode, $errstring)
- {
- if (!is_array($this->errorArray)) {
- $this->errorArray = [];
- }
-
- $this->errorArray [] = [
- 'code' => $errcode,
- 'string' => $errstring,
- ];
- }
-
- /**
- * Sets the errorcode from other classes (internal)
- * @access private
- * @param object $class The class with the error
- */
- public function throwErrorFromClass(AuthorObject $class)
- {
- $this->errorArray = $class->getErrors();
- $class->resetErrors();
- }
-}
-
diff --git a/lib/classes/AuthorObject.php b/lib/classes/AuthorObject.php
new file mode 100644
index 0000000..b3e98e3
--- /dev/null
+++ b/lib/classes/AuthorObject.php
@@ -0,0 +1,139 @@
+
+// +--------------------------------------------------------------------------+
+// 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 any later version.
+// +--------------------------------------------------------------------------+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// +--------------------------------------------------------------------------+
+
+
+define("ERROR_NORMAL", "1");
+define("ERROR_CRITICAL", "8");
+
+
+/**
+ * AuthorObject.class.php
+ *
+ * Class to provide basic properties of an object in Stud.IP
+ *
+ * @author Alexander Willner
+ * @copyright 2003 Stud.IP-Project
+ * @access public
+ * @package studip_core
+ * @modulegroup core
+ */
+class AuthorObject
+{
+
+ /**
+ * Holds the code and description of an internal error
+ * @access private
+ * @var array $errorArray
+ */
+ public $errorArray;
+
+ /**
+ * Holds the type of object. See INSTANCEOF_*
+ * @access private
+ * @var string $instanceof
+ */
+ public $instanceof;
+
+ /**
+ * Constructor
+ * @access public
+ */
+ public function __construct()
+ {
+ $this->instanceof = 'AuthorObject';
+ $this->errorArray = [];
+ }
+
+ /**
+ * Gets the type of object
+ * @access public
+ * @return string The type of object. See INSTANCEOF_*
+ */
+ public function x_instanceof()
+ {
+ return $this->instanceof;
+ }
+
+ /**
+ * Gives the internal errorcode
+ * @access public
+ * @return boolean True if an error exists
+ */
+ public function isError()
+ {
+ return count($this->errorArray) > 0;
+ }
+
+ /**
+ * Gives the codes and descriptions of the internal errors
+ * @access public
+ * @return array The errors as an Array like "1" => "Could not open DB"
+ */
+ public function getErrors()
+ {
+ return $this->errorArray;
+ }
+
+ /**
+ * Resets the errorcodes and descriptions
+ * @access public
+ */
+ public function resetErrors()
+ {
+ $this->errorArray = [];
+ }
+
+ /**
+ * Sets the errorcode (internal)
+ * @access public
+ * @param integer $errcode The code of the error
+ * @param string $errstring The description of the error
+ */
+ public function throwError($errcode, $errstring)
+ {
+ if (!is_array($this->errorArray)) {
+ $this->errorArray = [];
+ }
+
+ $this->errorArray [] = [
+ 'code' => $errcode,
+ 'string' => $errstring,
+ ];
+ }
+
+ /**
+ * Sets the errorcode from other classes (internal)
+ * @access private
+ * @param object $class The class with the error
+ */
+ public function throwErrorFromClass(AuthorObject $class)
+ {
+ $this->errorArray = $class->getErrors();
+ $class->resetErrors();
+ }
+}
+
diff --git a/lib/classes/AutoInsert.class.php b/lib/classes/AutoInsert.class.php
deleted file mode 100644
index b2168cc..0000000
--- a/lib/classes/AutoInsert.class.php
+++ /dev/null
@@ -1,342 +0,0 @@
-
- * @author Michael Riehemann
- * @author Jan Hendrik Willms
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @since 2.1
- */
-
-/**
- * AutoInsert.class.php
- * Provides functions required by StEP00216:
- * - Assign seminars for automatic registration of certain user types
- * - Maintenance of registration rules
- *
- * Example of use:
- * @code
- *
- * # show all auto insert seminars
- * $auto_sems = AutoInsert::getAllSeminars();
- *
- * # Save a new auto insert seminar with the user status
- * AutoInsert::saveSeminar($sem_id, $rechte);
- *
- * @endcode
- */
-class AutoInsert
-{
- private static $instance = null;
- protected static $seminar_cache = null;
-
- private $settings = [];
-
- public static function instance()
- {
- if (self::$instance === null) {
- self::$instance = new self();
- }
- return self::$instance;
- }
-
- public function __construct()
- {
- $this->loadSettings();
- }
-
- private function loadSettings()
- {
- $query = "SELECT a.seminar_id, GROUP_CONCAT(a.status,IF(LENGTH(a.domain_id)=0,':keine',CONCAT(':',a.domain_id))) AS domain_status, s.Name, s.Schreibzugriff, s.start_time ";
- $query .= "FROM auto_insert_sem a ";
- $query .= "JOIN seminare AS s USING (Seminar_id) ";
- $query .= "GROUP BY s.seminar_id ";
- $query .= "ORDER BY s.Name";
- $statement = DBManager::get()->query($query);
- $results = $statement->fetchAll(PDO::FETCH_ASSOC);
- foreach ($results as $result) {
- if ($result['Schreibzugriff'] < 3) {
- $domains = explode(',', $result['domain_status']);
-
- foreach ($domains as $domain) {
- $array = explode(':', $domain);
- $key = $array[1] . '.' . $array[0];
- $this->settings[$key][$result['seminar_id']] = ['Seminar_id' => $result['seminar_id'],
- 'name' => $result['Name'],
- 'Schreibzugriff' => $result['Schreibzugriff'],
- 'start_time' => $result['start_time']];
- }
- }
- }
- }
-
-
- private function getUserSeminars($user_id, $seminare)
- {
- $statement = DBManager::get()->prepare("SELECT Seminar_id,s.name,s.Schreibzugriff,s.start_time,su.status
- FROM seminar_user su
- INNER JOIN seminare s USING(Seminar_id)
- WHERE user_id = ? AND Seminar_id IN(?)");
- $statement->execute([$user_id, $seminare]);
- return $statement->fetchAll(PDO::FETCH_ASSOC);
- }
-
- /**
- * Trägt den Benutzer in den Eingestellten veranstaltungen automatisch ein.
- * @param string $user_id
- * @param string|bool $status Wenn Status nicht angegeben wird, wird der Status des Users aus user_id genommen
- * @return array 'added' Namen der Seminare in die der User eingetragen wurde
- * array 'removed' Namen der Seminare aus denen der User ausgetragen wurde
- */
- public function saveUser($user_id, $status = false)
- {
- $domains = [];
- if (!$status) {
- $status = $GLOBALS['perm']->get_perm($user_id);
- }
- foreach (UserDomain::getUserDomainsForUser($user_id) as $d) {
- $domains [] = $d->id; //Domains des Users
- }
-
- if (count($domains) === 0) {
- $domains [] = 'keine';
- }
- $settings = [];
- $all_seminare = [];
- foreach ($domains as $domain) {
-
- $key = $domain . '.' . $status;
- if (is_array($this->settings[$key])) {
- foreach ($this->settings[$key] as $id => $value) {
- $settings[$id] = $value;
- }
- }
- foreach ($this->settings as $key) {
- foreach ($key as $id => $sem) {
- $all_seminare[$id] = $sem;
- }
- }
- }
-
- $seminare = [];
- $seminare_tutor_dozent = [];
- foreach ($this->getUserSeminars($user_id, array_keys($all_seminare)) as $sem) {
- $seminare[$sem['Seminar_id']] = $sem;
- if (in_array($sem['status'], ['tutor', 'dozent'])) {
- $seminare_tutor_dozent[$sem['Seminar_id']] = $sem;
- }
- }
- $toAdd = array_diff_key($settings, $seminare);
- $toRemove = array_diff_key($all_seminare, $toAdd, $settings, $seminare_tutor_dozent);
-
- $added = [];
- $removed = [];
-
- foreach ($toAdd as $id => $seminar) {
- if ($this->addUser($user_id, $seminar)) $added[] = $seminar['name'];
- }
- foreach ($toRemove as $id => $seminar) {
- if ($this->removeUser($user_id, $seminar)) $removed[] = $seminar['name'];
- }
-
- return ['added' => $added, 'removed' => $removed];
- }
-
- private function addUser($user_id, $seminar)
- {
- return CourseMember::insertCourseMember($seminar['Seminar_id'], $user_id, 'autor');
- }
-
- private function removeUser($user_id, $seminar)
- {
- $rows = CourseMember::deleteBySQL(' user_id = ? AND Seminar_id = ?', [$user_id, $seminar['Seminar_id']]);
- $statusgruppe_rows = StatusgruppeUser::deleteBySQL(
- 'user_id = ? AND statusgruppe_id IN (SELECT statusgruppe_id FROM statusgruppen WHERE range_id = ?)',
- [$user_id, $seminar['Seminar_id']]
- );
- if ($rows > 0 || $statusgruppe_rows > 0) return true;
-
- return false;
- }
-
- /**
- * Tests if a seminar already has an autoinsert record
- * @param string $seminar_id Id of the seminar
- * @return bool Indicating whether the seminar already has an autoinsert record
- */
- public static function checkSeminar($seminar_id, $domain_id = false)
- {
- $cached = self::getSeminarCache();
-
- if (!isset($cached[$seminar_id])) {
- $query = "SELECT domain_id, 1
- FROM auto_insert_sem
- WHERE seminar_id = ?";
- $cached[$seminar_id] = DBManager::get()->fetchGroupedPairs(
- $query,
- [$seminar_id],
- function ($value) {
- return (bool) $value;
- }
- );
- }
-
- return array_key_exists($domain_id ?: '', $cached[$seminar_id])
- ? $cached[$seminar_id][$domain_id ?: '']
- : false;
- }
-
- /**
- * Enables a seminar for autoinsertion of users with the given status(ses)
- * @param string $seminar_id Id of the seminar
- * @param mixed $status Either a single string or an array of strings
- * containing the status(ses) to enable for
- * autoinsertion
- */
- public static function saveSeminar($seminar_id, $status, $domain_id)
- {
- $query = "INSERT INTO auto_insert_sem (seminar_id, status,domain_id) VALUES (?, ?,?)";
- $statement = DBManager::get()->prepare($query);
-
- foreach ((array)$status as $s) {
- $statement->execute([$seminar_id, $s, $domain_id]);
- }
- }
-
- /**
- * Updates an autoinsert record for a given seminar, dependent on the
- * parameter $remove it either inserts or removes the record for the given
- * parameters
- *
- * @param string $seminar_id Id of the seminar
- * @param string $status Status for autoinsertion
- * @param bool $remove Whether the record should be added or removed
- */
- public static function updateSeminar($seminar_id, $domain, $status, $remove = false)
- {
- $query = $remove ? "DELETE FROM auto_insert_sem WHERE seminar_id = ? AND status= ? AND domain_id = ?" : "INSERT IGNORE INTO auto_insert_sem (seminar_id, status,domain_id) VALUES (?, ?, ?)";
- $statement = DBManager::get()->prepare($query);
- $statement->execute([$seminar_id, $status, $domain]);
-
- if ($remove) {
- unset(self::getSeminarCache()[$seminar_id]);
- }
- }
-
- /**
- * Removes a seminar from the autoinsertion process.
- * @param string $seminar_id Id of the seminar
- */
- public static function deleteSeminar($seminar_id): bool
- {
- $query = "DELETE FROM auto_insert_sem WHERE seminar_id = ?";
- $statement = DBManager::get()->prepare($query);
- $result = $statement->execute([$seminar_id]);
-
- unset(self::getSeminarCache()[$seminar_id]);
-
- return $result > 0;
- }
-
- /**
- * Returns a list of all seminars enabled for autoinsertion
- * @param bool Indicates whether only the seminar ids (true) or the full
- * dataset shall be returned (false)
- * @return array The list of all enabled seminars (format according to $only_sem_id)
- */
- public static function getAllSeminars($only_sem_id = false)
- {
- if ($only_sem_id) {
- $query = "SELECT DISTINCT seminar_id FROM auto_insert_sem";
- $statement = DBManager::get()->query($query);
- $results = $statement->fetchAll(PDO::FETCH_COLUMN);
- } else {
- $query = "SELECT a.seminar_id, GROUP_CONCAT(a.status,IF(LENGTH(a.domain_id)=0,':keine',CONCAT(':',a.domain_id))) AS domain_status, s.Name, s.Schreibzugriff, s.start_time ";
- $query .= "FROM auto_insert_sem a ";
- $query .= "JOIN seminare AS s USING (Seminar_id) ";
-
- $query .= "GROUP BY s.seminar_id ";
- $query .= "ORDER BY s.Name";
- $statement = DBManager::get()->query($query);
- $results = $statement->fetchAll(PDO::FETCH_ASSOC);
- foreach ($results as $index => $result) {
- $domains = explode(',', $result['domain_status']);
- foreach ($domains as $domain) {
- $array = explode(':', $domain);
- $results[$index]['status'][$array[1]][] = $array[0];
- }
- }
- }
-
- return $results;
- }
-
- /**
- * Returns a seminar's info for autoinsertion
- * @param string $seminar_id Id of the seminar
- * @return array The seminar's data as an associative array
- */
- public static function getSeminar($seminar_id)
- {
- $query = "SELECT a.seminar_id, GROUP_CONCAT(a.status) AS status, s.Name ";
- $query .= "FROM auto_insert_sem a ";
- $query .= "JOIN seminare AS s USING (Seminar_id) ";
- $query .= "WHERE a.seminar_id = ? ";
- $query .= "GROUP BY s.seminar_id";
- $statement = DBManager::get()->prepare($query);
- $statement->execute([$seminar_id]);
-
- $result = $statement->fetch(PDO::FETCH_ASSOC);
- $result['status'] = explode(',', $result['status']);
- return $result;
- }
-
- /**
- * Store the user's automatic registration in a seminar redundantly to
- * avoid an annoying reregistration although the user explicitely left the
- * according seminar
- * @param string $user_id Id of the user
- * @param string $seminar_id Id of the seminar
- */
- public static function saveAutoInsertUser($seminar_id, $user_id)
- {
- $query = "INSERT IGNORE INTO auto_insert_user (Seminar_id, user_id, mkdate)
- SELECT ?, user_id, UNIX_TIMESTAMP()
- FROM auth_user_md5
- WHERE user_id = ? AND perms NOT IN ('root','admin')";
- return DBManager::get()->execute($query, [$seminar_id, $user_id]);
- }
-
- /**
- * Tests whether a user was already automatically registered for a certain
- * seminar.
- * @param string $seminar_id Id of the seminar
- * @param string $user_id If of the user
- * @return bool Indicates whether the user was already registered
- */
- public static function checkAutoInsertUser($seminar_id, $user_id)
- {
- $query = "SELECT 1 FROM auto_insert_user WHERE seminar_id = ? AND user_id = ?";
- $statement = DBManager::get()->prepare($query);
- $statement->execute([$seminar_id, $user_id]);
- $result = $statement->fetchColumn();
-
- return $result > 0;
- }
-
- /**
- * Returns the cache for seminars.
- */
- protected static function getSeminarCache(): StudipCachedArray
- {
- if (self::$seminar_cache === null) {
- self::$seminar_cache = new StudipCachedArray('AutoInsertSeminars');
- }
- return self::$seminar_cache;
- }
-}
diff --git a/lib/classes/AutoInsert.php b/lib/classes/AutoInsert.php
new file mode 100644
index 0000000..b2168cc
--- /dev/null
+++ b/lib/classes/AutoInsert.php
@@ -0,0 +1,342 @@
+
+ * @author Michael Riehemann
+ * @author Jan Hendrik Willms
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 2.1
+ */
+
+/**
+ * AutoInsert.class.php
+ * Provides functions required by StEP00216:
+ * - Assign seminars for automatic registration of certain user types
+ * - Maintenance of registration rules
+ *
+ * Example of use:
+ * @code
+ *
+ * # show all auto insert seminars
+ * $auto_sems = AutoInsert::getAllSeminars();
+ *
+ * # Save a new auto insert seminar with the user status
+ * AutoInsert::saveSeminar($sem_id, $rechte);
+ *
+ * @endcode
+ */
+class AutoInsert
+{
+ private static $instance = null;
+ protected static $seminar_cache = null;
+
+ private $settings = [];
+
+ public static function instance()
+ {
+ if (self::$instance === null) {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ public function __construct()
+ {
+ $this->loadSettings();
+ }
+
+ private function loadSettings()
+ {
+ $query = "SELECT a.seminar_id, GROUP_CONCAT(a.status,IF(LENGTH(a.domain_id)=0,':keine',CONCAT(':',a.domain_id))) AS domain_status, s.Name, s.Schreibzugriff, s.start_time ";
+ $query .= "FROM auto_insert_sem a ";
+ $query .= "JOIN seminare AS s USING (Seminar_id) ";
+ $query .= "GROUP BY s.seminar_id ";
+ $query .= "ORDER BY s.Name";
+ $statement = DBManager::get()->query($query);
+ $results = $statement->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($results as $result) {
+ if ($result['Schreibzugriff'] < 3) {
+ $domains = explode(',', $result['domain_status']);
+
+ foreach ($domains as $domain) {
+ $array = explode(':', $domain);
+ $key = $array[1] . '.' . $array[0];
+ $this->settings[$key][$result['seminar_id']] = ['Seminar_id' => $result['seminar_id'],
+ 'name' => $result['Name'],
+ 'Schreibzugriff' => $result['Schreibzugriff'],
+ 'start_time' => $result['start_time']];
+ }
+ }
+ }
+ }
+
+
+ private function getUserSeminars($user_id, $seminare)
+ {
+ $statement = DBManager::get()->prepare("SELECT Seminar_id,s.name,s.Schreibzugriff,s.start_time,su.status
+ FROM seminar_user su
+ INNER JOIN seminare s USING(Seminar_id)
+ WHERE user_id = ? AND Seminar_id IN(?)");
+ $statement->execute([$user_id, $seminare]);
+ return $statement->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * Trägt den Benutzer in den Eingestellten veranstaltungen automatisch ein.
+ * @param string $user_id
+ * @param string|bool $status Wenn Status nicht angegeben wird, wird der Status des Users aus user_id genommen
+ * @return array 'added' Namen der Seminare in die der User eingetragen wurde
+ * array 'removed' Namen der Seminare aus denen der User ausgetragen wurde
+ */
+ public function saveUser($user_id, $status = false)
+ {
+ $domains = [];
+ if (!$status) {
+ $status = $GLOBALS['perm']->get_perm($user_id);
+ }
+ foreach (UserDomain::getUserDomainsForUser($user_id) as $d) {
+ $domains [] = $d->id; //Domains des Users
+ }
+
+ if (count($domains) === 0) {
+ $domains [] = 'keine';
+ }
+ $settings = [];
+ $all_seminare = [];
+ foreach ($domains as $domain) {
+
+ $key = $domain . '.' . $status;
+ if (is_array($this->settings[$key])) {
+ foreach ($this->settings[$key] as $id => $value) {
+ $settings[$id] = $value;
+ }
+ }
+ foreach ($this->settings as $key) {
+ foreach ($key as $id => $sem) {
+ $all_seminare[$id] = $sem;
+ }
+ }
+ }
+
+ $seminare = [];
+ $seminare_tutor_dozent = [];
+ foreach ($this->getUserSeminars($user_id, array_keys($all_seminare)) as $sem) {
+ $seminare[$sem['Seminar_id']] = $sem;
+ if (in_array($sem['status'], ['tutor', 'dozent'])) {
+ $seminare_tutor_dozent[$sem['Seminar_id']] = $sem;
+ }
+ }
+ $toAdd = array_diff_key($settings, $seminare);
+ $toRemove = array_diff_key($all_seminare, $toAdd, $settings, $seminare_tutor_dozent);
+
+ $added = [];
+ $removed = [];
+
+ foreach ($toAdd as $id => $seminar) {
+ if ($this->addUser($user_id, $seminar)) $added[] = $seminar['name'];
+ }
+ foreach ($toRemove as $id => $seminar) {
+ if ($this->removeUser($user_id, $seminar)) $removed[] = $seminar['name'];
+ }
+
+ return ['added' => $added, 'removed' => $removed];
+ }
+
+ private function addUser($user_id, $seminar)
+ {
+ return CourseMember::insertCourseMember($seminar['Seminar_id'], $user_id, 'autor');
+ }
+
+ private function removeUser($user_id, $seminar)
+ {
+ $rows = CourseMember::deleteBySQL(' user_id = ? AND Seminar_id = ?', [$user_id, $seminar['Seminar_id']]);
+ $statusgruppe_rows = StatusgruppeUser::deleteBySQL(
+ 'user_id = ? AND statusgruppe_id IN (SELECT statusgruppe_id FROM statusgruppen WHERE range_id = ?)',
+ [$user_id, $seminar['Seminar_id']]
+ );
+ if ($rows > 0 || $statusgruppe_rows > 0) return true;
+
+ return false;
+ }
+
+ /**
+ * Tests if a seminar already has an autoinsert record
+ * @param string $seminar_id Id of the seminar
+ * @return bool Indicating whether the seminar already has an autoinsert record
+ */
+ public static function checkSeminar($seminar_id, $domain_id = false)
+ {
+ $cached = self::getSeminarCache();
+
+ if (!isset($cached[$seminar_id])) {
+ $query = "SELECT domain_id, 1
+ FROM auto_insert_sem
+ WHERE seminar_id = ?";
+ $cached[$seminar_id] = DBManager::get()->fetchGroupedPairs(
+ $query,
+ [$seminar_id],
+ function ($value) {
+ return (bool) $value;
+ }
+ );
+ }
+
+ return array_key_exists($domain_id ?: '', $cached[$seminar_id])
+ ? $cached[$seminar_id][$domain_id ?: '']
+ : false;
+ }
+
+ /**
+ * Enables a seminar for autoinsertion of users with the given status(ses)
+ * @param string $seminar_id Id of the seminar
+ * @param mixed $status Either a single string or an array of strings
+ * containing the status(ses) to enable for
+ * autoinsertion
+ */
+ public static function saveSeminar($seminar_id, $status, $domain_id)
+ {
+ $query = "INSERT INTO auto_insert_sem (seminar_id, status,domain_id) VALUES (?, ?,?)";
+ $statement = DBManager::get()->prepare($query);
+
+ foreach ((array)$status as $s) {
+ $statement->execute([$seminar_id, $s, $domain_id]);
+ }
+ }
+
+ /**
+ * Updates an autoinsert record for a given seminar, dependent on the
+ * parameter $remove it either inserts or removes the record for the given
+ * parameters
+ *
+ * @param string $seminar_id Id of the seminar
+ * @param string $status Status for autoinsertion
+ * @param bool $remove Whether the record should be added or removed
+ */
+ public static function updateSeminar($seminar_id, $domain, $status, $remove = false)
+ {
+ $query = $remove ? "DELETE FROM auto_insert_sem WHERE seminar_id = ? AND status= ? AND domain_id = ?" : "INSERT IGNORE INTO auto_insert_sem (seminar_id, status,domain_id) VALUES (?, ?, ?)";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$seminar_id, $status, $domain]);
+
+ if ($remove) {
+ unset(self::getSeminarCache()[$seminar_id]);
+ }
+ }
+
+ /**
+ * Removes a seminar from the autoinsertion process.
+ * @param string $seminar_id Id of the seminar
+ */
+ public static function deleteSeminar($seminar_id): bool
+ {
+ $query = "DELETE FROM auto_insert_sem WHERE seminar_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $result = $statement->execute([$seminar_id]);
+
+ unset(self::getSeminarCache()[$seminar_id]);
+
+ return $result > 0;
+ }
+
+ /**
+ * Returns a list of all seminars enabled for autoinsertion
+ * @param bool Indicates whether only the seminar ids (true) or the full
+ * dataset shall be returned (false)
+ * @return array The list of all enabled seminars (format according to $only_sem_id)
+ */
+ public static function getAllSeminars($only_sem_id = false)
+ {
+ if ($only_sem_id) {
+ $query = "SELECT DISTINCT seminar_id FROM auto_insert_sem";
+ $statement = DBManager::get()->query($query);
+ $results = $statement->fetchAll(PDO::FETCH_COLUMN);
+ } else {
+ $query = "SELECT a.seminar_id, GROUP_CONCAT(a.status,IF(LENGTH(a.domain_id)=0,':keine',CONCAT(':',a.domain_id))) AS domain_status, s.Name, s.Schreibzugriff, s.start_time ";
+ $query .= "FROM auto_insert_sem a ";
+ $query .= "JOIN seminare AS s USING (Seminar_id) ";
+
+ $query .= "GROUP BY s.seminar_id ";
+ $query .= "ORDER BY s.Name";
+ $statement = DBManager::get()->query($query);
+ $results = $statement->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($results as $index => $result) {
+ $domains = explode(',', $result['domain_status']);
+ foreach ($domains as $domain) {
+ $array = explode(':', $domain);
+ $results[$index]['status'][$array[1]][] = $array[0];
+ }
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Returns a seminar's info for autoinsertion
+ * @param string $seminar_id Id of the seminar
+ * @return array The seminar's data as an associative array
+ */
+ public static function getSeminar($seminar_id)
+ {
+ $query = "SELECT a.seminar_id, GROUP_CONCAT(a.status) AS status, s.Name ";
+ $query .= "FROM auto_insert_sem a ";
+ $query .= "JOIN seminare AS s USING (Seminar_id) ";
+ $query .= "WHERE a.seminar_id = ? ";
+ $query .= "GROUP BY s.seminar_id";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$seminar_id]);
+
+ $result = $statement->fetch(PDO::FETCH_ASSOC);
+ $result['status'] = explode(',', $result['status']);
+ return $result;
+ }
+
+ /**
+ * Store the user's automatic registration in a seminar redundantly to
+ * avoid an annoying reregistration although the user explicitely left the
+ * according seminar
+ * @param string $user_id Id of the user
+ * @param string $seminar_id Id of the seminar
+ */
+ public static function saveAutoInsertUser($seminar_id, $user_id)
+ {
+ $query = "INSERT IGNORE INTO auto_insert_user (Seminar_id, user_id, mkdate)
+ SELECT ?, user_id, UNIX_TIMESTAMP()
+ FROM auth_user_md5
+ WHERE user_id = ? AND perms NOT IN ('root','admin')";
+ return DBManager::get()->execute($query, [$seminar_id, $user_id]);
+ }
+
+ /**
+ * Tests whether a user was already automatically registered for a certain
+ * seminar.
+ * @param string $seminar_id Id of the seminar
+ * @param string $user_id If of the user
+ * @return bool Indicates whether the user was already registered
+ */
+ public static function checkAutoInsertUser($seminar_id, $user_id)
+ {
+ $query = "SELECT 1 FROM auto_insert_user WHERE seminar_id = ? AND user_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$seminar_id, $user_id]);
+ $result = $statement->fetchColumn();
+
+ return $result > 0;
+ }
+
+ /**
+ * Returns the cache for seminars.
+ */
+ protected static function getSeminarCache(): StudipCachedArray
+ {
+ if (self::$seminar_cache === null) {
+ self::$seminar_cache = new StudipCachedArray('AutoInsertSeminars');
+ }
+ return self::$seminar_cache;
+ }
+}
diff --git a/lib/classes/Avatar.class.php b/lib/classes/Avatar.class.php
deleted file mode 100644
index 959523f..0000000
--- a/lib/classes/Avatar.class.php
+++ /dev/null
@@ -1,635 +0,0 @@
-
- * @license GPL2 or any later version
- * @since 1.7
- */
-class Avatar
-{
- public const AVATAR_TYPE = 'user';
- protected const CREATE_CHUNKED_FOLDERS = true;
-
- public const EXTENSION = 'webp';
- public const IMAGE_QUALITY = 90;
-
- /**
- * This constant stands for the maximal size of a user picture.
- */
- public const ORIGINAL = 'original';
-
- /**
- * This constant stands for the maximal size of a user picture.
- */
- public const NORMAL = 'normal';
-
- /**
- * This constant stands for a medium size of a user picture.
- */
- public const MEDIUM = 'medium';
-
- /**
- * This constant stands for an icon size of a user picture.
- */
- public const SMALL = 'small';
-
- /**
- * This constant represents the maximal size of a user picture in bytes.
- */
- public const MAX_FILE_SIZE = 10485760;
-
- /**
- * This constant holds the username and ID of the "nobody" avatar.
- */
- protected const NOBODY = 'nobody';
-
- /**
- * Holds the user's id
- *
- * @var string
- */
- protected $user_id;
-
- /**
- * Holds the user's username
- *
- * @var string
- */
- protected $username;
-
- /**
- * Returns an avatar object of the appropriate class.
- *
- * @param string $id the user's id
- * @param string $username the user's username (optional)
- *
- * @return static the user's avatar.
- */
- public static function getAvatar($id)
- {
- $username = null;
-
- if (func_num_args() === 2) {
- $username = func_get_arg(1);
- }
-
- return new static($id, $username);
- }
-
- /**
- * Returns an avatar object for "nobody".
- *
- * @return Avatar the user's avatar.
- */
- public static function getNobody()
- {
- return new static(static::NOBODY, static::NOBODY);
- }
-
- /**
- * Returns the url to the customized avatars
- *
- * @return string
- */
- public function getAvatarDirectoryUrl()
- {
- return sprintf(
- '%s/%s%s',
- $GLOBALS['DYNAMIC_CONTENT_URL'],
- static::AVATAR_TYPE,
- static::CREATE_CHUNKED_FOLDERS ? '/' . substr($this->user_id, 0, 2) : ''
- );
- }
-
- /**
- * Returns the path to the customized avatars
- *
- * @return string
- */
- public function getAvatarDirectoryPath()
- {
- return sprintf(
- '%s/%s%s',
- $GLOBALS['DYNAMIC_CONTENT_PATH'],
- static::AVATAR_TYPE,
- static::CREATE_CHUNKED_FOLDERS ? '/' . substr($this->user_id, 0, 2) : ''
- );
- }
-
- /**
- * Returns the url to the default avatars
- */
- public function getDefaultAvatarDirectoryUrl(): string
- {
- return Assets::url('images/avatars/' . static::AVATAR_TYPE);
- }
-
- /**
- * Returns the path to the default avatars
- */
- public function getDefaultAvatarDirectoryPath(): string
- {
- return Assets::path('images/avatars/' . static::AVATAR_TYPE);
- }
-
- /**
- * Returns the url to a customized avatar
- *
- * @return string
- */
- public function getCustomAvatarUrl($size)
- {
- if ($this->isNobody()) {
- return sprintf(
- '%s/%s_%s.%s',
- $this->getDefaultAvatarDirectoryUrl(),
- $this->user_id,
- $size,
- self::EXTENSION
- );
- }
-
- return sprintf(
- '%s/%s_%s.%s?d=%s',
- $this->getAvatarDirectoryUrl(),
- $this->user_id,
- $size,
- self::EXTENSION,
- @filemtime($this->getCustomAvatarPath($size)) ?: "0"
- );
- }
-
- /**
- * Returns the path to a customized avatar
- *
- * @return string
- */
- public function getCustomAvatarPath($size)
- {
- if ($this->isNobody()) {
- return sprintf(
- '%s/%s_%s.%s',
- $this->getDefaultAvatarDirectoryPath(),
- $this->user_id,
- $size,
- self::EXTENSION
- );
- }
-
- return sprintf(
- '%s/%s_%s.%s',
- $this->getAvatarDirectoryPath(),
- $this->user_id,
- $size,
- self::EXTENSION
- );
- }
-
- /**
- * Constructs a new Avatar object belonging to a user with the given id.
- *
- * @param string $user_id the user's id
- * @param string $username the user's username (optional)
- */
- protected function __construct($user_id, $username = null)
- {
- $this->user_id = $user_id;
- $this->username = $username;
-
- $this->checkAvatarVisibility();
- }
-
- /**
- * Returns the file name of a user's avatar.
- *
- * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- *
- * @return string the absolute file path to the avatar
- */
- public function getFilename($size)
- {
- return $this->is_customized()
- ? $this->getCustomAvatarPath($size)
- : $this->getNobody()->getCustomAvatarPath($size);
- }
-
- /**
- * Returns the URL of a user's picture.
- *
- * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- *
- * @return string the URL to the user's picture
- */
- # TODO (mlunzena) in Url umbenennen
- public function getURL($size)
- {
- return $this->is_customized()
- ? $this->getCustomAvatarUrl($size)
- : $this->getNobody()->getCustomAvatarUrl($size);
- }
-
- /**
- * Returns whether this avatar is a default/"nobody" avatar.
- */
- public function isNobody(): bool
- {
- return $this->user_id === static::NOBODY;
- }
-
- /**
- * Returns whether a customized file exists
- */
- public function customizedFileExists(): bool
- {
- return file_exists($this->getCustomAvatarPath(static::MEDIUM));
- }
-
- /**
- * Returns whether a user has uploaded a custom picture.
- *
- * @return boolean returns TRUE if the user customized her picture, FALSE
- * otherwise.
- */
- public function is_customized()
- {
- return !$this->isNobody()
- && $this->customizedFileExists();
- }
-
- /**
- * Returns the CSS class to use for this avatar image.
- *
- * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- *
- * @return string CSS class to use for the avatar
- */
- protected function getCssClass($size)
- {
- if (!isset($this->username)) {
- $this->username = get_username($this->user_id);
- }
-
- return sprintf(
- 'avatar-%s user-%s' . ($this->is_customized() ? '' : ' recolor'),
- $size,
- htmlReady($this->username)
- );
- }
-
- /**
- * Constructs a desired HTML image tag for an Avatar. Additional
- * html attributes may also be specified using the $opt parameter.
- *
- * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- * @param array $opt array of attributes to add to the HTML image tag
- *
- * @return string returns the HTML image tag
- */
- public function getImageTag($size = self::MEDIUM, $opt = [])
- {
- $opt['src'] = $this->getURL($size);
-
- if (isset($opt['class'])) {
- $opt['class'] = $this->getCssClass($size) . ' ' . $opt['class'];
- } else {
- $opt['class'] = $this->getCssClass($size);
- }
-
- // Apply cast to string for title if necessary
- if (isset($opt['title']) && !is_string($opt['title'])) {
- $opt['title'] = (string) $opt['title'];
- }
-
- if (!empty($opt['title']) && $opt['title'] !== html_entity_decode($opt['title'])) {
- // Decode already htmlready encoded titles (which were used until
- // all attributes were encoded inside this method)
- $opt['title'] = html_entity_decode($opt['title']);
-
- if (Studip\ENV === 'development') {
- $trace = debug_backtrace();
- $caller = array_shift($trace);
-
- $file = str_replace("{$GLOBALS['STUDIP_BASE_PATH']}/", '', $caller['file']);
- trigger_error(
- "{$file}:{$caller['line']}: Passes already encoded title to Avatar::getImageTag()",
- E_USER_DEPRECATED
- );
- }
- }
-
- if (!isset($opt['alt']) && !isset($opt['title'])) {
- //Add an empty alt attribute to prevent screen readers from
- //reading the URL of the icon:
- $opt['alt'] = '';
- }
-
- return '';
- }
-
- /**
- * Creates all the different sized thumbnails for an uploaded file.
- *
- * @param string $userfile the key of the uploaded file, see documentation about $_FILES
- *
- * @return void
- */
- public function createFromUpload($userfile)
- {
- try {
- // keine Datei ausgewählt!
- if (!$_FILES[$userfile]['name']) {
- throw new Exception(_('Sie haben keine Datei zum Hochladen ausgewählt!'));
- }
-
- // Fehler beim Hochladen
- if ($_FILES[$userfile]['error'] !== UPLOAD_ERR_OK) {
- throw new Exception(_('Es gab einen Fehler beim Hochladen der Datei!'));
- }
-
-
- // Bilddatei ist zu groß
- if ($_FILES[$userfile]['size'] > self::MAX_FILE_SIZE) {
- throw new Exception(sprintf(
- _('Die hochgeladene Bilddatei ist %s KB groß. Die maximale Dateigröße beträgt %s KB!'),
- round($_FILES[$userfile]['size'] / 1024),
- self::MAX_FILE_SIZE / 1024)
- );
- }
-
- // get extension
- $pathinfo = pathinfo($_FILES[$userfile]['name']);
- $ext = mb_strtolower($pathinfo['extension']);
-
- // passende Endung ?
- if (!in_array($ext, words('jpg jpeg gif png webp'))) {
- throw new Exception(sprintf(
- _('Der Dateityp der Bilddatei ist falsch (%s). Es sind nur die Dateiendungen .gif, .png, .jpeg, .jpg oder .webp erlaubt!'),
- $ext
- ));
- }
-
- // na dann kopieren wir mal...
- $filename = tempnam($GLOBALS['TMP_PATH'], 'avatar-upload');
-
- if (!@move_uploaded_file($_FILES[$userfile]['tmp_name'], $filename)) {
- throw new Exception(_("Es ist ein Fehler beim Kopieren der Datei aufgetreten. Das Bild wurde nicht hochgeladen!"));
- }
-
- // set permissions for uploaded file
- @chmod($filename, 0666 & ~umask());
-
- $this->sanitizeOrientation($filename);
- $this->createFrom($filename);
- } finally {
- if (isset($filename)) {
- @unlink($filename);
- }
- }
- }
-
- /**
- * Creates thumbnails from an image.
- *
- * @param string $filename filename of the image to create thumbnails from
- *
- * @return void
- */
- public function createFrom($filename)
- {
- if (!extension_loaded('gd')) {
- throw new Exception(_('Es ist ein Fehler beim Bearbeiten des Bildes aufgetreten.') . ' (' . _('Fehlende GD-Lib') . ')');
- }
-
- set_error_handler([__CLASS__, 'error_handler']);
-
- NotificationCenter::postNotification('AvatarWillCreate', $this->user_id);
- $this->resize(static::NORMAL, $filename);
- $this->resize(static::MEDIUM, $filename);
- $this->resize(static::SMALL, $filename);
- NotificationCenter::postNotification('AvatarDidCreate', $this->user_id);
-
- restore_error_handler();
- }
-
- /**
- * Removes all uploaded pictures of a user.
- */
- public function reset()
- {
- if ($this->is_customized()) {
- NotificationCenter::postNotification('AvatarWillDelete', $this->user_id);
- @unlink($this->getCustomAvatarPath(static::NORMAL));
- @unlink($this->getCustomAvatarPath(static::SMALL));
- @unlink($this->getCustomAvatarPath(static::MEDIUM));
- NotificationCenter::postNotification('AvatarDidDelete', $this->user_id);
- }
- }
-
- /**
- * Return the dimension of a size
- *
- * @param string $size the dimension of a size
- * @return array{0: int, 1: int} a tupel of integers [width, height]
- */
- public static function getDimension($size)
- {
- $dimensions = [
- static::NORMAL => [250, 250],
- static::MEDIUM => [100, 100],
- static::SMALL => [25, 25]
- ];
- return $dimensions[$size];
- }
-
- /**
- * Create from an image thumbnails of a specified size.
- *
- * @param string $size the size of the thumbnail to create
- * @param string $filename the filename of the image to make thumbnail of
- */
- private function resize(string $size, string $filename)
- {
- [$thumb_width, $thumb_height] = static::getDimension($size);
-
- $thumb_width = $thumb_width * 2;
- $thumb_height = $thumb_height * 2;
-
- [$width, $height, $type] = getimagesize($filename);
-
- # create image resource from filename
- $lookup = [
- IMAGETYPE_GIF => 'imagecreatefromgif',
- IMAGETYPE_JPEG => 'imagecreatefromjpeg',
- IMAGETYPE_PNG => 'imagecreatefrompng',
- IMAGETYPE_WEBP => 'imagecreatefromwebp',
- ];
- if (!isset($lookup[$type])) {
- throw new Exception(_("Der Typ des Bilds wird nicht unterstützt."));
- }
- $image = $lookup[$type]($filename);
-
- imagealphablending($image, false);
- imagesavealpha($image, true);
-
- # resize image if needed
- if ($height > $thumb_height || $width > $thumb_width) {
- $factor = max($thumb_width / $width, $thumb_height / $height);
- $resized_width = round($width * $factor);
- $resized_height = round($height * $factor);
- } else {
- $resized_width = $width;
- $resized_height = $height;
- }
-
- $image = self::imageresize($image, $width, $height, $resized_width, $resized_height);
-
- $dst = imagecreatetruecolor($thumb_width, $thumb_height);
- imagealphablending($dst, false);
- imagesavealpha($dst, true);
-
- $trans_colour = imagecolorallocatealpha($dst, 0, 0, 0, 127);
- imagefill($dst, 0, 0, $trans_colour);
-
- // center the new image
- $ypos = intval($thumb_height - $resized_height) >> 1;
- $xpos = intval($thumb_width - $resized_width) >> 1;
-
- imagecopy(
- $dst, $image,
- $xpos, $ypos,
- 0, 0,
- $resized_width, $resized_height
- );
-
- $output_file = $this->getCustomAvatarPath($size);
- $directory = dirname($output_file);
- if (!is_dir($directory) && !mkdir($directory)) {
- throw new Exception(_('Das Verzeichnis zum Speichern der Datei konnte nicht angelegt werden.'));
- }
-
- imagewebp($dst, $output_file, self::IMAGE_QUALITY);
- }
-
- private function imageresize($image, $current_width, $current_height, $width, $height)
- {
- $image_resized = imagecreatetruecolor($width, $height);
-
- imagealphablending($image_resized, false);
- imagesavealpha($image_resized, true);
- imagecopyresampled(
- $image_resized, $image,
- 0, 0,
- 0, 0,
- $width, $height,
- $current_width, $current_height
- );
-
- return $image_resized;
- }
-
- public static function error_handler($errno, $errstr, $errfile, $errline)
- {
- if (defined('E_RECOVERABLE_ERROR')
- && $errno == constant('E_RECOVERABLE_ERROR'))
- {
- $message = sprintf(
- 'Recoverable error "%s" occured in file %s line %u.',
- $errstr,
- $errfile,
- $errline
- );
- throw new Exception($message);
- }
-
- # execute PHP internal error handler
- return false;
- }
-
- /**
- * Return the default title of the avatar.
- * @return string the default title
- */
- public function getDefaultTitle()
- {
- if ($this->isNobody()) {
- return static::NOBODY;
- }
-
- require_once 'lib/functions.php';
- return get_fullname($this->user_id);
- }
-
- /**
- * Return if avatar is visible to the current user.
- * Also set the user_id of avatar to nobody if not visible to current user.
- * @return boolean: true if visible
- */
- protected function checkAvatarVisibility()
- {
- $visible = Visibility::verify('picture', $this->user_id);
- if (!$visible) {
- $this->user_id = self::NOBODY;
- }
- return $visible;
- }
-
- /**
- * Corrects the orientation of images from iOS/OS X devices which might
- * lead to a rotated image. EXIF information is checked and when the
- * orientation is set by EXIF data, we rotate the image accordingly.
- *
- * @param string $filename Filename of the image to correct
- */
- protected function sanitizeOrientation($filename)
- {
- if (!function_exists('exif_read_data')) {
- return;
- }
-
- if (exif_imagetype($filename) !== IMAGETYPE_JPEG) {
- return;
- }
-
- $exif = exif_read_data($filename);
- if (!$exif || !$exif['Orientation'] || $exif['Orientation'] == 1) {
- return;
- }
-
- $degree = 0;
- switch ($exif['Orientation']) {
- case 3:
- $degree = 180;
- break;
- case 6:
- $degree = -90;
- break;
- case 8:
- $degree = 90;
- break;
- }
-
- if ($degree) {
- $img = imagecreatefromstring(file_get_contents($filename));
- $img = imagerotate($img, $degree, 0);
-
- $extension = pathinfo($filename, PATHINFO_EXTENSION);
- if ($extension === 'jpg' || $extension === 'jpeg') {
- imagejpeg($img, $filename, 95);
- } elseif ($extension === 'gif') {
- imagegif($img, $filename);
- } elseif ($extension === 'png') {
- imagepng($img, $filename, 9);
- } else {
- imagewebp($img, $filename, self::IMAGE_QUALITY);
- }
-
- imagedestroy($img);
- }
- }
-}
diff --git a/lib/classes/Avatar.php b/lib/classes/Avatar.php
new file mode 100644
index 0000000..959523f
--- /dev/null
+++ b/lib/classes/Avatar.php
@@ -0,0 +1,635 @@
+
+ * @license GPL2 or any later version
+ * @since 1.7
+ */
+class Avatar
+{
+ public const AVATAR_TYPE = 'user';
+ protected const CREATE_CHUNKED_FOLDERS = true;
+
+ public const EXTENSION = 'webp';
+ public const IMAGE_QUALITY = 90;
+
+ /**
+ * This constant stands for the maximal size of a user picture.
+ */
+ public const ORIGINAL = 'original';
+
+ /**
+ * This constant stands for the maximal size of a user picture.
+ */
+ public const NORMAL = 'normal';
+
+ /**
+ * This constant stands for a medium size of a user picture.
+ */
+ public const MEDIUM = 'medium';
+
+ /**
+ * This constant stands for an icon size of a user picture.
+ */
+ public const SMALL = 'small';
+
+ /**
+ * This constant represents the maximal size of a user picture in bytes.
+ */
+ public const MAX_FILE_SIZE = 10485760;
+
+ /**
+ * This constant holds the username and ID of the "nobody" avatar.
+ */
+ protected const NOBODY = 'nobody';
+
+ /**
+ * Holds the user's id
+ *
+ * @var string
+ */
+ protected $user_id;
+
+ /**
+ * Holds the user's username
+ *
+ * @var string
+ */
+ protected $username;
+
+ /**
+ * Returns an avatar object of the appropriate class.
+ *
+ * @param string $id the user's id
+ * @param string $username the user's username (optional)
+ *
+ * @return static the user's avatar.
+ */
+ public static function getAvatar($id)
+ {
+ $username = null;
+
+ if (func_num_args() === 2) {
+ $username = func_get_arg(1);
+ }
+
+ return new static($id, $username);
+ }
+
+ /**
+ * Returns an avatar object for "nobody".
+ *
+ * @return Avatar the user's avatar.
+ */
+ public static function getNobody()
+ {
+ return new static(static::NOBODY, static::NOBODY);
+ }
+
+ /**
+ * Returns the url to the customized avatars
+ *
+ * @return string
+ */
+ public function getAvatarDirectoryUrl()
+ {
+ return sprintf(
+ '%s/%s%s',
+ $GLOBALS['DYNAMIC_CONTENT_URL'],
+ static::AVATAR_TYPE,
+ static::CREATE_CHUNKED_FOLDERS ? '/' . substr($this->user_id, 0, 2) : ''
+ );
+ }
+
+ /**
+ * Returns the path to the customized avatars
+ *
+ * @return string
+ */
+ public function getAvatarDirectoryPath()
+ {
+ return sprintf(
+ '%s/%s%s',
+ $GLOBALS['DYNAMIC_CONTENT_PATH'],
+ static::AVATAR_TYPE,
+ static::CREATE_CHUNKED_FOLDERS ? '/' . substr($this->user_id, 0, 2) : ''
+ );
+ }
+
+ /**
+ * Returns the url to the default avatars
+ */
+ public function getDefaultAvatarDirectoryUrl(): string
+ {
+ return Assets::url('images/avatars/' . static::AVATAR_TYPE);
+ }
+
+ /**
+ * Returns the path to the default avatars
+ */
+ public function getDefaultAvatarDirectoryPath(): string
+ {
+ return Assets::path('images/avatars/' . static::AVATAR_TYPE);
+ }
+
+ /**
+ * Returns the url to a customized avatar
+ *
+ * @return string
+ */
+ public function getCustomAvatarUrl($size)
+ {
+ if ($this->isNobody()) {
+ return sprintf(
+ '%s/%s_%s.%s',
+ $this->getDefaultAvatarDirectoryUrl(),
+ $this->user_id,
+ $size,
+ self::EXTENSION
+ );
+ }
+
+ return sprintf(
+ '%s/%s_%s.%s?d=%s',
+ $this->getAvatarDirectoryUrl(),
+ $this->user_id,
+ $size,
+ self::EXTENSION,
+ @filemtime($this->getCustomAvatarPath($size)) ?: "0"
+ );
+ }
+
+ /**
+ * Returns the path to a customized avatar
+ *
+ * @return string
+ */
+ public function getCustomAvatarPath($size)
+ {
+ if ($this->isNobody()) {
+ return sprintf(
+ '%s/%s_%s.%s',
+ $this->getDefaultAvatarDirectoryPath(),
+ $this->user_id,
+ $size,
+ self::EXTENSION
+ );
+ }
+
+ return sprintf(
+ '%s/%s_%s.%s',
+ $this->getAvatarDirectoryPath(),
+ $this->user_id,
+ $size,
+ self::EXTENSION
+ );
+ }
+
+ /**
+ * Constructs a new Avatar object belonging to a user with the given id.
+ *
+ * @param string $user_id the user's id
+ * @param string $username the user's username (optional)
+ */
+ protected function __construct($user_id, $username = null)
+ {
+ $this->user_id = $user_id;
+ $this->username = $username;
+
+ $this->checkAvatarVisibility();
+ }
+
+ /**
+ * Returns the file name of a user's avatar.
+ *
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
+ *
+ * @return string the absolute file path to the avatar
+ */
+ public function getFilename($size)
+ {
+ return $this->is_customized()
+ ? $this->getCustomAvatarPath($size)
+ : $this->getNobody()->getCustomAvatarPath($size);
+ }
+
+ /**
+ * Returns the URL of a user's picture.
+ *
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
+ *
+ * @return string the URL to the user's picture
+ */
+ # TODO (mlunzena) in Url umbenennen
+ public function getURL($size)
+ {
+ return $this->is_customized()
+ ? $this->getCustomAvatarUrl($size)
+ : $this->getNobody()->getCustomAvatarUrl($size);
+ }
+
+ /**
+ * Returns whether this avatar is a default/"nobody" avatar.
+ */
+ public function isNobody(): bool
+ {
+ return $this->user_id === static::NOBODY;
+ }
+
+ /**
+ * Returns whether a customized file exists
+ */
+ public function customizedFileExists(): bool
+ {
+ return file_exists($this->getCustomAvatarPath(static::MEDIUM));
+ }
+
+ /**
+ * Returns whether a user has uploaded a custom picture.
+ *
+ * @return boolean returns TRUE if the user customized her picture, FALSE
+ * otherwise.
+ */
+ public function is_customized()
+ {
+ return !$this->isNobody()
+ && $this->customizedFileExists();
+ }
+
+ /**
+ * Returns the CSS class to use for this avatar image.
+ *
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
+ *
+ * @return string CSS class to use for the avatar
+ */
+ protected function getCssClass($size)
+ {
+ if (!isset($this->username)) {
+ $this->username = get_username($this->user_id);
+ }
+
+ return sprintf(
+ 'avatar-%s user-%s' . ($this->is_customized() ? '' : ' recolor'),
+ $size,
+ htmlReady($this->username)
+ );
+ }
+
+ /**
+ * Constructs a desired HTML image tag for an Avatar. Additional
+ * html attributes may also be specified using the $opt parameter.
+ *
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
+ * @param array $opt array of attributes to add to the HTML image tag
+ *
+ * @return string returns the HTML image tag
+ */
+ public function getImageTag($size = self::MEDIUM, $opt = [])
+ {
+ $opt['src'] = $this->getURL($size);
+
+ if (isset($opt['class'])) {
+ $opt['class'] = $this->getCssClass($size) . ' ' . $opt['class'];
+ } else {
+ $opt['class'] = $this->getCssClass($size);
+ }
+
+ // Apply cast to string for title if necessary
+ if (isset($opt['title']) && !is_string($opt['title'])) {
+ $opt['title'] = (string) $opt['title'];
+ }
+
+ if (!empty($opt['title']) && $opt['title'] !== html_entity_decode($opt['title'])) {
+ // Decode already htmlready encoded titles (which were used until
+ // all attributes were encoded inside this method)
+ $opt['title'] = html_entity_decode($opt['title']);
+
+ if (Studip\ENV === 'development') {
+ $trace = debug_backtrace();
+ $caller = array_shift($trace);
+
+ $file = str_replace("{$GLOBALS['STUDIP_BASE_PATH']}/", '', $caller['file']);
+ trigger_error(
+ "{$file}:{$caller['line']}: Passes already encoded title to Avatar::getImageTag()",
+ E_USER_DEPRECATED
+ );
+ }
+ }
+
+ if (!isset($opt['alt']) && !isset($opt['title'])) {
+ //Add an empty alt attribute to prevent screen readers from
+ //reading the URL of the icon:
+ $opt['alt'] = '';
+ }
+
+ return '';
+ }
+
+ /**
+ * Creates all the different sized thumbnails for an uploaded file.
+ *
+ * @param string $userfile the key of the uploaded file, see documentation about $_FILES
+ *
+ * @return void
+ */
+ public function createFromUpload($userfile)
+ {
+ try {
+ // keine Datei ausgewählt!
+ if (!$_FILES[$userfile]['name']) {
+ throw new Exception(_('Sie haben keine Datei zum Hochladen ausgewählt!'));
+ }
+
+ // Fehler beim Hochladen
+ if ($_FILES[$userfile]['error'] !== UPLOAD_ERR_OK) {
+ throw new Exception(_('Es gab einen Fehler beim Hochladen der Datei!'));
+ }
+
+
+ // Bilddatei ist zu groß
+ if ($_FILES[$userfile]['size'] > self::MAX_FILE_SIZE) {
+ throw new Exception(sprintf(
+ _('Die hochgeladene Bilddatei ist %s KB groß. Die maximale Dateigröße beträgt %s KB!'),
+ round($_FILES[$userfile]['size'] / 1024),
+ self::MAX_FILE_SIZE / 1024)
+ );
+ }
+
+ // get extension
+ $pathinfo = pathinfo($_FILES[$userfile]['name']);
+ $ext = mb_strtolower($pathinfo['extension']);
+
+ // passende Endung ?
+ if (!in_array($ext, words('jpg jpeg gif png webp'))) {
+ throw new Exception(sprintf(
+ _('Der Dateityp der Bilddatei ist falsch (%s). Es sind nur die Dateiendungen .gif, .png, .jpeg, .jpg oder .webp erlaubt!'),
+ $ext
+ ));
+ }
+
+ // na dann kopieren wir mal...
+ $filename = tempnam($GLOBALS['TMP_PATH'], 'avatar-upload');
+
+ if (!@move_uploaded_file($_FILES[$userfile]['tmp_name'], $filename)) {
+ throw new Exception(_("Es ist ein Fehler beim Kopieren der Datei aufgetreten. Das Bild wurde nicht hochgeladen!"));
+ }
+
+ // set permissions for uploaded file
+ @chmod($filename, 0666 & ~umask());
+
+ $this->sanitizeOrientation($filename);
+ $this->createFrom($filename);
+ } finally {
+ if (isset($filename)) {
+ @unlink($filename);
+ }
+ }
+ }
+
+ /**
+ * Creates thumbnails from an image.
+ *
+ * @param string $filename filename of the image to create thumbnails from
+ *
+ * @return void
+ */
+ public function createFrom($filename)
+ {
+ if (!extension_loaded('gd')) {
+ throw new Exception(_('Es ist ein Fehler beim Bearbeiten des Bildes aufgetreten.') . ' (' . _('Fehlende GD-Lib') . ')');
+ }
+
+ set_error_handler([__CLASS__, 'error_handler']);
+
+ NotificationCenter::postNotification('AvatarWillCreate', $this->user_id);
+ $this->resize(static::NORMAL, $filename);
+ $this->resize(static::MEDIUM, $filename);
+ $this->resize(static::SMALL, $filename);
+ NotificationCenter::postNotification('AvatarDidCreate', $this->user_id);
+
+ restore_error_handler();
+ }
+
+ /**
+ * Removes all uploaded pictures of a user.
+ */
+ public function reset()
+ {
+ if ($this->is_customized()) {
+ NotificationCenter::postNotification('AvatarWillDelete', $this->user_id);
+ @unlink($this->getCustomAvatarPath(static::NORMAL));
+ @unlink($this->getCustomAvatarPath(static::SMALL));
+ @unlink($this->getCustomAvatarPath(static::MEDIUM));
+ NotificationCenter::postNotification('AvatarDidDelete', $this->user_id);
+ }
+ }
+
+ /**
+ * Return the dimension of a size
+ *
+ * @param string $size the dimension of a size
+ * @return array{0: int, 1: int} a tupel of integers [width, height]
+ */
+ public static function getDimension($size)
+ {
+ $dimensions = [
+ static::NORMAL => [250, 250],
+ static::MEDIUM => [100, 100],
+ static::SMALL => [25, 25]
+ ];
+ return $dimensions[$size];
+ }
+
+ /**
+ * Create from an image thumbnails of a specified size.
+ *
+ * @param string $size the size of the thumbnail to create
+ * @param string $filename the filename of the image to make thumbnail of
+ */
+ private function resize(string $size, string $filename)
+ {
+ [$thumb_width, $thumb_height] = static::getDimension($size);
+
+ $thumb_width = $thumb_width * 2;
+ $thumb_height = $thumb_height * 2;
+
+ [$width, $height, $type] = getimagesize($filename);
+
+ # create image resource from filename
+ $lookup = [
+ IMAGETYPE_GIF => 'imagecreatefromgif',
+ IMAGETYPE_JPEG => 'imagecreatefromjpeg',
+ IMAGETYPE_PNG => 'imagecreatefrompng',
+ IMAGETYPE_WEBP => 'imagecreatefromwebp',
+ ];
+ if (!isset($lookup[$type])) {
+ throw new Exception(_("Der Typ des Bilds wird nicht unterstützt."));
+ }
+ $image = $lookup[$type]($filename);
+
+ imagealphablending($image, false);
+ imagesavealpha($image, true);
+
+ # resize image if needed
+ if ($height > $thumb_height || $width > $thumb_width) {
+ $factor = max($thumb_width / $width, $thumb_height / $height);
+ $resized_width = round($width * $factor);
+ $resized_height = round($height * $factor);
+ } else {
+ $resized_width = $width;
+ $resized_height = $height;
+ }
+
+ $image = self::imageresize($image, $width, $height, $resized_width, $resized_height);
+
+ $dst = imagecreatetruecolor($thumb_width, $thumb_height);
+ imagealphablending($dst, false);
+ imagesavealpha($dst, true);
+
+ $trans_colour = imagecolorallocatealpha($dst, 0, 0, 0, 127);
+ imagefill($dst, 0, 0, $trans_colour);
+
+ // center the new image
+ $ypos = intval($thumb_height - $resized_height) >> 1;
+ $xpos = intval($thumb_width - $resized_width) >> 1;
+
+ imagecopy(
+ $dst, $image,
+ $xpos, $ypos,
+ 0, 0,
+ $resized_width, $resized_height
+ );
+
+ $output_file = $this->getCustomAvatarPath($size);
+ $directory = dirname($output_file);
+ if (!is_dir($directory) && !mkdir($directory)) {
+ throw new Exception(_('Das Verzeichnis zum Speichern der Datei konnte nicht angelegt werden.'));
+ }
+
+ imagewebp($dst, $output_file, self::IMAGE_QUALITY);
+ }
+
+ private function imageresize($image, $current_width, $current_height, $width, $height)
+ {
+ $image_resized = imagecreatetruecolor($width, $height);
+
+ imagealphablending($image_resized, false);
+ imagesavealpha($image_resized, true);
+ imagecopyresampled(
+ $image_resized, $image,
+ 0, 0,
+ 0, 0,
+ $width, $height,
+ $current_width, $current_height
+ );
+
+ return $image_resized;
+ }
+
+ public static function error_handler($errno, $errstr, $errfile, $errline)
+ {
+ if (defined('E_RECOVERABLE_ERROR')
+ && $errno == constant('E_RECOVERABLE_ERROR'))
+ {
+ $message = sprintf(
+ 'Recoverable error "%s" occured in file %s line %u.',
+ $errstr,
+ $errfile,
+ $errline
+ );
+ throw new Exception($message);
+ }
+
+ # execute PHP internal error handler
+ return false;
+ }
+
+ /**
+ * Return the default title of the avatar.
+ * @return string the default title
+ */
+ public function getDefaultTitle()
+ {
+ if ($this->isNobody()) {
+ return static::NOBODY;
+ }
+
+ require_once 'lib/functions.php';
+ return get_fullname($this->user_id);
+ }
+
+ /**
+ * Return if avatar is visible to the current user.
+ * Also set the user_id of avatar to nobody if not visible to current user.
+ * @return boolean: true if visible
+ */
+ protected function checkAvatarVisibility()
+ {
+ $visible = Visibility::verify('picture', $this->user_id);
+ if (!$visible) {
+ $this->user_id = self::NOBODY;
+ }
+ return $visible;
+ }
+
+ /**
+ * Corrects the orientation of images from iOS/OS X devices which might
+ * lead to a rotated image. EXIF information is checked and when the
+ * orientation is set by EXIF data, we rotate the image accordingly.
+ *
+ * @param string $filename Filename of the image to correct
+ */
+ protected function sanitizeOrientation($filename)
+ {
+ if (!function_exists('exif_read_data')) {
+ return;
+ }
+
+ if (exif_imagetype($filename) !== IMAGETYPE_JPEG) {
+ return;
+ }
+
+ $exif = exif_read_data($filename);
+ if (!$exif || !$exif['Orientation'] || $exif['Orientation'] == 1) {
+ return;
+ }
+
+ $degree = 0;
+ switch ($exif['Orientation']) {
+ case 3:
+ $degree = 180;
+ break;
+ case 6:
+ $degree = -90;
+ break;
+ case 8:
+ $degree = 90;
+ break;
+ }
+
+ if ($degree) {
+ $img = imagecreatefromstring(file_get_contents($filename));
+ $img = imagerotate($img, $degree, 0);
+
+ $extension = pathinfo($filename, PATHINFO_EXTENSION);
+ if ($extension === 'jpg' || $extension === 'jpeg') {
+ imagejpeg($img, $filename, 95);
+ } elseif ($extension === 'gif') {
+ imagegif($img, $filename);
+ } elseif ($extension === 'png') {
+ imagepng($img, $filename, 9);
+ } else {
+ imagewebp($img, $filename, self::IMAGE_QUALITY);
+ }
+
+ imagedestroy($img);
+ }
+ }
+}
diff --git a/lib/classes/BreadCrumb.class.php b/lib/classes/BreadCrumb.class.php
deleted file mode 100644
index 73a2086..0000000
--- a/lib/classes/BreadCrumb.class.php
+++ /dev/null
@@ -1,103 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @since 3.5
- */
-class BreadCrumb
-{
-
- /**
- * Array with parts of bread crumb navigation.
- *
- * @var array
- */
- private $trail = [];
-
- public function __construct()
- {
- URLHelper::bindLinkParam('trail', $this->trail);
- }
-
- /**
- * Appends a new element to the end of the bread crumb navigation.
- *
- * @param MvvTreeItem $object The MvvTreeItem object of the current view
- * to append.
- * @param string $action The url to the current view.
- */
- public function append($object, $action)
- {
- $trail = $this->getTrail();
- $id = '';
- if (is_object($object)) {
- $type = get_class($object);
- $id = $object->id;
- $trail[$type] = [
- 'id' => $object->id,
- 'actn' => $action
- ];
- } else if (is_array($object)) {
- $id = reset($object)->id;
- $type = get_class(reset($object));
- foreach ($object as $obj) {
- if ($obj && $obj->id != $id) {
- $additional_objects[get_class($obj)] = $obj->id;
- }
- }
- $trail[$type] = [
- 'id' => $id,
- 'add' => $additional_objects,
- 'actn' => $action
- ];
- } else {
- $trail[$action] = [
- 'name' => $object,
- 'actn' => $action
- ];
- }
- $newTrail = [];
- $lastElement = false;
- foreach ($trail as $key => $trail_item) {
- if ($lastElement) break;
- $newTrail[$key] = $trail_item;
- $lastElement = $key === $id;
- }
- $this->trail = $newTrail;
- }
-
- /**
- * Removes the last element from the bread crumb navigation.
- */
- public function pop()
- {
- array_pop($this->trail);
- }
-
- /**
- * Returns all elements of the bread crumb navigation.
- *
- * @return array All elements of the bread crumb navigation
- */
- public function getTrail()
- {
- return $this->trail;
- }
-
- /**
- * Initialize a new bread crumb navigation.
- */
- public function init()
- {
- $this->trail = [];
- }
-
-}
diff --git a/lib/classes/BreadCrumb.php b/lib/classes/BreadCrumb.php
new file mode 100644
index 0000000..73a2086
--- /dev/null
+++ b/lib/classes/BreadCrumb.php
@@ -0,0 +1,103 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 3.5
+ */
+class BreadCrumb
+{
+
+ /**
+ * Array with parts of bread crumb navigation.
+ *
+ * @var array
+ */
+ private $trail = [];
+
+ public function __construct()
+ {
+ URLHelper::bindLinkParam('trail', $this->trail);
+ }
+
+ /**
+ * Appends a new element to the end of the bread crumb navigation.
+ *
+ * @param MvvTreeItem $object The MvvTreeItem object of the current view
+ * to append.
+ * @param string $action The url to the current view.
+ */
+ public function append($object, $action)
+ {
+ $trail = $this->getTrail();
+ $id = '';
+ if (is_object($object)) {
+ $type = get_class($object);
+ $id = $object->id;
+ $trail[$type] = [
+ 'id' => $object->id,
+ 'actn' => $action
+ ];
+ } else if (is_array($object)) {
+ $id = reset($object)->id;
+ $type = get_class(reset($object));
+ foreach ($object as $obj) {
+ if ($obj && $obj->id != $id) {
+ $additional_objects[get_class($obj)] = $obj->id;
+ }
+ }
+ $trail[$type] = [
+ 'id' => $id,
+ 'add' => $additional_objects,
+ 'actn' => $action
+ ];
+ } else {
+ $trail[$action] = [
+ 'name' => $object,
+ 'actn' => $action
+ ];
+ }
+ $newTrail = [];
+ $lastElement = false;
+ foreach ($trail as $key => $trail_item) {
+ if ($lastElement) break;
+ $newTrail[$key] = $trail_item;
+ $lastElement = $key === $id;
+ }
+ $this->trail = $newTrail;
+ }
+
+ /**
+ * Removes the last element from the bread crumb navigation.
+ */
+ public function pop()
+ {
+ array_pop($this->trail);
+ }
+
+ /**
+ * Returns all elements of the bread crumb navigation.
+ *
+ * @return array All elements of the bread crumb navigation
+ */
+ public function getTrail()
+ {
+ return $this->trail;
+ }
+
+ /**
+ * Initialize a new bread crumb navigation.
+ */
+ public function init()
+ {
+ $this->trail = [];
+ }
+
+}
diff --git a/lib/classes/Button.class.php b/lib/classes/Button.class.php
deleted file mode 100644
index 1c2c437..0000000
--- a/lib/classes/Button.class.php
+++ /dev/null
@@ -1,58 +0,0 @@
- HTML element.
- *
- * @param string $label the label of the button element
- * @param string $name the @name element of the button element
- * @param array $attributes the attributes of the button element
- */
- protected function initialize($label, $name, $attributes)
- {
- $this->attributes['name'] = $name ?: $this->label;
- }
-
- /**
- * @return string returns a HTML representation of this button.
- */
- public function __toString()
- {
- // add "button" to attribute @class
- if (!isset($this->attributes['class'])) {
- $this->attributes['class'] = '';
- }
- $this->attributes['class'] .= ' button';
-
- $attributes = [];
- ksort($this->attributes);
- foreach ($this->attributes as $k => $v) {
- $attributes[] = sprintf(' %s="%s"', $k, htmlReady($v));
- }
-
- return sprintf(
- '',
- join('', $attributes),
- htmlReady($this->label)
- );
- }
-}
diff --git a/lib/classes/Button.php b/lib/classes/Button.php
new file mode 100644
index 0000000..1c2c437
--- /dev/null
+++ b/lib/classes/Button.php
@@ -0,0 +1,58 @@
+ HTML element.
+ *
+ * @param string $label the label of the button element
+ * @param string $name the @name element of the button element
+ * @param array $attributes the attributes of the button element
+ */
+ protected function initialize($label, $name, $attributes)
+ {
+ $this->attributes['name'] = $name ?: $this->label;
+ }
+
+ /**
+ * @return string returns a HTML representation of this button.
+ */
+ public function __toString()
+ {
+ // add "button" to attribute @class
+ if (!isset($this->attributes['class'])) {
+ $this->attributes['class'] = '';
+ }
+ $this->attributes['class'] .= ' button';
+
+ $attributes = [];
+ ksort($this->attributes);
+ foreach ($this->attributes as $k => $v) {
+ $attributes[] = sprintf(' %s="%s"', $k, htmlReady($v));
+ }
+
+ return sprintf(
+ '',
+ join('', $attributes),
+ htmlReady($this->label)
+ );
+ }
+}
diff --git a/lib/classes/CSVArrayObject.class.php b/lib/classes/CSVArrayObject.class.php
deleted file mode 100644
index f075795..0000000
--- a/lib/classes/CSVArrayObject.class.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- * @link http://www.php.net/manual/en/class.arrayobject.php
- */
-class CSVArrayObject extends StudipArrayObject
-{
- /**
- * Construct an array object from a string of comma separated items
- *
- * @param string $input a string of comma separated items
- */
- function __construct($input)
- {
- if (is_string($input)) {
- $input = mb_strlen($input) ? array_map('trim', explode(',', $input)) : [];
- }
- parent::__construct((array)$input);
- }
-
- /**
- * magic method for use of object in string context
- *
- * @return string internal array itmes converted to a comma separated list
- */
- function __toString()
- {
- return implode(',', $this->getArrayCopy());
- }
-}
diff --git a/lib/classes/CSVArrayObject.php b/lib/classes/CSVArrayObject.php
new file mode 100644
index 0000000..f075795
--- /dev/null
+++ b/lib/classes/CSVArrayObject.php
@@ -0,0 +1,47 @@
+
+ * @link http://www.php.net/manual/en/class.arrayobject.php
+ */
+class CSVArrayObject extends StudipArrayObject
+{
+ /**
+ * Construct an array object from a string of comma separated items
+ *
+ * @param string $input a string of comma separated items
+ */
+ function __construct($input)
+ {
+ if (is_string($input)) {
+ $input = mb_strlen($input) ? array_map('trim', explode(',', $input)) : [];
+ }
+ parent::__construct((array)$input);
+ }
+
+ /**
+ * magic method for use of object in string context
+ *
+ * @return string internal array itmes converted to a comma separated list
+ */
+ function __toString()
+ {
+ return implode(',', $this->getArrayCopy());
+ }
+}
diff --git a/lib/classes/Color.class.php b/lib/classes/Color.class.php
deleted file mode 100644
index a9b4506..0000000
--- a/lib/classes/Color.class.php
+++ /dev/null
@@ -1,370 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-/**
- * class to mix colors and convert them between different types
- *
- * @since 2.1
- */
-class Color {
- /**
- * HTML-4-Standard color-names plus a few additions
- * May be expanded in a later version to allow
- * X11 color names as described in
- * http://en.wikipedia.org/wiki/Web_colors
- *
- * @var array
- */
- static $colorstrings = [
- 'aqua' => [0, 255, 255, 1],
- 'azure' => [240, 255, 255, 1],
- 'blue' => [0, 0, 255, 1],
- 'cyan' => [0, 255, 255, 1],
- 'darkblue' => [0, 0, 139, 1],
- 'darkred' => [139, 0, 0, 1],
- 'gold' => [255, 215, 0, 1],
- 'gray' => [128, 128, 128, 1],
- 'indigo' => [75, 0, 130, 1],
- 'lightsteelblue' => [176, 196, 222, 1],
- 'lightyellow' => [255, 255, 224, 1],
- 'lime' => [0, 255, 0, 1],
- 'magenta' => [255, 0, 255, 1],
- 'navy' => [0, 0, 128, 1],
- 'olive' => [128, 128, 0, 1],
- 'orange' => [255, 165, 0, 1],
- 'pink' => [255, 192, 203, 1],
- 'red' => [255, 0, 0, 1],
- 'purple' => [128, 0, 128, 1],
- 'violet' => [238, 130, 238, 1],
- 'yellow' => [255, 255, 0, 1],
- 'black' => [0, 0, 0, 1],
- 'white' => [255, 255, 255, 1]
- ];
-
- /**
- * converts a css-hex-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function hex2array($color) {
- $color = str_replace('#','',$color);
- $arr[0] = hexdec(mb_substr($color,0,2));
- $arr[1] = hexdec(mb_substr($color,2,2));
- $arr[2] = hexdec(mb_substr($color,4,2));
- $arr[3] = 1.0;
- return $arr;
- }
-
- /**
- * converts a css-rgba-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function rgba2array($color) {
- preg_match("/rgba\(\s*(\d+\%?),\s*(\d+\%?),\s*(\d+\%?),\s*(\d*\.?\d*)\s*\)/", $color, $matches);
- array_shift($matches);
- $matches[3] = floatval($matches[3]);
- return $matches;
- }
-
- /**
- * converts a css-rgb-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function rgb2array($color) {
- preg_match("/rgb\(\s*(\d+\%?),\s*(\d+\%?),\s*(\d+\%?)\s*\)/", $color, $matches);
- array_shift($matches);
- $matches[3] = 1.0;
- return $matches;
- }
-
- /**
- * converts a css-hsl-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function hsl2array($color) {
- preg_match("/hsl\(\s*(\d+),\s*(\d+)\%,\s*(\d+)\%\s*\)/", $color, $matches);
- array_shift($matches);
- $matches[0] %= 360;
- $matches[3] = 1.0;
- $h = $matches[0];
- $s = $matches[1];
- $l = $matches[2];
- $m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s;
- $m1 = $l * 2 - $m2;
- return [self::_color_hue2rgb($m1, $m2, $h + 0.33333),
- self::_color_hue2rgb($m1, $m2, $h),
- self::_color_hue2rgb($m1, $m2, $h - 0.33333),
- 1.0
- ];
- }
-
- static private function _color_hue2rgb($m1, $m2, $h) {
- $h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
- if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
- if ($h * 2 < 1) return $m2;
- if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
- return $m1;
- }
-
- /**
- * Mixes two colors by preferring the first one with $percentOfColor1 percent.
- * If color2 is the only color with alpha-chanel, it will return a color
- * in the format of color2 else (most of the time) in the format of color1.
- * @return string: "#ffffff", "rgb(...)", "rgba(...)" depending on color1 and alpha-chanel
- */
- static function mix($color1, $color2, $percentOfColor1 = 50, $f = null) {
- $percentOfColor1 = $percentOfColor1 > 100
- ? 100
- : ($percentOfColor1 < 0 ? 0 : $percentOfColor1);
- list($color1, $format1) = self::_normalize($color1);
- list($color2, $format2) = self::_normalize($color2);
- $color_new[0] = floor(($color1[0] * $percentOfColor1
- + $color2[0] * (100 - $percentOfColor1)) / 100);
- $color_new[1] = floor(($color1[1] * $percentOfColor1
- + $color2[1] * (100 - $percentOfColor1)) / 100);
- $color_new[2] = floor(($color1[2] * $percentOfColor1
- + $color2[2] * (100 - $percentOfColor1)) / 100);
- $color_new[3] = ($color1[3] * $percentOfColor1
- + $color2[3] * (100 - $percentOfColor1)) / 100;
- $format = ((mb_strpos($format2, "a") !== false) && (mb_strpos($format1, "a") === false))
- ? $format2
- : $format1;
- $func = "_array2" . $format;
- return self::$func($color_new);
- }
-
- /**
- * Sets the opacity for a color. Hue and saturation is kept, but opacity is changed.
- * Returns a format with alpha-chanel (rgba or hsla) depending on the given format.
- */
- static function opacity($color, $opacity) {
- list($color, $format) = self::_normalize($color);
- if (in_array($format, ["hex", "rgb"])) {
- $format = "rgba";
- } elseif ($format = "hsl") {
- $format = "hsla";
- }
- $color[3] = floatval($opacity);
- $color[3] = $color[3] < 0 ? 0 : ($color[3] > 1 ? 1 : $color[3]);
- $func = "_array2" . $format;
- return self::$func($color);
- }
-
- /**
- * Make the passed color brighter
- *
- * @author Till Glöggler
- *
- * @param string $color any type of css-valid color
- * @param int $factor percentage of brightning, 100 for white and 0 for no changes to color
- * @return string brightened color in same format as the passed one
- */
- static function brighten($color, $factor = 35) {
- // convert to color to rgba
- list($color, $format) = self::_normalize($color);
- if ($factor > 100) {
- $factor = 100;
- } elseif($factor < 0) {
- $factor = 0;
- }
-
- // return the color itself, if the conversion failed
- if (!$format) return $color;
-
- // brighten the color
- $color[0] = floor(($color[0] * (100 - $factor) + 255 * $factor) / 100);
- $color[1] = floor(($color[1] * (100 - $factor) + 255 * $factor) / 100);
- $color[2] = floor(($color[2] * (100 - $factor) + 255 * $factor) / 100);
-
- // convert the color back (if possible)
- $func = "_array2" . $format;
- return self::$func($color);
- }
-
- /**
- * converts any css-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- private static function _normalize($color) {
- if ($color[0] === "#") {
- $format = "hex";
- $arr = self::hex2array($color);
- } elseif (preg_match("/\(.*\)/", $color)) {
- $format = mb_substr($color, 0, mb_strpos($color, "("));
- $func = $format."2array";
- $arr = self::$func($color);
- } elseif (self::$colorstrings[mb_strtolower($color)]) {
- $format = "rgb"; //we don't want colors as strings like "red"
- $arr = self::$colorstrings[mb_strtolower($color)];
- }
- return [$arr, $format];
- }
-
- /**
- * converts a css-rgba-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function rgba($color) {
- list($arr, $format) = self::_normalize($color);
- return self::_array2rgba($arr);
- }
-
- /**
- * converts a css-rgb-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function rgb($color) {
- list($arr, $format) = self::_normalize($color);
- return self::_array2rgb($arr);
- }
-
- /**
- * converts a css-hex-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function hex($color) {
- list($arr, $format) = self::_normalize($color);
- return self::_array2hex($arr);
- }
-
- /**
- * converts a css-hsl-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function hsl($color) {
- list($arr, $format) = self::_normalize($color);
- return self::_array2hsl($arr);
- }
-
- /**
- * converts a css-hsla-color into a rgba-quadruple
- *
- * @param string $color the color to be converted
- * @return array colors as rgba-quadruple
- */
- static function hsla($color) {
- list($arr, $format) = self::_normalize($color);
- return self::_array2hsla($arr);
- }
-
-
- /**
- * converts a rgba-quadruple into a css-hex-color
- *
- * @param array $arr the rgba-quadruple to be converted
- * @return array colors as css-hex
- */
- static private function _array2hex($arr) {
- return "#" .
- ($arr[0] < 16 ? "0" : "").dechex($arr[0]) .
- ($arr[1] < 16 ? "0" : "").dechex($arr[1]) .
- ($arr[2] < 16 ? "0" : "").dechex($arr[2]);
- }
-
- /**
- * converts a rgba-quadruple into a css-rgb-color
- *
- * @param array $arr the rgba-quadruple to be converted
- * @return array colors as css-rgb
- */
- static private function _array2rgb($arr) {
- array_pop($arr);
- return "rgb(".implode(", ", $arr).")";
- }
-
- /**
- * converts a rgba-quadruple into a css-rgba-color
- *
- * @param array $arr the rgba-quadruple to be converted
- * @return array colors as css-rgba
- */
- static private function _array2rgba($arr) {
- return "rgba(".implode(", ", $arr).")";
- }
-
- /**
- * converts a rgba-quadruple into a css-hsl-color
- *
- * @param array $arr the rgba-quadruple to be converted
- * @return array colors as css-hsl
- */
- static private function _array2hsl($arr) {
- $arr = self::_calculate_hsl($arr);
- return "hsl(".$arr[0].", ".$arr[1]."%, ".$arr[2]."%)";
- }
-
- /**
- * converts a rgba-quadruple into a css-hsla-color
- *
- * @param array $arr the rgba-quadruple to be converted
- * @return array colors as css-hsla
- */
- static private function _array2hsla($arr) {
- $hsl = self::_calculate_hsl($arr);
- return "hsl(".$hsl[0].", ".$hsl[1]."%, ".$hsl[2]."%, ".$arr[3].")";
- }
-
- static private function _calculate_hsl($arr) {
- $clrR = ($arr[0]);
- $clrG = ($arr[1]);
- $clrB = ($arr[2]);
-
- $clrMin = min($clrR, $clrG, $clrB);
- $clrMax = max($clrR, $clrG, $clrB);
- $deltaMax = $clrMax - $clrMin;
-
- $L = ($clrMax + $clrMin) / 510;
-
- if (0 == $deltaMax) {
- $H = 0;
- $S = 0;
- } else {
- if (0.5 > $L) {
- $S = $deltaMax / ($clrMax + $clrMin);
- } else {
- $S = $deltaMax / (510 - $clrMax - $clrMin);
- }
-
- if ($clrMax == $clrR) {
- $H = ($clrG - $clrB) / (6.0 * $deltaMax);
- } else if ($clrMax == $clrG) {
- $H = 1/3 + ($clrB - $clrR) / (6.0 * $deltaMax);
- } else {
- $H = 2 / 3 + ($clrR - $clrG) / (6.0 * $deltaMax);
- }
-
- if (0 > $H) $H += 1;
- if (1 < $H) $H -= 1;
- }
- return [$H, $S, $L];
- }
-
-}
\ No newline at end of file
diff --git a/lib/classes/Color.php b/lib/classes/Color.php
new file mode 100644
index 0000000..a9b4506
--- /dev/null
+++ b/lib/classes/Color.php
@@ -0,0 +1,370 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+/**
+ * class to mix colors and convert them between different types
+ *
+ * @since 2.1
+ */
+class Color {
+ /**
+ * HTML-4-Standard color-names plus a few additions
+ * May be expanded in a later version to allow
+ * X11 color names as described in
+ * http://en.wikipedia.org/wiki/Web_colors
+ *
+ * @var array
+ */
+ static $colorstrings = [
+ 'aqua' => [0, 255, 255, 1],
+ 'azure' => [240, 255, 255, 1],
+ 'blue' => [0, 0, 255, 1],
+ 'cyan' => [0, 255, 255, 1],
+ 'darkblue' => [0, 0, 139, 1],
+ 'darkred' => [139, 0, 0, 1],
+ 'gold' => [255, 215, 0, 1],
+ 'gray' => [128, 128, 128, 1],
+ 'indigo' => [75, 0, 130, 1],
+ 'lightsteelblue' => [176, 196, 222, 1],
+ 'lightyellow' => [255, 255, 224, 1],
+ 'lime' => [0, 255, 0, 1],
+ 'magenta' => [255, 0, 255, 1],
+ 'navy' => [0, 0, 128, 1],
+ 'olive' => [128, 128, 0, 1],
+ 'orange' => [255, 165, 0, 1],
+ 'pink' => [255, 192, 203, 1],
+ 'red' => [255, 0, 0, 1],
+ 'purple' => [128, 0, 128, 1],
+ 'violet' => [238, 130, 238, 1],
+ 'yellow' => [255, 255, 0, 1],
+ 'black' => [0, 0, 0, 1],
+ 'white' => [255, 255, 255, 1]
+ ];
+
+ /**
+ * converts a css-hex-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function hex2array($color) {
+ $color = str_replace('#','',$color);
+ $arr[0] = hexdec(mb_substr($color,0,2));
+ $arr[1] = hexdec(mb_substr($color,2,2));
+ $arr[2] = hexdec(mb_substr($color,4,2));
+ $arr[3] = 1.0;
+ return $arr;
+ }
+
+ /**
+ * converts a css-rgba-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function rgba2array($color) {
+ preg_match("/rgba\(\s*(\d+\%?),\s*(\d+\%?),\s*(\d+\%?),\s*(\d*\.?\d*)\s*\)/", $color, $matches);
+ array_shift($matches);
+ $matches[3] = floatval($matches[3]);
+ return $matches;
+ }
+
+ /**
+ * converts a css-rgb-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function rgb2array($color) {
+ preg_match("/rgb\(\s*(\d+\%?),\s*(\d+\%?),\s*(\d+\%?)\s*\)/", $color, $matches);
+ array_shift($matches);
+ $matches[3] = 1.0;
+ return $matches;
+ }
+
+ /**
+ * converts a css-hsl-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function hsl2array($color) {
+ preg_match("/hsl\(\s*(\d+),\s*(\d+)\%,\s*(\d+)\%\s*\)/", $color, $matches);
+ array_shift($matches);
+ $matches[0] %= 360;
+ $matches[3] = 1.0;
+ $h = $matches[0];
+ $s = $matches[1];
+ $l = $matches[2];
+ $m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s;
+ $m1 = $l * 2 - $m2;
+ return [self::_color_hue2rgb($m1, $m2, $h + 0.33333),
+ self::_color_hue2rgb($m1, $m2, $h),
+ self::_color_hue2rgb($m1, $m2, $h - 0.33333),
+ 1.0
+ ];
+ }
+
+ static private function _color_hue2rgb($m1, $m2, $h) {
+ $h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
+ if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
+ if ($h * 2 < 1) return $m2;
+ if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
+ return $m1;
+ }
+
+ /**
+ * Mixes two colors by preferring the first one with $percentOfColor1 percent.
+ * If color2 is the only color with alpha-chanel, it will return a color
+ * in the format of color2 else (most of the time) in the format of color1.
+ * @return string: "#ffffff", "rgb(...)", "rgba(...)" depending on color1 and alpha-chanel
+ */
+ static function mix($color1, $color2, $percentOfColor1 = 50, $f = null) {
+ $percentOfColor1 = $percentOfColor1 > 100
+ ? 100
+ : ($percentOfColor1 < 0 ? 0 : $percentOfColor1);
+ list($color1, $format1) = self::_normalize($color1);
+ list($color2, $format2) = self::_normalize($color2);
+ $color_new[0] = floor(($color1[0] * $percentOfColor1
+ + $color2[0] * (100 - $percentOfColor1)) / 100);
+ $color_new[1] = floor(($color1[1] * $percentOfColor1
+ + $color2[1] * (100 - $percentOfColor1)) / 100);
+ $color_new[2] = floor(($color1[2] * $percentOfColor1
+ + $color2[2] * (100 - $percentOfColor1)) / 100);
+ $color_new[3] = ($color1[3] * $percentOfColor1
+ + $color2[3] * (100 - $percentOfColor1)) / 100;
+ $format = ((mb_strpos($format2, "a") !== false) && (mb_strpos($format1, "a") === false))
+ ? $format2
+ : $format1;
+ $func = "_array2" . $format;
+ return self::$func($color_new);
+ }
+
+ /**
+ * Sets the opacity for a color. Hue and saturation is kept, but opacity is changed.
+ * Returns a format with alpha-chanel (rgba or hsla) depending on the given format.
+ */
+ static function opacity($color, $opacity) {
+ list($color, $format) = self::_normalize($color);
+ if (in_array($format, ["hex", "rgb"])) {
+ $format = "rgba";
+ } elseif ($format = "hsl") {
+ $format = "hsla";
+ }
+ $color[3] = floatval($opacity);
+ $color[3] = $color[3] < 0 ? 0 : ($color[3] > 1 ? 1 : $color[3]);
+ $func = "_array2" . $format;
+ return self::$func($color);
+ }
+
+ /**
+ * Make the passed color brighter
+ *
+ * @author Till Glöggler
+ *
+ * @param string $color any type of css-valid color
+ * @param int $factor percentage of brightning, 100 for white and 0 for no changes to color
+ * @return string brightened color in same format as the passed one
+ */
+ static function brighten($color, $factor = 35) {
+ // convert to color to rgba
+ list($color, $format) = self::_normalize($color);
+ if ($factor > 100) {
+ $factor = 100;
+ } elseif($factor < 0) {
+ $factor = 0;
+ }
+
+ // return the color itself, if the conversion failed
+ if (!$format) return $color;
+
+ // brighten the color
+ $color[0] = floor(($color[0] * (100 - $factor) + 255 * $factor) / 100);
+ $color[1] = floor(($color[1] * (100 - $factor) + 255 * $factor) / 100);
+ $color[2] = floor(($color[2] * (100 - $factor) + 255 * $factor) / 100);
+
+ // convert the color back (if possible)
+ $func = "_array2" . $format;
+ return self::$func($color);
+ }
+
+ /**
+ * converts any css-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ private static function _normalize($color) {
+ if ($color[0] === "#") {
+ $format = "hex";
+ $arr = self::hex2array($color);
+ } elseif (preg_match("/\(.*\)/", $color)) {
+ $format = mb_substr($color, 0, mb_strpos($color, "("));
+ $func = $format."2array";
+ $arr = self::$func($color);
+ } elseif (self::$colorstrings[mb_strtolower($color)]) {
+ $format = "rgb"; //we don't want colors as strings like "red"
+ $arr = self::$colorstrings[mb_strtolower($color)];
+ }
+ return [$arr, $format];
+ }
+
+ /**
+ * converts a css-rgba-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function rgba($color) {
+ list($arr, $format) = self::_normalize($color);
+ return self::_array2rgba($arr);
+ }
+
+ /**
+ * converts a css-rgb-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function rgb($color) {
+ list($arr, $format) = self::_normalize($color);
+ return self::_array2rgb($arr);
+ }
+
+ /**
+ * converts a css-hex-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function hex($color) {
+ list($arr, $format) = self::_normalize($color);
+ return self::_array2hex($arr);
+ }
+
+ /**
+ * converts a css-hsl-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function hsl($color) {
+ list($arr, $format) = self::_normalize($color);
+ return self::_array2hsl($arr);
+ }
+
+ /**
+ * converts a css-hsla-color into a rgba-quadruple
+ *
+ * @param string $color the color to be converted
+ * @return array colors as rgba-quadruple
+ */
+ static function hsla($color) {
+ list($arr, $format) = self::_normalize($color);
+ return self::_array2hsla($arr);
+ }
+
+
+ /**
+ * converts a rgba-quadruple into a css-hex-color
+ *
+ * @param array $arr the rgba-quadruple to be converted
+ * @return array colors as css-hex
+ */
+ static private function _array2hex($arr) {
+ return "#" .
+ ($arr[0] < 16 ? "0" : "").dechex($arr[0]) .
+ ($arr[1] < 16 ? "0" : "").dechex($arr[1]) .
+ ($arr[2] < 16 ? "0" : "").dechex($arr[2]);
+ }
+
+ /**
+ * converts a rgba-quadruple into a css-rgb-color
+ *
+ * @param array $arr the rgba-quadruple to be converted
+ * @return array colors as css-rgb
+ */
+ static private function _array2rgb($arr) {
+ array_pop($arr);
+ return "rgb(".implode(", ", $arr).")";
+ }
+
+ /**
+ * converts a rgba-quadruple into a css-rgba-color
+ *
+ * @param array $arr the rgba-quadruple to be converted
+ * @return array colors as css-rgba
+ */
+ static private function _array2rgba($arr) {
+ return "rgba(".implode(", ", $arr).")";
+ }
+
+ /**
+ * converts a rgba-quadruple into a css-hsl-color
+ *
+ * @param array $arr the rgba-quadruple to be converted
+ * @return array colors as css-hsl
+ */
+ static private function _array2hsl($arr) {
+ $arr = self::_calculate_hsl($arr);
+ return "hsl(".$arr[0].", ".$arr[1]."%, ".$arr[2]."%)";
+ }
+
+ /**
+ * converts a rgba-quadruple into a css-hsla-color
+ *
+ * @param array $arr the rgba-quadruple to be converted
+ * @return array colors as css-hsla
+ */
+ static private function _array2hsla($arr) {
+ $hsl = self::_calculate_hsl($arr);
+ return "hsl(".$hsl[0].", ".$hsl[1]."%, ".$hsl[2]."%, ".$arr[3].")";
+ }
+
+ static private function _calculate_hsl($arr) {
+ $clrR = ($arr[0]);
+ $clrG = ($arr[1]);
+ $clrB = ($arr[2]);
+
+ $clrMin = min($clrR, $clrG, $clrB);
+ $clrMax = max($clrR, $clrG, $clrB);
+ $deltaMax = $clrMax - $clrMin;
+
+ $L = ($clrMax + $clrMin) / 510;
+
+ if (0 == $deltaMax) {
+ $H = 0;
+ $S = 0;
+ } else {
+ if (0.5 > $L) {
+ $S = $deltaMax / ($clrMax + $clrMin);
+ } else {
+ $S = $deltaMax / (510 - $clrMax - $clrMin);
+ }
+
+ if ($clrMax == $clrR) {
+ $H = ($clrG - $clrB) / (6.0 * $deltaMax);
+ } else if ($clrMax == $clrG) {
+ $H = 1/3 + ($clrB - $clrR) / (6.0 * $deltaMax);
+ } else {
+ $H = 2 / 3 + ($clrR - $clrG) / (6.0 * $deltaMax);
+ }
+
+ if (0 > $H) $H += 1;
+ if (1 < $H) $H -= 1;
+ }
+ return [$H, $S, $L];
+ }
+
+}
\ No newline at end of file
diff --git a/lib/classes/Config.class.php b/lib/classes/Config.class.php
deleted file mode 100644
index 642665d..0000000
--- a/lib/classes/Config.class.php
+++ /dev/null
@@ -1,469 +0,0 @@
-
- * @copyright 2010 Stud.IP Core-Group
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
-*/
-
-class Config implements ArrayAccess, Countable, IteratorAggregate
-{
- private static $instance = null;
-
- /**
- * contains all config entries as field => value pairs
- * @var array
- */
- protected $data = [];
- /**
- * contains additional metadata for config fields
- * @var array
- */
- protected $metadata = [];
-
- /**
- * returns singleton instance
- * @return Config
- */
- public static function get()
- {
- if (self::$instance === null) {
- $config = new Config();
- self::$instance = $config;
- }
- return self::$instance;
- }
-
- /**
- * alias of Config::get() for compatibility
- * @return Config
- */
- public static function getInstance()
- {
- return self::get();
- }
-
- /**
- * use to set singleton instance for testing
- * or to unset by passing null
- * @param Config $my_instance
- */
- public static function set()
- {
- $my_instance = func_get_arg(0);
- self::$instance = $my_instance;
- }
-
- /**
- * pass array of config entries in field => value pairs
- * to circumvent fetching from database
- * @param array $data
- */
- public function __construct($data = null)
- {
- $this->fetchData($data);
- }
-
- /**
- * returns a list of config entry names, filtered by
- * given params
- * @param string filter by range: global, range, user, course or institute
- * @param string filter by section
- * @param string filter by part of name
- * @return array
- */
- public function getFields($range = null, $section = null, $name = null)
- {
- if ($range && !in_array($range, words('global range user course institute'))) {
- throw new Exception('Invalid range type');
- }
-
- $temp = $this->metadata;
-
- if ($range) {
- $temp = array_filter($temp, function ($a) use ($range) {
- return $a['range'] === $range
- || ($a['range'] === 'range' && in_array($range, words('user course institute')));
- });
- }
- if ($section) {
- $temp = array_filter($temp, function ($a) use ($section) {
- return $a['section'] === $section;
- });
- }
- if ($name) {
- $temp = array_filter($temp, function ($a) use ($name) {
- return mb_stripos($a['field'], $name) !== false;
- });
- }
-
- return array_keys($temp);
- }
-
- /**
- * returns metadata for config entry
- * @param string $field
- * @return array
- */
- public function getMetadata($field)
- {
- return $this->metadata[$field] ?? [];
- }
-
- /**
- * returns value of config entry
- * for compatibility reasons an existing variable in global
- * namespace with the same name is also returned
- * @param string $field
- * @return mixed
- */
- public function getValue($field)
- {
- if ($this->fromEnv($field)) {
- return $_ENV["STUDIP_CONFIG_{$field}"];
- }
-
- if (array_key_exists($field, $this->data)) {
- return $this->data[$field];
- }
-
- if (isset($GLOBALS[$field]) && !isset($_REQUEST[$field])) {
- return $GLOBALS[$field];
- }
-
- return null;
- }
-
- /**
- * set config entry to given value, but don't store it
- * in database
- * @param string $field
- * @param mixed $value
- * @return
- */
- public function setValue($field, $value)
- {
- if (array_key_exists($field, $this->data)) {
- return $this->data[$field] = $value;
- }
- }
-
- /**
- * IteratorAggregate
- */
- public function getIterator(): Traversable
- {
- return new ArrayIterator($this->data);
- }
-
- /**
- * magic method for dynamic properties
- */
- public function __get($field)
- {
- return $this->getValue($field);
- }
-
- /**
- * magic method for dynamic properties
- */
- public function __set($field, $value)
- {
- return $this->setValue($field, $value);
- }
-
- /**
- * magic method for dynamic properties
- */
- public function __isset($field)
- {
- return isset($this->data[$field]);
- }
-
- /**
- * ArrayAccess: Check whether the given offset exists.
- */
- public function offsetExists($offset): bool
- {
- return isset($this->$offset);
- }
-
- /**
- * ArrayAccess: Get the value at the given offset.
- */
- public function offsetGet($offset): mixed
- {
- return $this->$offset;
- }
-
- /**
- * ArrayAccess: Set the value at the given offset.
- */
- public function offsetSet($offset, $value): void
- {
- $this->$offset = $value;
- }
-
- /**
- * ArrayAccess: unset the value at the given offset (not applicable)
- */
- public function offsetUnset($offset): void
- {
-
- }
-
- /**
- * Countable
- */
- public function count(): int
- {
- return count($this->data);
- }
-
- /**
- * fetch config data from table config
- * pass array to override database access
- * @param array $data
- */
- protected function fetchData($data = null)
- {
- if ($data !== null) {
- $this->data = $data;
- } else {
- $this->data = [];
- $db = DBManager::get();
-
- try {
- $query = "SELECT config.field, IFNULL(config_values.value, config.value) AS value, type, section, `range`, description,
- config_values.comment, config_values.value = config.value AS is_default
- FROM config
- LEFT JOIN config_values ON config.field = config_values.field AND range_id = 'studip'
- ORDER BY section, config.field";
- $rs = $db->query($query);
- } catch (Exception $e) {
- //if migration is smaller than 226 and Stud.IP needs to be migrated to version 4.1 or greater:
- $query = "SELECT field, value, type, section, `range`, description, comment, is_default
- FROM `config`
- ORDER BY is_default DESC, section, field";
- $rs = $db->query($query);
- }
-
- while ($row = $rs->fetch(PDO::FETCH_ASSOC)) {
- // set the the type of the default entry for the modified entry
- if (!empty($this->metadata[$row['field']])) {
- $row['type'] = $this->metadata[$row['field']]['type'];
- }
-
- $this->data[$row['field']] = $this->convertFromDatabase(
- $row['type'],
- $row['value'],
- $row['field']
- );
-
- $this->metadata[$row['field']] = array_intersect_key($row, array_flip(words('type section range description is_default comment')));
- $this->metadata[$row['field']]['field'] = $row['field'];
- $this->metadata[$row['field']]['type'] = $row['type'] ?: 'string';
- }
- }
- }
-
- /**
- * store new value for existing config entry in database
- * posts notification ConfigValueChanged if entry is changed
- * @param string $field
- * @param string $data
- * @throws InvalidArgumentException
- * @return boolean
- */
- public function store($field, $data)
- {
- if (!is_array($data) || !isset($data['value'])) {
- $values['value'] = $data;
- } else {
- $values = $data;
- }
-
- $values['value'] = $this->convertForDatabase(
- $this->metadata[$field]['type'],
- $values['value'],
- $field
- );
-
- $entry = ConfigEntry::find($field);
- if (!isset($entry)) {
- throw new InvalidArgumentException($field . " not found in config table");
- }
- $ret = 0;
- if (isset($values['value'])) {
- $value_entry = new ConfigValue([$field, 'studip']);
- $old_value = $value_entry->isNew() ? $entry->value : $value_entry->value;
- $value_entry->value = $values['value'];
- if (isset($values['comment'])) {
- $value_entry->comment = $values['comment'];
- }
- if ($entry->isDefault($value_entry)) {
- $ret += $value_entry->delete();
- } else {
- $ret += $value_entry->store();
- }
- }
-
- if (isset($values['section'])) {
- $entry->section = $values['section'];
- $ret += $entry->store();
- }
-
- if ($ret) {
- $this->fetchData();
- if (isset($value_entry)) {
- NotificationCenter::postNotification('ConfigValueDidChange', $this, [
- 'field' => $field,
- 'old_value' => $old_value,
- 'new_value' => $value_entry->value,
- ]);
- }
- }
- return $ret > 0;
- }
-
- /**
- * creates a new config entry in database
- * @param string name of entry
- * @param array data to insert as assoc array
- * @throws InvalidArgumentException
- * @return null|ConfigEntry
- */
- public function create($field, $data = [])
- {
- if (!$field) {
- throw new InvalidArgumentException("config fieldname is mandatory");
- }
- $entry = new ConfigEntry($field);
- if (!$entry->isNew()) {
- throw new InvalidArgumentException("config $field already exists");
- }
- $entry->setData($data);
- $ret = $entry->store() ? $entry : null;
- if ($ret) {
- $this->fetchData();
- }
- return $ret;
- }
-
- /**
- * delete config entry from database
- * @param string name of entry
- * @throws InvalidArgumentException
- * @return integer number of deleted rows
- */
- public function delete($field)
- {
- if (!$field) {
- throw new InvalidArgumentException("config fieldname is mandatory");
- }
- ConfigValue::deleteBySql('field=?', [$field]);
- $deleted = ConfigEntry::deleteBySql('field=?', [$field]);
- if ($deleted) {
- $this->fetchData();
- }
- return $deleted;
- }
-
- /**
- * Returns the identifier for the i18n field.
- * @param string $field
- * @return string
- */
- protected function getI18NIdentifier($field)
- {
- return md5($field);
- }
-
- /**
- * Transforms the data from the database for use.
- *
- * @param string $type
- * @param mixed $value
- * @param string $field
- * @return mixed
- */
- public function convertFromDatabase($type, $value, $field)
- {
- if ($type === 'integer') {
- return (int) $value;
- }
-
- if ($type === 'boolean') {
- return (bool) $value;
- }
-
- if ($type === 'array') {
- return (array) json_decode($value, true);
- }
-
- if ($type === 'i18n') {
- return new I18NString($value, null, [
- 'object_id' => $this->getI18NIdentifier($field),
- 'table' => 'config',
- 'field' => 'value',
- ]);
- }
-
- return (string) $value;
- }
-
- /**
- * Transforms the given value to be stored in the database.
- *
- * @param string $type
- * @param mixed $value
- * @param string $field
- * @return mixed
- */
- public function convertForDatabase($type, $value, $field)
- {
- if ($type === 'boolean') {
- return (bool) $value;
- }
-
- if ($type === 'integer') {
- return (int) $value;
- }
-
- if ($type === 'array') {
- return json_encode($value);
- }
-
- if ($type === 'i18n') {
- $value->setMetadata([
- 'object_id' => $this->getI18NIdentifier($field),
- 'table' => 'config',
- 'field' => 'value',
- ]);
- $value->storeTranslations();
-
- return $value->original();
- }
-
- return (string) $value;
- }
-
- /**
- * Returns whether the value was set from the environment.
- *
- * @param string $field
- * @return bool
- */
- public function fromEnv(string $field): bool
- {
- return isset($_ENV["STUDIP_CONFIG_{$field}"]);
- }
-}
diff --git a/lib/classes/Config.php b/lib/classes/Config.php
new file mode 100644
index 0000000..642665d
--- /dev/null
+++ b/lib/classes/Config.php
@@ -0,0 +1,469 @@
+
+ * @copyright 2010 Stud.IP Core-Group
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+*/
+
+class Config implements ArrayAccess, Countable, IteratorAggregate
+{
+ private static $instance = null;
+
+ /**
+ * contains all config entries as field => value pairs
+ * @var array
+ */
+ protected $data = [];
+ /**
+ * contains additional metadata for config fields
+ * @var array
+ */
+ protected $metadata = [];
+
+ /**
+ * returns singleton instance
+ * @return Config
+ */
+ public static function get()
+ {
+ if (self::$instance === null) {
+ $config = new Config();
+ self::$instance = $config;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * alias of Config::get() for compatibility
+ * @return Config
+ */
+ public static function getInstance()
+ {
+ return self::get();
+ }
+
+ /**
+ * use to set singleton instance for testing
+ * or to unset by passing null
+ * @param Config $my_instance
+ */
+ public static function set()
+ {
+ $my_instance = func_get_arg(0);
+ self::$instance = $my_instance;
+ }
+
+ /**
+ * pass array of config entries in field => value pairs
+ * to circumvent fetching from database
+ * @param array $data
+ */
+ public function __construct($data = null)
+ {
+ $this->fetchData($data);
+ }
+
+ /**
+ * returns a list of config entry names, filtered by
+ * given params
+ * @param string filter by range: global, range, user, course or institute
+ * @param string filter by section
+ * @param string filter by part of name
+ * @return array
+ */
+ public function getFields($range = null, $section = null, $name = null)
+ {
+ if ($range && !in_array($range, words('global range user course institute'))) {
+ throw new Exception('Invalid range type');
+ }
+
+ $temp = $this->metadata;
+
+ if ($range) {
+ $temp = array_filter($temp, function ($a) use ($range) {
+ return $a['range'] === $range
+ || ($a['range'] === 'range' && in_array($range, words('user course institute')));
+ });
+ }
+ if ($section) {
+ $temp = array_filter($temp, function ($a) use ($section) {
+ return $a['section'] === $section;
+ });
+ }
+ if ($name) {
+ $temp = array_filter($temp, function ($a) use ($name) {
+ return mb_stripos($a['field'], $name) !== false;
+ });
+ }
+
+ return array_keys($temp);
+ }
+
+ /**
+ * returns metadata for config entry
+ * @param string $field
+ * @return array
+ */
+ public function getMetadata($field)
+ {
+ return $this->metadata[$field] ?? [];
+ }
+
+ /**
+ * returns value of config entry
+ * for compatibility reasons an existing variable in global
+ * namespace with the same name is also returned
+ * @param string $field
+ * @return mixed
+ */
+ public function getValue($field)
+ {
+ if ($this->fromEnv($field)) {
+ return $_ENV["STUDIP_CONFIG_{$field}"];
+ }
+
+ if (array_key_exists($field, $this->data)) {
+ return $this->data[$field];
+ }
+
+ if (isset($GLOBALS[$field]) && !isset($_REQUEST[$field])) {
+ return $GLOBALS[$field];
+ }
+
+ return null;
+ }
+
+ /**
+ * set config entry to given value, but don't store it
+ * in database
+ * @param string $field
+ * @param mixed $value
+ * @return
+ */
+ public function setValue($field, $value)
+ {
+ if (array_key_exists($field, $this->data)) {
+ return $this->data[$field] = $value;
+ }
+ }
+
+ /**
+ * IteratorAggregate
+ */
+ public function getIterator(): Traversable
+ {
+ return new ArrayIterator($this->data);
+ }
+
+ /**
+ * magic method for dynamic properties
+ */
+ public function __get($field)
+ {
+ return $this->getValue($field);
+ }
+
+ /**
+ * magic method for dynamic properties
+ */
+ public function __set($field, $value)
+ {
+ return $this->setValue($field, $value);
+ }
+
+ /**
+ * magic method for dynamic properties
+ */
+ public function __isset($field)
+ {
+ return isset($this->data[$field]);
+ }
+
+ /**
+ * ArrayAccess: Check whether the given offset exists.
+ */
+ public function offsetExists($offset): bool
+ {
+ return isset($this->$offset);
+ }
+
+ /**
+ * ArrayAccess: Get the value at the given offset.
+ */
+ public function offsetGet($offset): mixed
+ {
+ return $this->$offset;
+ }
+
+ /**
+ * ArrayAccess: Set the value at the given offset.
+ */
+ public function offsetSet($offset, $value): void
+ {
+ $this->$offset = $value;
+ }
+
+ /**
+ * ArrayAccess: unset the value at the given offset (not applicable)
+ */
+ public function offsetUnset($offset): void
+ {
+
+ }
+
+ /**
+ * Countable
+ */
+ public function count(): int
+ {
+ return count($this->data);
+ }
+
+ /**
+ * fetch config data from table config
+ * pass array to override database access
+ * @param array $data
+ */
+ protected function fetchData($data = null)
+ {
+ if ($data !== null) {
+ $this->data = $data;
+ } else {
+ $this->data = [];
+ $db = DBManager::get();
+
+ try {
+ $query = "SELECT config.field, IFNULL(config_values.value, config.value) AS value, type, section, `range`, description,
+ config_values.comment, config_values.value = config.value AS is_default
+ FROM config
+ LEFT JOIN config_values ON config.field = config_values.field AND range_id = 'studip'
+ ORDER BY section, config.field";
+ $rs = $db->query($query);
+ } catch (Exception $e) {
+ //if migration is smaller than 226 and Stud.IP needs to be migrated to version 4.1 or greater:
+ $query = "SELECT field, value, type, section, `range`, description, comment, is_default
+ FROM `config`
+ ORDER BY is_default DESC, section, field";
+ $rs = $db->query($query);
+ }
+
+ while ($row = $rs->fetch(PDO::FETCH_ASSOC)) {
+ // set the the type of the default entry for the modified entry
+ if (!empty($this->metadata[$row['field']])) {
+ $row['type'] = $this->metadata[$row['field']]['type'];
+ }
+
+ $this->data[$row['field']] = $this->convertFromDatabase(
+ $row['type'],
+ $row['value'],
+ $row['field']
+ );
+
+ $this->metadata[$row['field']] = array_intersect_key($row, array_flip(words('type section range description is_default comment')));
+ $this->metadata[$row['field']]['field'] = $row['field'];
+ $this->metadata[$row['field']]['type'] = $row['type'] ?: 'string';
+ }
+ }
+ }
+
+ /**
+ * store new value for existing config entry in database
+ * posts notification ConfigValueChanged if entry is changed
+ * @param string $field
+ * @param string $data
+ * @throws InvalidArgumentException
+ * @return boolean
+ */
+ public function store($field, $data)
+ {
+ if (!is_array($data) || !isset($data['value'])) {
+ $values['value'] = $data;
+ } else {
+ $values = $data;
+ }
+
+ $values['value'] = $this->convertForDatabase(
+ $this->metadata[$field]['type'],
+ $values['value'],
+ $field
+ );
+
+ $entry = ConfigEntry::find($field);
+ if (!isset($entry)) {
+ throw new InvalidArgumentException($field . " not found in config table");
+ }
+ $ret = 0;
+ if (isset($values['value'])) {
+ $value_entry = new ConfigValue([$field, 'studip']);
+ $old_value = $value_entry->isNew() ? $entry->value : $value_entry->value;
+ $value_entry->value = $values['value'];
+ if (isset($values['comment'])) {
+ $value_entry->comment = $values['comment'];
+ }
+ if ($entry->isDefault($value_entry)) {
+ $ret += $value_entry->delete();
+ } else {
+ $ret += $value_entry->store();
+ }
+ }
+
+ if (isset($values['section'])) {
+ $entry->section = $values['section'];
+ $ret += $entry->store();
+ }
+
+ if ($ret) {
+ $this->fetchData();
+ if (isset($value_entry)) {
+ NotificationCenter::postNotification('ConfigValueDidChange', $this, [
+ 'field' => $field,
+ 'old_value' => $old_value,
+ 'new_value' => $value_entry->value,
+ ]);
+ }
+ }
+ return $ret > 0;
+ }
+
+ /**
+ * creates a new config entry in database
+ * @param string name of entry
+ * @param array data to insert as assoc array
+ * @throws InvalidArgumentException
+ * @return null|ConfigEntry
+ */
+ public function create($field, $data = [])
+ {
+ if (!$field) {
+ throw new InvalidArgumentException("config fieldname is mandatory");
+ }
+ $entry = new ConfigEntry($field);
+ if (!$entry->isNew()) {
+ throw new InvalidArgumentException("config $field already exists");
+ }
+ $entry->setData($data);
+ $ret = $entry->store() ? $entry : null;
+ if ($ret) {
+ $this->fetchData();
+ }
+ return $ret;
+ }
+
+ /**
+ * delete config entry from database
+ * @param string name of entry
+ * @throws InvalidArgumentException
+ * @return integer number of deleted rows
+ */
+ public function delete($field)
+ {
+ if (!$field) {
+ throw new InvalidArgumentException("config fieldname is mandatory");
+ }
+ ConfigValue::deleteBySql('field=?', [$field]);
+ $deleted = ConfigEntry::deleteBySql('field=?', [$field]);
+ if ($deleted) {
+ $this->fetchData();
+ }
+ return $deleted;
+ }
+
+ /**
+ * Returns the identifier for the i18n field.
+ * @param string $field
+ * @return string
+ */
+ protected function getI18NIdentifier($field)
+ {
+ return md5($field);
+ }
+
+ /**
+ * Transforms the data from the database for use.
+ *
+ * @param string $type
+ * @param mixed $value
+ * @param string $field
+ * @return mixed
+ */
+ public function convertFromDatabase($type, $value, $field)
+ {
+ if ($type === 'integer') {
+ return (int) $value;
+ }
+
+ if ($type === 'boolean') {
+ return (bool) $value;
+ }
+
+ if ($type === 'array') {
+ return (array) json_decode($value, true);
+ }
+
+ if ($type === 'i18n') {
+ return new I18NString($value, null, [
+ 'object_id' => $this->getI18NIdentifier($field),
+ 'table' => 'config',
+ 'field' => 'value',
+ ]);
+ }
+
+ return (string) $value;
+ }
+
+ /**
+ * Transforms the given value to be stored in the database.
+ *
+ * @param string $type
+ * @param mixed $value
+ * @param string $field
+ * @return mixed
+ */
+ public function convertForDatabase($type, $value, $field)
+ {
+ if ($type === 'boolean') {
+ return (bool) $value;
+ }
+
+ if ($type === 'integer') {
+ return (int) $value;
+ }
+
+ if ($type === 'array') {
+ return json_encode($value);
+ }
+
+ if ($type === 'i18n') {
+ $value->setMetadata([
+ 'object_id' => $this->getI18NIdentifier($field),
+ 'table' => 'config',
+ 'field' => 'value',
+ ]);
+ $value->storeTranslations();
+
+ return $value->original();
+ }
+
+ return (string) $value;
+ }
+
+ /**
+ * Returns whether the value was set from the environment.
+ *
+ * @param string $field
+ * @return bool
+ */
+ public function fromEnv(string $field): bool
+ {
+ return isset($_ENV["STUDIP_CONFIG_{$field}"]);
+ }
+}
diff --git a/lib/classes/CourseAvatar.class.php b/lib/classes/CourseAvatar.class.php
deleted file mode 100644
index 8c153a8..0000000
--- a/lib/classes/CourseAvatar.class.php
+++ /dev/null
@@ -1,44 +0,0 @@
-user_id}";
- }
-
- /**
- * Return the default title of the avatar.
- * @return string the default title
- */
- public function getDefaultTitle()
- {
- return Seminar::GetInstance($this->user_id)->name;
- }
-
- /**
- * Return if avatar is visible to the current user.
- * @return boolean: true if visible
- */
- protected function checkAvatarVisibility()
- {
- //no special conditions for visibility of course-avatars yet
- return true;
- }
-}
diff --git a/lib/classes/CourseAvatar.php b/lib/classes/CourseAvatar.php
new file mode 100644
index 0000000..8c153a8
--- /dev/null
+++ b/lib/classes/CourseAvatar.php
@@ -0,0 +1,44 @@
+user_id}";
+ }
+
+ /**
+ * Return the default title of the avatar.
+ * @return string the default title
+ */
+ public function getDefaultTitle()
+ {
+ return Seminar::GetInstance($this->user_id)->name;
+ }
+
+ /**
+ * Return if avatar is visible to the current user.
+ * @return boolean: true if visible
+ */
+ protected function checkAvatarVisibility()
+ {
+ //no special conditions for visibility of course-avatars yet
+ return true;
+ }
+}
diff --git a/lib/classes/CourseConfig.class.php b/lib/classes/CourseConfig.class.php
deleted file mode 100644
index ad62be0..0000000
--- a/lib/classes/CourseConfig.class.php
+++ /dev/null
@@ -1,23 +0,0 @@
-
- * @copyright 2010 Stud.IP Core-Group
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
-*/
-
-class CourseConfig extends RangeConfig
-{
- /**
- * range type
- */
- const RANGE_TYPE = 'course';
-}
diff --git a/lib/classes/CourseConfig.php b/lib/classes/CourseConfig.php
new file mode 100644
index 0000000..ad62be0
--- /dev/null
+++ b/lib/classes/CourseConfig.php
@@ -0,0 +1,23 @@
+
+ * @copyright 2010 Stud.IP Core-Group
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+*/
+
+class CourseConfig extends RangeConfig
+{
+ /**
+ * range type
+ */
+ const RANGE_TYPE = 'course';
+}
diff --git a/lib/classes/CronJob.class.php b/lib/classes/CronJob.class.php
deleted file mode 100644
index 1e7fb41..0000000
--- a/lib/classes/CronJob.class.php
+++ /dev/null
@@ -1,140 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @since 2.4
- */
-
-// +---------------------------------------------------------------------------+
-// This file is part of Stud.IP
-// Cronjob.class.php
-//
-// Copyright (C) 2013 Jan-Hendrik Willms
-// +---------------------------------------------------------------------------+
-// 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 any later version.
-// +---------------------------------------------------------------------------+
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// +---------------------------------------------------------------------------+
-
-abstract class CronJob
-{
- /**
- * Return the name of the cronjob.
- */
- abstract public static function getName();
-
- /**
- * Return the description of the cronjob.
- */
- abstract public static function getDescription();
-
- /**
- * Execute the cronjob.
- *
- * @param mixed $last_result What the last execution of this cronjob
- * returned.
- * @param Array $parameters Parameters for this cronjob instance which
- * were defined during scheduling.
- */
- abstract public function execute($last_result, $parameters = []);
-
- /**
- * Returns a list of available parameters for this cronjob.
- *
- * Each parameter is an entry in the resulting with a unique identifier
- * with the following array fields:
- *
- * - "type" which is one of the following:
- * - boolean, a simple binary option
- * - string, a single line of text
- * - text, a multiline chunk of text
- * - integer, a number
- * - select, a defined set of values (define in the field "values" as
- * an array)
- * - "default" provides a default value for this field (optional)
- * - "status" is either "optional" or "mandatory" (optional, defaults to
- * optional)
- * - "description" provides a decription for this parameter
- *
- * Example:
- *
- *
- * return array(
- * 'area' => array(
- * 'type' => 'select',
- * 'values' => array('seminar', 'institute', 'user'),
- * 'description' => 'Example parameter #1',
- * ),
- * 'verbose' => array(
- * 'type' => 'boolean',
- * 'default' => false,
- * 'status' => 'optional',
- * 'description' => 'Example parameter #2',
- * ),
- * );
- *
- *
- * @param Array List of paramters in the format described above.
- */
- public static function getParameters()
- {
- return [];
- }
-
- /**
- * Setup method.
- */
- public function setUp()
- {
- }
-
- /**
- * Teardown method.
- */
- public function tearDown()
- {
- }
-
-// Convenience methods to ease the usage
-
- /**
- * Registers the cronjob and/or returns the corresponding task.
- *
- * @return CronjobTask Task for this cronjob
- */
- public static function register()
- {
- $class_name = get_called_class();
- $reflection = new ReflectionClass($class_name);
-
- $task_id = CronjobScheduler::getInstance()->registerTask($reflection->newInstance());
-
- return CronjobTask::find($task_id);
- }
-
- /**
- * Unregisters a previously registered task.
- */
- public static function unregister()
- {
- $class_name = get_called_class();
- $task = CronjobTask::findOneByClass($class_name);
-
- if ($task !== null) {
- CronjobScheduler::getInstance()->unregisterTask($task->id);
- }
- }
-
-}
diff --git a/lib/classes/CronJob.php b/lib/classes/CronJob.php
new file mode 100644
index 0000000..1e7fb41
--- /dev/null
+++ b/lib/classes/CronJob.php
@@ -0,0 +1,140 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 2.4
+ */
+
+// +---------------------------------------------------------------------------+
+// This file is part of Stud.IP
+// Cronjob.class.php
+//
+// Copyright (C) 2013 Jan-Hendrik Willms
+// +---------------------------------------------------------------------------+
+// 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 any later version.
+// +---------------------------------------------------------------------------+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// +---------------------------------------------------------------------------+
+
+abstract class CronJob
+{
+ /**
+ * Return the name of the cronjob.
+ */
+ abstract public static function getName();
+
+ /**
+ * Return the description of the cronjob.
+ */
+ abstract public static function getDescription();
+
+ /**
+ * Execute the cronjob.
+ *
+ * @param mixed $last_result What the last execution of this cronjob
+ * returned.
+ * @param Array $parameters Parameters for this cronjob instance which
+ * were defined during scheduling.
+ */
+ abstract public function execute($last_result, $parameters = []);
+
+ /**
+ * Returns a list of available parameters for this cronjob.
+ *
+ * Each parameter is an entry in the resulting with a unique identifier
+ * with the following array fields:
+ *
+ * - "type" which is one of the following:
+ * - boolean, a simple binary option
+ * - string, a single line of text
+ * - text, a multiline chunk of text
+ * - integer, a number
+ * - select, a defined set of values (define in the field "values" as
+ * an array)
+ * - "default" provides a default value for this field (optional)
+ * - "status" is either "optional" or "mandatory" (optional, defaults to
+ * optional)
+ * - "description" provides a decription for this parameter
+ *
+ * Example:
+ *
+ *
+ * return array(
+ * 'area' => array(
+ * 'type' => 'select',
+ * 'values' => array('seminar', 'institute', 'user'),
+ * 'description' => 'Example parameter #1',
+ * ),
+ * 'verbose' => array(
+ * 'type' => 'boolean',
+ * 'default' => false,
+ * 'status' => 'optional',
+ * 'description' => 'Example parameter #2',
+ * ),
+ * );
+ *
+ *
+ * @param Array List of paramters in the format described above.
+ */
+ public static function getParameters()
+ {
+ return [];
+ }
+
+ /**
+ * Setup method.
+ */
+ public function setUp()
+ {
+ }
+
+ /**
+ * Teardown method.
+ */
+ public function tearDown()
+ {
+ }
+
+// Convenience methods to ease the usage
+
+ /**
+ * Registers the cronjob and/or returns the corresponding task.
+ *
+ * @return CronjobTask Task for this cronjob
+ */
+ public static function register()
+ {
+ $class_name = get_called_class();
+ $reflection = new ReflectionClass($class_name);
+
+ $task_id = CronjobScheduler::getInstance()->registerTask($reflection->newInstance());
+
+ return CronjobTask::find($task_id);
+ }
+
+ /**
+ * Unregisters a previously registered task.
+ */
+ public static function unregister()
+ {
+ $class_name = get_called_class();
+ $task = CronjobTask::findOneByClass($class_name);
+
+ if ($task !== null) {
+ CronjobScheduler::getInstance()->unregisterTask($task->id);
+ }
+ }
+
+}
diff --git a/lib/classes/CronjobScheduler.class.php b/lib/classes/CronjobScheduler.class.php
deleted file mode 100644
index cc4cf8b..0000000
--- a/lib/classes/CronjobScheduler.class.php
+++ /dev/null
@@ -1,333 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @since 2.4
- */
-
-// +---------------------------------------------------------------------------+
-// This file is part of Stud.IP
-// CronjobScheduler.class.php
-//
-// Copyright (C) 2013 Jan-Hendrik Willms
-// +---------------------------------------------------------------------------+
-// 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 any later version.
-// +---------------------------------------------------------------------------+
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// +---------------------------------------------------------------------------+
-
-class CronjobScheduler
-{
- protected static $instance = null;
-
- /**
- * Returns the scheduler object. Implements the singleton pattern to
- * ensure that only one scheduler exists.
- *
- * @return CronjobScheduler The scheduler object
- */
- public static function getInstance()
- {
- if (self::$instance === null) {
- self::$instance = new self();
- }
- return self::$instance;
- }
-
- /**
- * Private constructor to ensure the singleton pattern is used correctly.
- */
- private function __construct()
- {
- }
-
- /**
- * Registers a new executable task.
- *
- * @param mixed $task Either path of the task class filename (relative
- * to Stud.IP root) or an instance of CronJob
- * @param bool $active Indicates whether the task should be set active
- * or not
- * @return String Id of the created task
- * @throws InvalidArgumentException when the task class file does not
- * exist
- * @throws RuntimeException when task has already been registered
- */
- public function registerTask($task, $active = true)
- {
- if (is_object($task)) {
- $reflection = new ReflectionClass($task);
- $class = $reflection->getName();
- $class_filename = studip_relative_path($reflection->getFileName());
- } else {
- $filename = $GLOBALS['STUDIP_BASE_PATH'] . '/' . $task;
- if (!file_exists($filename)) {
- $message = sprintf('Task class file "%s" does not exist.', $task);
- throw new InvalidArgumentException($message);
- }
- $class_filename = $task;
-
- $classes = get_declared_classes();
- require_once $filename;
- $new_classes = array_diff(get_declared_classes(), $classes);
- $new_classes = array_filter($new_classes, function ($class) {
- return is_subclass_of($class, 'CronJob', true);
- });
- $class = end($new_classes);
-
- if (empty($class)) {
- throw new RuntimeException('No valid class was defined in file.');
- }
-
- $reflection = new ReflectionClass($class);
- }
-
- if (!$reflection->isSubclassOf('CronJob')) {
- $message = sprintf('Job class "%s" (defined in %s) does not extend the abstract CronJob class.', $class, $filename);
- throw new RuntimeException($message);
- }
-
- if ($task = CronjobTask::findOneByClass($class)) {
- return $task->task_id;
- }
-
- $task = new CronjobTask();
- $task->filename = $class_filename;
- $task->class = $class;
- $task->active = (int)$active;
- $task->store();
-
- return $task->task_id;
- }
-
- /**
- * Unregisters a previously registered task.
- *
- * @param String $task_id Id of the task to be unregistered
- * @return CronjobScheduler to allow chaining
- * @throws InvalidArgumentException when no task with the given id exists
- */
- public function unregisterTask($task_id)
- {
- $task = CronjobTask::find($task_id);
- if ($task === null) {
- $message = sprintf('A task with the id "%s" does not exist.', $task_id);
- throw new InvalidArgumentException($message);
- }
- $task->delete();
-
- return $this;
- }
-
- /**
- * Schedules a task for periodic execution with the provided schedule.
- *
- * @param String $task_id The id of the task to be executed
- * @param mixed $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:
- * - 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:
- * - 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:
- * - 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:
- * - 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 Array $parameters Optional parameters passed to the task
- * @return CronjobSchedule The generated schedule object.
- */
- public function schedule(
- string $task_id,
- ?int $minute = null,
- ?int $hour = null,
- ?int $day = null,
- ?int $month = null,
- ?int $day_of_week = null,
- array $parameters = []
- ): CronjobSchedule {
- $schedule = new CronjobSchedule();
- $schedule->task_id = $task_id;
- $schedule->parameters = $parameters;
-
- $schedule->minute = $minute;
- $schedule->hour = $hour;
- $schedule->day = $day;
- $schedule->month = $month;
- $schedule->day_of_week = $day_of_week;
-
- $schedule->store();
-
- $task = $schedule->task;
- $task->assigned_count += 1;
- $task->store();
-
- return $schedule;
- }
-
- /**
- * An alias for schedule for backwards compatibility.
- *
- * @see CronjobScheduler::schedule()
- */
- public function schedulePeriodic(
- $task_id,
- $minute = null,
- $hour = null,
- $day = null,
- $month = null,
- $day_of_week = null,
- $priority = null,
- $parameters = []
- ) {
- return $this->schedule($task_id, $minute, $hour, $day, $month, $day_of_week, $parameters);
- }
-
- /**
- * Cancels the provided schedule.
- *
- * @param String $schedule_id Id of the schedule to be canceled
- */
- public function cancel($schedule_id)
- {
- CronjobSchedule::find($schedule_id)->delete();
- }
-
- /**
- * Cancels all schedules of the provided task.
- *
- * @param String $task_id Id of the task which schedules shall be canceled
- */
- public function cancelByTask($task_id)
- {
- $schedules = CronjobSchedule::findByTask_id($task_id);
- foreach ($schedules as $schedule) {
- $schedule->delete();
- }
- }
-
- /**
- * Executes the available schedules if they are to be executed.
- * This method can only be run once - even if one execution takes more
- * than planned. This is ensured by a locking mechanism.
- */
- public function run()
- {
- if (!Config::get()->CRONJOBS_ENABLE) {
- return;
- }
-
- $lock = new FileLock('studip-cronjob');
-
- // Check whether a previous cronjob worker is still running.
- if (!$lock->tryLock()) {
- return;
- }
-
- // Find all schedules that are due to execute and which task is active
- $temp = CronjobSchedule::findBySQL('`active` = 1 AND `next_execution` <= UNIX_TIMESTAMP() '
- .'ORDER BY `next_execution`');
- $schedules = array_filter($temp, function ($schedule) { return $schedule->task->active; });
-
- if (count($schedules) === 0) {
- return;
- }
-
- foreach ($schedules as $schedule) {
- $log = new CronjobLog();
- $log->schedule_id = $schedule->schedule_id;
- $log->scheduled = $schedule->next_execution;
- $log->executed = time();
- $log->exception = null;
- $log->duration = -1;
-
- try {
- // Skip schedules with missing task classes
- if (!$schedule->task->valid) {
- throw new Exception(_('Die Klasse für den Cronjob-Task konnte nicht gefunden werden'));
- }
-
- // Start capturing output and measuring duration
- ob_start();
- $start_time = microtime(true);
-
- $schedule->execute();
-
- // Actually capture output and duration
- $end_time = microtime(true);
- $output = ob_get_clean();
-
- // Complete log
- $log->output = $output;
- $log->duration = $end_time - $start_time;
- $log->store();
- } catch (Exception $e) {
- $log->exception = $e;
- $log->store();
-
- // Deactivate schedule
- $schedule->deactivate();
-
- // Send mail to root accounts
- $subject = sprintf('[Cronjobs] %s: %s',
- _('Fehlerhafte Ausführung'),
- $schedule->title);
-
- $message = sprintf(_('Der Cronjob "%s" wurde deaktiviert, da bei der Ausführung ein Fehler aufgetreten ist.'), $schedule->title) . "\n";
- $message .= "\n";
- $message .= display_exception($e) . "\n";
-
- $message .= _('Für weiterführende Informationen klicken Sie bitten den folgenden Link:') . "\n";
-
- $old = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']);
- $message .= URLHelper::getURL('dispatch.php/admin/cronjobs/logs/schedule/' . $schedule->schedule_id);
- URLHelper::setBaseURL($old);
-
- $this->sendMailToRoots($subject, $message);
- }
- }
-
- // Release lock
- $lock->release();
- }
-
- /**
- * Sends an internal mail with the provided subject and message to all
- * users with a global permission of "root".
- *
- * @param String $subject The subject of the message
- * @param String $message The message itself
- */
- private function sendMailToRoots($subject, $message)
- {
- $temp = User::findByPerms('root');
- $roots = SimpleORMapCollection::createFromArray($temp)
- ->filter(function($r) { return $r->locked == 0; })
- ->pluck('username');
-
- $msging = new messaging;
- $msging->insert_message($message, $roots, '____%system%____', null, null, null, null, $subject, false, 'high');
- }
-}
diff --git a/lib/classes/CronjobScheduler.php b/lib/classes/CronjobScheduler.php
new file mode 100644
index 0000000..cc4cf8b
--- /dev/null
+++ b/lib/classes/CronjobScheduler.php
@@ -0,0 +1,333 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 2.4
+ */
+
+// +---------------------------------------------------------------------------+
+// This file is part of Stud.IP
+// CronjobScheduler.class.php
+//
+// Copyright (C) 2013 Jan-Hendrik Willms
+// +---------------------------------------------------------------------------+
+// 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 any later version.
+// +---------------------------------------------------------------------------+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// +---------------------------------------------------------------------------+
+
+class CronjobScheduler
+{
+ protected static $instance = null;
+
+ /**
+ * Returns the scheduler object. Implements the singleton pattern to
+ * ensure that only one scheduler exists.
+ *
+ * @return CronjobScheduler The scheduler object
+ */
+ public static function getInstance()
+ {
+ if (self::$instance === null) {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Private constructor to ensure the singleton pattern is used correctly.
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Registers a new executable task.
+ *
+ * @param mixed $task Either path of the task class filename (relative
+ * to Stud.IP root) or an instance of CronJob
+ * @param bool $active Indicates whether the task should be set active
+ * or not
+ * @return String Id of the created task
+ * @throws InvalidArgumentException when the task class file does not
+ * exist
+ * @throws RuntimeException when task has already been registered
+ */
+ public function registerTask($task, $active = true)
+ {
+ if (is_object($task)) {
+ $reflection = new ReflectionClass($task);
+ $class = $reflection->getName();
+ $class_filename = studip_relative_path($reflection->getFileName());
+ } else {
+ $filename = $GLOBALS['STUDIP_BASE_PATH'] . '/' . $task;
+ if (!file_exists($filename)) {
+ $message = sprintf('Task class file "%s" does not exist.', $task);
+ throw new InvalidArgumentException($message);
+ }
+ $class_filename = $task;
+
+ $classes = get_declared_classes();
+ require_once $filename;
+ $new_classes = array_diff(get_declared_classes(), $classes);
+ $new_classes = array_filter($new_classes, function ($class) {
+ return is_subclass_of($class, 'CronJob', true);
+ });
+ $class = end($new_classes);
+
+ if (empty($class)) {
+ throw new RuntimeException('No valid class was defined in file.');
+ }
+
+ $reflection = new ReflectionClass($class);
+ }
+
+ if (!$reflection->isSubclassOf('CronJob')) {
+ $message = sprintf('Job class "%s" (defined in %s) does not extend the abstract CronJob class.', $class, $filename);
+ throw new RuntimeException($message);
+ }
+
+ if ($task = CronjobTask::findOneByClass($class)) {
+ return $task->task_id;
+ }
+
+ $task = new CronjobTask();
+ $task->filename = $class_filename;
+ $task->class = $class;
+ $task->active = (int)$active;
+ $task->store();
+
+ return $task->task_id;
+ }
+
+ /**
+ * Unregisters a previously registered task.
+ *
+ * @param String $task_id Id of the task to be unregistered
+ * @return CronjobScheduler to allow chaining
+ * @throws InvalidArgumentException when no task with the given id exists
+ */
+ public function unregisterTask($task_id)
+ {
+ $task = CronjobTask::find($task_id);
+ if ($task === null) {
+ $message = sprintf('A task with the id "%s" does not exist.', $task_id);
+ throw new InvalidArgumentException($message);
+ }
+ $task->delete();
+
+ return $this;
+ }
+
+ /**
+ * Schedules a task for periodic execution with the provided schedule.
+ *
+ * @param String $task_id The id of the task to be executed
+ * @param mixed $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:
+ * - 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:
+ * - 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:
+ * - 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:
+ * - 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 Array $parameters Optional parameters passed to the task
+ * @return CronjobSchedule The generated schedule object.
+ */
+ public function schedule(
+ string $task_id,
+ ?int $minute = null,
+ ?int $hour = null,
+ ?int $day = null,
+ ?int $month = null,
+ ?int $day_of_week = null,
+ array $parameters = []
+ ): CronjobSchedule {
+ $schedule = new CronjobSchedule();
+ $schedule->task_id = $task_id;
+ $schedule->parameters = $parameters;
+
+ $schedule->minute = $minute;
+ $schedule->hour = $hour;
+ $schedule->day = $day;
+ $schedule->month = $month;
+ $schedule->day_of_week = $day_of_week;
+
+ $schedule->store();
+
+ $task = $schedule->task;
+ $task->assigned_count += 1;
+ $task->store();
+
+ return $schedule;
+ }
+
+ /**
+ * An alias for schedule for backwards compatibility.
+ *
+ * @see CronjobScheduler::schedule()
+ */
+ public function schedulePeriodic(
+ $task_id,
+ $minute = null,
+ $hour = null,
+ $day = null,
+ $month = null,
+ $day_of_week = null,
+ $priority = null,
+ $parameters = []
+ ) {
+ return $this->schedule($task_id, $minute, $hour, $day, $month, $day_of_week, $parameters);
+ }
+
+ /**
+ * Cancels the provided schedule.
+ *
+ * @param String $schedule_id Id of the schedule to be canceled
+ */
+ public function cancel($schedule_id)
+ {
+ CronjobSchedule::find($schedule_id)->delete();
+ }
+
+ /**
+ * Cancels all schedules of the provided task.
+ *
+ * @param String $task_id Id of the task which schedules shall be canceled
+ */
+ public function cancelByTask($task_id)
+ {
+ $schedules = CronjobSchedule::findByTask_id($task_id);
+ foreach ($schedules as $schedule) {
+ $schedule->delete();
+ }
+ }
+
+ /**
+ * Executes the available schedules if they are to be executed.
+ * This method can only be run once - even if one execution takes more
+ * than planned. This is ensured by a locking mechanism.
+ */
+ public function run()
+ {
+ if (!Config::get()->CRONJOBS_ENABLE) {
+ return;
+ }
+
+ $lock = new FileLock('studip-cronjob');
+
+ // Check whether a previous cronjob worker is still running.
+ if (!$lock->tryLock()) {
+ return;
+ }
+
+ // Find all schedules that are due to execute and which task is active
+ $temp = CronjobSchedule::findBySQL('`active` = 1 AND `next_execution` <= UNIX_TIMESTAMP() '
+ .'ORDER BY `next_execution`');
+ $schedules = array_filter($temp, function ($schedule) { return $schedule->task->active; });
+
+ if (count($schedules) === 0) {
+ return;
+ }
+
+ foreach ($schedules as $schedule) {
+ $log = new CronjobLog();
+ $log->schedule_id = $schedule->schedule_id;
+ $log->scheduled = $schedule->next_execution;
+ $log->executed = time();
+ $log->exception = null;
+ $log->duration = -1;
+
+ try {
+ // Skip schedules with missing task classes
+ if (!$schedule->task->valid) {
+ throw new Exception(_('Die Klasse für den Cronjob-Task konnte nicht gefunden werden'));
+ }
+
+ // Start capturing output and measuring duration
+ ob_start();
+ $start_time = microtime(true);
+
+ $schedule->execute();
+
+ // Actually capture output and duration
+ $end_time = microtime(true);
+ $output = ob_get_clean();
+
+ // Complete log
+ $log->output = $output;
+ $log->duration = $end_time - $start_time;
+ $log->store();
+ } catch (Exception $e) {
+ $log->exception = $e;
+ $log->store();
+
+ // Deactivate schedule
+ $schedule->deactivate();
+
+ // Send mail to root accounts
+ $subject = sprintf('[Cronjobs] %s: %s',
+ _('Fehlerhafte Ausführung'),
+ $schedule->title);
+
+ $message = sprintf(_('Der Cronjob "%s" wurde deaktiviert, da bei der Ausführung ein Fehler aufgetreten ist.'), $schedule->title) . "\n";
+ $message .= "\n";
+ $message .= display_exception($e) . "\n";
+
+ $message .= _('Für weiterführende Informationen klicken Sie bitten den folgenden Link:') . "\n";
+
+ $old = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']);
+ $message .= URLHelper::getURL('dispatch.php/admin/cronjobs/logs/schedule/' . $schedule->schedule_id);
+ URLHelper::setBaseURL($old);
+
+ $this->sendMailToRoots($subject, $message);
+ }
+ }
+
+ // Release lock
+ $lock->release();
+ }
+
+ /**
+ * Sends an internal mail with the provided subject and message to all
+ * users with a global permission of "root".
+ *
+ * @param String $subject The subject of the message
+ * @param String $message The message itself
+ */
+ private function sendMailToRoots($subject, $message)
+ {
+ $temp = User::findByPerms('root');
+ $roots = SimpleORMapCollection::createFromArray($temp)
+ ->filter(function($r) { return $r->locked == 0; })
+ ->pluck('username');
+
+ $msging = new messaging;
+ $msging->insert_message($message, $roots, '____%system%____', null, null, null, null, $subject, false, 'high');
+ }
+}
diff --git a/lib/classes/DBManager.class.php b/lib/classes/DBManager.class.php
deleted file mode 100644
index 7854cff..0000000
--- a/lib/classes/DBManager.class.php
+++ /dev/null
@@ -1,250 +0,0 @@
-
- *
- * 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.
- */
-
-/**
- * This class provides a singleton instance that is used to manage PDO database
- * connections.
- *
- * Example of use:
- * @code
- * # get hold of the DBManager's singleton
- * $manager = DBManager::getInstance();
- *
- * # set PDO connections using a DSN
- * $manager->setConnection('example',
- * 'mysql:host=localhost;dbname=example',
- * 'root', '');
- * # or an existing instance of PDO
- * $manager->setConnection('example2', $existingPdo);
- *
- * # retrieve a PDO connection later in your code
- * $db = $manager->getConnection("studip");
- *
- * # or as a shortcut
- * $db = DBManager::get("studip");
- *
- * # or even shorter ("studip" is the default key)
- * $db = DBManager::get();
- *
- * # and use the connection
- * $db->query('SELECT * FROM user_info');
- *
- * # you may even alias connections
- * $manager->aliasConnection("studip", "studip-slave");
- *
- * # but this is just sugar for
- * $studip = $manager->getConnection("studip");
- * $manager->setConnection("studip-slave", $studip);
- *
- * @endcode
- */
-class DBManager
-{
-
-
- /**
- * the singleton instance
- *
- * @access private
- * @var DBManager
- */
- static private $instance;
-
-
- /**
- * an array of connections of the singleton instance
- *
- * @access private
- * @var array
- */
- private $connections;
-
-
- /**
- * @access private
- *
- * @return void
- */
- private function __construct()
- {
- $this->connections = [];
- }
-
-
- /**
- * This method returns the singleton instance of this class.
- *
- * @return DBManager the singleton instance
- */
- static public function getInstance()
- {
- if (is_null(DBManager::$instance)) {
- DBManager::$instance = new DBManager();
- }
- return DBManager::$instance;
- }
-
-
- /**
- * This method returns the database connection to the given key. Throws a
- * DBManagerException if there is no such connection.
- *
- * Example usage:
- * @code
- * try {
- * $db = DBManager::getInstance()->getConnection("foo");
- * $db->exec($sql);
- * } catch (DBManagerException $e) {
- * echo "oops";
- * }
- * @endcode
- *
- * @param string the key
- *
- * @throw DBManagerException
- *
- * @return PDO the database connection
- */
- public function getConnection($database)
- {
-
- if (!isset($this->connections[$database])) {
- throw new DBManagerException('Database connection: "'.$database.
- '" does not exist.');
- }
-
- return $this->connections[$database];
- }
-
-
- /**
- * This method maps the specified key to the specified database connection.
- *
- * You can either use an instance of class PDO or specify a DSN (optionally
- * with username/password).
- *
- * The (possibly newly created) connection is configured to throw exceptions
- * and to buffer queries if it is a MySQL connection.
- *
- * Examples:
- * @code
- * $dbManager = DBManager::getInstance();
- *
- * // using an existing PDO connection
- * $existingPdo = new LoggingPDO($dsn);
- * $dbManager->setConnection('studip', $pdo);
- *
- * // using a DSN with username and password
- * $dbManager->setConnection('studip', $dsn , $username, $password);
- * @endcode
- *
- * @param string the key
- * @param string|PDO either a DSN or an existing PDO connection
- * @param string (optional) the connection's username
- * @param array (optional) the connection's password
- *
- * @return DBManager this instance, useful for cascading method calls
- */
- public function setConnection($database, $dsnOrConnection, $user = NULL,
- $pass = NULL)
- {
- $connection = $dsnOrConnection instanceof PDO
- ? $dsnOrConnection
- : new StudipPDO($dsnOrConnection, $user, $pass);
-
- $this->configureConnection($connection);
- $this->connections[$database] = $connection;
-
- return $this;
- }
-
-
- // PDO connection should throw exceptions and use buffered queries
- private function configureConnection($connection)
- {
-
- $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
- if ($connection->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
- $connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
- // $connection->exec('SET CHARACTER SET latin1');
- }
- }
-
- /**
- * This method creates an alias for a database connection.
- *
- * This is useful if you want to use different keys but access the same
- * database, e.g. if you want to use master-slave replication in the future
- *
- * @param string the old key of the database connection
- * @param string the new key of the database connection
- *
- * @return DBManager this instance, useful for cascading method calls
- */
- public function aliasConnection($old, $new)
- {
-
- if (!isset($this->connections[$old])) {
- throw new DBManagerException('No database found using key: ' . $old);
- }
-
- $this->connections[$new] = $this->connections[$old];
-
- return $this;
- }
-
-
- /**
- * Shortcut static method to retrieve the database connection for a given
- * key.
- *
- * Example usage:
- * @code
- * // instead of
- * $db = DBManager::getInstance()->getConnection("studip");
- *
- * // this can be shortened to
- * $db = DBManager::get("studip");
- *
- * // or in this case (as "studip" is the default key)
- * $db = DBManager::get();
- * @endcode
- *
- * @param string the key
- *
- * @return StudipPDO the database connection
- */
- static public function get($database = 'studip')
- {
- $manager = DBManager::getInstance();
- return $manager->getConnection($database);
- }
-}
-
-
-/**
- * The DBManager throws this exception if it cannot find a connection using
- * a non existant key.
- */
-class DBManagerException extends Exception
-{
-
- /**
- * @param string the message of this exception
- *
- * @return void
- */
- public function __construct($message)
- {
- parent::__construct($message);
- }
-}
diff --git a/lib/classes/DBManager.php b/lib/classes/DBManager.php
new file mode 100644
index 0000000..7854cff
--- /dev/null
+++ b/lib/classes/DBManager.php
@@ -0,0 +1,250 @@
+
+ *
+ * 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.
+ */
+
+/**
+ * This class provides a singleton instance that is used to manage PDO database
+ * connections.
+ *
+ * Example of use:
+ * @code
+ * # get hold of the DBManager's singleton
+ * $manager = DBManager::getInstance();
+ *
+ * # set PDO connections using a DSN
+ * $manager->setConnection('example',
+ * 'mysql:host=localhost;dbname=example',
+ * 'root', '');
+ * # or an existing instance of PDO
+ * $manager->setConnection('example2', $existingPdo);
+ *
+ * # retrieve a PDO connection later in your code
+ * $db = $manager->getConnection("studip");
+ *
+ * # or as a shortcut
+ * $db = DBManager::get("studip");
+ *
+ * # or even shorter ("studip" is the default key)
+ * $db = DBManager::get();
+ *
+ * # and use the connection
+ * $db->query('SELECT * FROM user_info');
+ *
+ * # you may even alias connections
+ * $manager->aliasConnection("studip", "studip-slave");
+ *
+ * # but this is just sugar for
+ * $studip = $manager->getConnection("studip");
+ * $manager->setConnection("studip-slave", $studip);
+ *
+ * @endcode
+ */
+class DBManager
+{
+
+
+ /**
+ * the singleton instance
+ *
+ * @access private
+ * @var DBManager
+ */
+ static private $instance;
+
+
+ /**
+ * an array of connections of the singleton instance
+ *
+ * @access private
+ * @var array
+ */
+ private $connections;
+
+
+ /**
+ * @access private
+ *
+ * @return void
+ */
+ private function __construct()
+ {
+ $this->connections = [];
+ }
+
+
+ /**
+ * This method returns the singleton instance of this class.
+ *
+ * @return DBManager the singleton instance
+ */
+ static public function getInstance()
+ {
+ if (is_null(DBManager::$instance)) {
+ DBManager::$instance = new DBManager();
+ }
+ return DBManager::$instance;
+ }
+
+
+ /**
+ * This method returns the database connection to the given key. Throws a
+ * DBManagerException if there is no such connection.
+ *
+ * Example usage:
+ * @code
+ * try {
+ * $db = DBManager::getInstance()->getConnection("foo");
+ * $db->exec($sql);
+ * } catch (DBManagerException $e) {
+ * echo "oops";
+ * }
+ * @endcode
+ *
+ * @param string the key
+ *
+ * @throw DBManagerException
+ *
+ * @return PDO the database connection
+ */
+ public function getConnection($database)
+ {
+
+ if (!isset($this->connections[$database])) {
+ throw new DBManagerException('Database connection: "'.$database.
+ '" does not exist.');
+ }
+
+ return $this->connections[$database];
+ }
+
+
+ /**
+ * This method maps the specified key to the specified database connection.
+ *
+ * You can either use an instance of class PDO or specify a DSN (optionally
+ * with username/password).
+ *
+ * The (possibly newly created) connection is configured to throw exceptions
+ * and to buffer queries if it is a MySQL connection.
+ *
+ * Examples:
+ * @code
+ * $dbManager = DBManager::getInstance();
+ *
+ * // using an existing PDO connection
+ * $existingPdo = new LoggingPDO($dsn);
+ * $dbManager->setConnection('studip', $pdo);
+ *
+ * // using a DSN with username and password
+ * $dbManager->setConnection('studip', $dsn , $username, $password);
+ * @endcode
+ *
+ * @param string the key
+ * @param string|PDO either a DSN or an existing PDO connection
+ * @param string (optional) the connection's username
+ * @param array (optional) the connection's password
+ *
+ * @return DBManager this instance, useful for cascading method calls
+ */
+ public function setConnection($database, $dsnOrConnection, $user = NULL,
+ $pass = NULL)
+ {
+ $connection = $dsnOrConnection instanceof PDO
+ ? $dsnOrConnection
+ : new StudipPDO($dsnOrConnection, $user, $pass);
+
+ $this->configureConnection($connection);
+ $this->connections[$database] = $connection;
+
+ return $this;
+ }
+
+
+ // PDO connection should throw exceptions and use buffered queries
+ private function configureConnection($connection)
+ {
+
+ $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ if ($connection->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
+ $connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
+ // $connection->exec('SET CHARACTER SET latin1');
+ }
+ }
+
+ /**
+ * This method creates an alias for a database connection.
+ *
+ * This is useful if you want to use different keys but access the same
+ * database, e.g. if you want to use master-slave replication in the future
+ *
+ * @param string the old key of the database connection
+ * @param string the new key of the database connection
+ *
+ * @return DBManager this instance, useful for cascading method calls
+ */
+ public function aliasConnection($old, $new)
+ {
+
+ if (!isset($this->connections[$old])) {
+ throw new DBManagerException('No database found using key: ' . $old);
+ }
+
+ $this->connections[$new] = $this->connections[$old];
+
+ return $this;
+ }
+
+
+ /**
+ * Shortcut static method to retrieve the database connection for a given
+ * key.
+ *
+ * Example usage:
+ * @code
+ * // instead of
+ * $db = DBManager::getInstance()->getConnection("studip");
+ *
+ * // this can be shortened to
+ * $db = DBManager::get("studip");
+ *
+ * // or in this case (as "studip" is the default key)
+ * $db = DBManager::get();
+ * @endcode
+ *
+ * @param string the key
+ *
+ * @return StudipPDO the database connection
+ */
+ static public function get($database = 'studip')
+ {
+ $manager = DBManager::getInstance();
+ return $manager->getConnection($database);
+ }
+}
+
+
+/**
+ * The DBManager throws this exception if it cannot find a connection using
+ * a non existant key.
+ */
+class DBManagerException extends Exception
+{
+
+ /**
+ * @param string the message of this exception
+ *
+ * @return void
+ */
+ public function __construct($message)
+ {
+ parent::__construct($message);
+ }
+}
diff --git a/lib/classes/DataFieldBoolEntry.class.php b/lib/classes/DataFieldBoolEntry.class.php
deleted file mode 100644
index da294b6..0000000
--- a/lib/classes/DataFieldBoolEntry.class.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldBoolEntry extends DataFieldEntry
-{
- protected $template = 'bool.php';
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- return $this->getValue() ? _('Ja') : _('Nein');
- }
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($submitted_value)
- {
- $this->setValue((int) $submitted_value);
- }
-}
diff --git a/lib/classes/DataFieldBoolEntry.php b/lib/classes/DataFieldBoolEntry.php
new file mode 100644
index 0000000..da294b6
--- /dev/null
+++ b/lib/classes/DataFieldBoolEntry.php
@@ -0,0 +1,35 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldBoolEntry extends DataFieldEntry
+{
+ protected $template = 'bool.php';
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ return $this->getValue() ? _('Ja') : _('Nein');
+ }
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($submitted_value)
+ {
+ $this->setValue((int) $submitted_value);
+ }
+}
diff --git a/lib/classes/DataFieldComboEntry.class.php b/lib/classes/DataFieldComboEntry.class.php
deleted file mode 100644
index 6047050..0000000
--- a/lib/classes/DataFieldComboEntry.class.php
+++ /dev/null
@@ -1,79 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldComboEntry extends DataFieldEntry
-{
- protected $template = 'combo.php';
-
- /**
- * Constructs this datafield
- *
- * @param DataField $datafield Underlying model
- * @param String $rangeID Range id
- * @param mixed $value Value
- */
- public function __construct(DataField $struct, $range_id, $value)
- {
- parent::__construct($struct, $range_id, $value);
-
- if ($this->getValue() === null) {
- $parameters = $this->getParameters();
- $this->setValue($parameters[0]); // first selectbox entry is default
- }
- }
-
- /**
- * Returns the number of html fields this datafield uses for input.
- *
- * @return int representing the number of html fields
- */
- public function numberOfHTMLFields()
- {
- return 2;
- }
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($value)
- {
- $index = $value['combo'];
- $value = $value[$index];
- parent::setValueFromSubmit($value);
- }
-
- /**
- * Returns the according input elements as html for this datafield
- *
- * @param String $name Name prefix of the associated input
- * @param Array $variables Additional variables
- * @return String containing the required html
- */
- public function getHTML($name = '', $variables = [])
- {
- return parent::getHTML($name, $variables + [
- 'values' => $this->getParameters(),
- ]);
- }
-
- /**
- * Returns the individual type parameters.
- *
- * @return array containing the individual type parameters
- */
- protected function getParameters()
- {
- $parameters = explode("\n", rtrim($this->model->typeparam));
- $parameters = array_map('trim', $parameters);
- return $parameters;
- }
-}
diff --git a/lib/classes/DataFieldComboEntry.php b/lib/classes/DataFieldComboEntry.php
new file mode 100644
index 0000000..6047050
--- /dev/null
+++ b/lib/classes/DataFieldComboEntry.php
@@ -0,0 +1,79 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldComboEntry extends DataFieldEntry
+{
+ protected $template = 'combo.php';
+
+ /**
+ * Constructs this datafield
+ *
+ * @param DataField $datafield Underlying model
+ * @param String $rangeID Range id
+ * @param mixed $value Value
+ */
+ public function __construct(DataField $struct, $range_id, $value)
+ {
+ parent::__construct($struct, $range_id, $value);
+
+ if ($this->getValue() === null) {
+ $parameters = $this->getParameters();
+ $this->setValue($parameters[0]); // first selectbox entry is default
+ }
+ }
+
+ /**
+ * Returns the number of html fields this datafield uses for input.
+ *
+ * @return int representing the number of html fields
+ */
+ public function numberOfHTMLFields()
+ {
+ return 2;
+ }
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($value)
+ {
+ $index = $value['combo'];
+ $value = $value[$index];
+ parent::setValueFromSubmit($value);
+ }
+
+ /**
+ * Returns the according input elements as html for this datafield
+ *
+ * @param String $name Name prefix of the associated input
+ * @param Array $variables Additional variables
+ * @return String containing the required html
+ */
+ public function getHTML($name = '', $variables = [])
+ {
+ return parent::getHTML($name, $variables + [
+ 'values' => $this->getParameters(),
+ ]);
+ }
+
+ /**
+ * Returns the individual type parameters.
+ *
+ * @return array containing the individual type parameters
+ */
+ protected function getParameters()
+ {
+ $parameters = explode("\n", rtrim($this->model->typeparam));
+ $parameters = array_map('trim', $parameters);
+ return $parameters;
+ }
+}
diff --git a/lib/classes/DataFieldDateEntry.class.php b/lib/classes/DataFieldDateEntry.class.php
deleted file mode 100644
index bf7d518..0000000
--- a/lib/classes/DataFieldDateEntry.class.php
+++ /dev/null
@@ -1,80 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldDateEntry extends DataFieldEntry
-{
- protected $template = 'date.php';
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($value)
- {
- if ($value) {
- $value = trim($value);
- $items = explode(".", $value);
- $value = array_reverse($items);
- $value = array_filter($value);
- $date = implode('-', $value);
- parent::setValueFromSubmit($date);
- }
- }
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entries = true)
- {
- if ($this->isValid()) {
- $value = trim($this->value);
- $value = explode('-', $value);
- $value = array_reverse($value);
- $value = implode('.', $value);
- return $value;
- }
-
- return '';
- }
-
- /**
- * Returns the according input elements as html for this datafield
- *
- * @param String $name Name prefix of the associated input
- * @param Array $variables Additional variables
- * @return String containing the required html
- */
- public function getHTML($name = '', $variables = [])
- {
- return parent::getHTML($name, $variables + [
- 'timestamp' => strtotime(trim($this->value)),
- ]);
- }
-
- /**
- * Returns whether the datafield contents are valid
- *
- * @return boolean indicating whether the datafield contents are valid
- */
- public function isValid()
- {
- $value = trim($this->value);
-
- if (!$value) {
- return parent::isValid();
- }
-
- return parent::isValid() && strtotime($value) !== false;
- }
-}
diff --git a/lib/classes/DataFieldDateEntry.php b/lib/classes/DataFieldDateEntry.php
new file mode 100644
index 0000000..bf7d518
--- /dev/null
+++ b/lib/classes/DataFieldDateEntry.php
@@ -0,0 +1,80 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldDateEntry extends DataFieldEntry
+{
+ protected $template = 'date.php';
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($value)
+ {
+ if ($value) {
+ $value = trim($value);
+ $items = explode(".", $value);
+ $value = array_reverse($items);
+ $value = array_filter($value);
+ $date = implode('-', $value);
+ parent::setValueFromSubmit($date);
+ }
+ }
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entries = true)
+ {
+ if ($this->isValid()) {
+ $value = trim($this->value);
+ $value = explode('-', $value);
+ $value = array_reverse($value);
+ $value = implode('.', $value);
+ return $value;
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns the according input elements as html for this datafield
+ *
+ * @param String $name Name prefix of the associated input
+ * @param Array $variables Additional variables
+ * @return String containing the required html
+ */
+ public function getHTML($name = '', $variables = [])
+ {
+ return parent::getHTML($name, $variables + [
+ 'timestamp' => strtotime(trim($this->value)),
+ ]);
+ }
+
+ /**
+ * Returns whether the datafield contents are valid
+ *
+ * @return boolean indicating whether the datafield contents are valid
+ */
+ public function isValid()
+ {
+ $value = trim($this->value);
+
+ if (!$value) {
+ return parent::isValid();
+ }
+
+ return parent::isValid() && strtotime($value) !== false;
+ }
+}
diff --git a/lib/classes/DataFieldEmailEntry.class.php b/lib/classes/DataFieldEmailEntry.class.php
deleted file mode 100644
index aafe7bb..0000000
--- a/lib/classes/DataFieldEmailEntry.class.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldEmailEntry extends DataFieldEntry
-{
- protected $template = 'email.php';
-
- /**
- * Returns whether the datafield contents are valid
- *
- * @return boolean indicating whether the datafield contents are valid
- */
- public function isValid()
- {
- return parent::isValid()
- && (!$this->getValue() || filter_var($this->getValue(), FILTER_VALIDATE_EMAIL));
- }
-}
diff --git a/lib/classes/DataFieldEmailEntry.php b/lib/classes/DataFieldEmailEntry.php
new file mode 100644
index 0000000..aafe7bb
--- /dev/null
+++ b/lib/classes/DataFieldEmailEntry.php
@@ -0,0 +1,25 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldEmailEntry extends DataFieldEntry
+{
+ protected $template = 'email.php';
+
+ /**
+ * Returns whether the datafield contents are valid
+ *
+ * @return boolean indicating whether the datafield contents are valid
+ */
+ public function isValid()
+ {
+ return parent::isValid()
+ && (!$this->getValue() || filter_var($this->getValue(), FILTER_VALIDATE_EMAIL));
+ }
+}
diff --git a/lib/classes/DataFieldEntry.class.php b/lib/classes/DataFieldEntry.class.php
deleted file mode 100644
index fd50a0b..0000000
--- a/lib/classes/DataFieldEntry.class.php
+++ /dev/null
@@ -1,588 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-abstract class DataFieldEntry
-{
- protected static $supported_types = [
- 'bool',
- 'textline',
- 'textlinei18n',
- 'textarea',
- 'textareai18n',
- 'textmarkup',
- 'textmarkupi18n',
- 'selectbox',
- 'selectboxmultiple',
- 'date',
- 'time',
- 'email',
- 'phone',
- 'radio',
- 'combo',
- 'link',
- ];
-
- protected $language = '';
- protected $template = null;
-
- /**
- * Returns all supported datafield types.
- *
- * @param string Object type of the datafield.
- * @return array of supported types
- */
- public static function getSupportedTypes($object_type = null)
- {
- // no i18n fields for descriptors
- if (in_array($object_type, ['moduldeskriptor', 'modulteildeskriptor'])) {
- return array_diff(
- self::$supported_types,
- [
- 'textlinei18n',
- 'textareai18n',
- 'textmarkupi18n'
- ]);
- }
- return self::$supported_types;
- }
-
- /**
- * Returns the according class for a given type.
- *
- * @param String $type Type of the datafield
- * @return String class name
- */
- private static function getClassForType($type)
- {
- if ($type === 'selectboxmultiple') {
- return 'DataFieldSelectboxMultipleEntry';
- }
- return 'DataField' . ucfirst($type) . 'Entry';
- }
-
- /**
- * Factory method that returns the appropriate datafield object
- * for the given parameters.
- *
- * @param DataField $datafield Underlying structure
- * @param String $rangeID Range id
- * @param mixed $value Value of the entry
- * @return DataFieldEntry instance of appropriate type
- */
- public static function createDataFieldEntry(DataField $datafield, $rangeID = '', $value = null)
- {
- $type = $datafield->type;
- if (!in_array($type, self::getSupportedTypes())) {
- return false;
- }
-
- $entry_class = self::getClassForType($type);
- $entry = new $entry_class($datafield, $rangeID, $value);
-
- return $entry;
- }
-
- /**
- * Enter description here...
- *
- * @param string $range_id
- * @param string $object_type
- * @param string $object_class_hint
- * @return array
- */
- public static function getDataFieldEntries($range_id, $object_type = '', $object_class_hint = '')
- {
- if (!$range_id) {
- return []; // we necessarily need a range ID
- }
- $parameters = [];
- $clause1 = '';
- $clause2 = '';
- $clause3 = '';
- if(is_array($range_id)) {
- // rangeID may be an array ("classic" rangeID and second rangeID used for user roles)
- $secRangeID = $range_id[1];
- $rangeID = $range_id[0]; // to keep compatible with following code
- if('usersemdata' !== $object_type && 'roleinstdata' !== $object_type) {
- $object_type = 'userinstrole';
- }
- $clause1 = "AND sec_range_id= :sec_range_id";
- $parameters[':sec_range_id'] = $secRangeID;
- } else {
- $rangeID = $range_id;
- }
- if (!$object_type) {
- $object_type = get_object_type($rangeID);
- }
-
- if($object_type) {
- switch ($object_type) {
- case 'sem':
- if ($object_class_hint) {
- $object_class = SeminarCategories::GetByTypeId($object_class_hint);
- } else {
- $object_class = SeminarCategories::GetBySeminarId($rangeID);
- }
-
- $clause2 = "object_class = :object_class OR object_class IS NULL";
- $parameters[':object_class'] = (int) $object_class->id;
- $clause3 = 'a.institut_id IS NULL OR a.institut_id IN (:institut_ids)';
- $query = "SELECT institut_id FROM seminar_inst WHERE seminar_id = :seminar_id";
- $statement = DBManager::get()->prepare($query);
- $statement->execute([':seminar_id' => $rangeID]);
- $parameters[':institut_ids'] = array_keys($statement->fetchGrouped());
- break;
- case 'inst':
- case 'fak':
-
- if ($object_class_hint) {
- $object_class = $object_class_hint;
- } else {
- $query = "SELECT type FROM Institute WHERE Institut_id = ?";
- $statement = DBManager::get()->prepare($query);
- $statement->execute([$rangeID]);
- $object_class = $statement->fetchColumn();
- }
- $object_type = "inst";
- $clause2 = "object_class = :object_class OR object_class IS NULL";
- $parameters[':object_class'] = (int) $object_class;
- $clause3 = 'a.institut_id IS NULL OR a.institut_id = :institut_id';
- $parameters[':institut_id'] = $rangeID;
- break;
- case 'roleinstdata': //hmm tja, vermutlich so
- case 'moduldeskriptor':
- case 'modulteildeskriptor':
- case 'studycourse':
- $clause2 = '1';
- $clause3 = '1';
- if (is_array($range_id) && isset($range_id[0])) {
- $clause3 = 'a.institut_id IS NULL OR a.institut_id = :institut_id';
- $parameters[':institut_id'] = $range_id[0];
- }
- break;
- case 'user':
- case 'userinstrole':
- case 'usersemdata':
- $object_class = is_object($GLOBALS['perm']) ? DataField::permMask($GLOBALS['perm']->get_perm($rangeID)) : 0;
- $clause2 = "((object_class & :object_class) OR object_class IS NULL)";
- $parameters[':object_class'] = (int) $object_class;
-
- $clause3 = 'a.institut_id IS NULL OR a.institut_id IN (:institut_ids)';
- $query = "SELECT institut_id FROM user_inst WHERE user_id = :user_id";
- $statement = DBManager::get()->prepare($query);
- $statement->execute([':user_id' => $rangeID]);
- $parameters[':institut_ids'] = array_keys($statement->fetchGrouped());
- break;
- }
- $query = "SELECT a.*, content
- FROM datafields AS a
- LEFT JOIN datafields_entries AS b
- ON (a.datafield_id = b.datafield_id AND range_id = :range_id {$clause1})
- WHERE object_type = :object_type AND ({$clause2}) AND ($clause3)
- ORDER BY priority";
- $parameters[':range_id'] = $rangeID;
- $parameters[':object_type'] = $object_type;
-
- $rs = DBManager::get()->prepare($query);
- $rs->execute($parameters);
-
- $entries = [];
- while ($data = $rs->fetch(PDO::FETCH_ASSOC)) {
- $datafield = DataField::buildExisting($data);
- $entries[$data['datafield_id']] = DataFieldEntry::createDataFieldEntry($datafield, $range_id, $data['content']);
- }
- }
- return $entries ?: [];
- }
-
- /**
- * Removes all datafields from a given range_id (and secondary range
- * id if passed as array)
- *
- * @param mixed $range_id Range id (or array with range id and secondary
- * range id)
- * @return int representing the number of deleted entries
- */
- public static function removeAll($range_id)
- {
- if (is_array($range_id)) {
- list ($rangeID, $secRangeID) = $range_id;
- } else {
- $rangeID = $range_id;
- $secRangeID = "";
- }
-
- if (!$rangeID && !$secRangeID) {
- return;
- }
-
- $conditions = [];
- $parameters = [];
-
- if ($rangeID) {
- $conditions[] = 'range_id = ?';
- $parameters[] = $rangeID;
- }
- if ($secRangeID) {
- $conditions[] = 'sec_range_id = ?';
- $parameters[] = $secRangeID;
- }
-
- $where = implode(' AND ', $conditions);
-
- return DatafieldEntryModel::deleteBySQL($where, $parameters);
- }
-
- public $value;
- public $model;
- public $rangeID;
-
- /**
- * Constructs this datafield
- *
- * @param DataField $datafield Underlying model
- * @param mixed $range_id Range id (or array with range id and secondary
- * range id)
- * @param mixed $value Value
- */
- public function __construct(DataField $datafield = null, $rangeID = '', $value = null)
- {
- $this->model = $datafield;
- $this->rangeID = $rangeID;
- $this->value = isset($value) ? $value : $datafield->default_value;
- }
-
- /**
- * Stores this datafield entry
- *
- * @return int representing the number of changed entries
- */
- public function store()
- {
- $id = [
- $this->model->id,
- (string) $this->getRangeID(),
- (string) $this->getSecondRangeID(),
- (string) $this->language
- ];
- $entry = new DatafieldEntryModel($id);
- $entry->lang = (string) $this->language;
-
- $old_value = $entry->content;
- $entry->content = $this->getValue();
-
- if ($entry->content == $this->model->default_value) {
- $result = $entry->isNew() ? 0 : $entry->delete();
- } else {
- $result = $entry->store();
- }
-
- if ($result) {
- NotificationCenter::postNotification('DatafieldDidUpdate', $this, [
- 'changed' => $result,
- 'old_value' => $old_value,
- ]);
- }
-
- return $result;
- }
-
- /**
- * Returns whether this datafield is required
- *
- * @return bool indicating whether the datafield is required or not
- */
- public function isRequired()
- {
- return $this->model->is_required;
- }
-
- /**
- * Returns the description of this datafield
- *
- * @return String containing the description
- */
- public function getDescription()
- {
- return $this->model->description;
- }
-
- /**
- * Returns the type of this datafield
- *
- * @return string type of entry
- */
- public function getType()
- {
- $class = mb_strtolower(get_class($this));
- return mb_substr($class, 9, mb_strpos($class, 'entry') - 9);
- }
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- if ($entities) {
- return htmlReady($this->getValue());
- }
- return $this->getValue();
- }
-
- /**
- * Returns the value of the datafield
- *
- * @return mixed containing the value
- */
- public function getValue()
- {
- return $this->value;
- }
-
- /**
- * Returns the name of the datafield
- *
- * @return String containing the name
- */
- public function getName()
- {
- return $this->model->name;
- }
-
- /**
- * Returns the id of the datafield
- *
- * @return String containing the id
- */
- public function getId()
- {
- return $this->model->id;
- }
-
- /**
- * Returns the according input elements as html for this datafield
- *
- * @param String $name Name prefix of the associated input
- * @param Array $variables Additional variables
- * @return String containing the required html
- */
- public function getHTML($name = '', $variables = [])
- {
- $variables = array_merge([
- 'name' => $name,
- 'entry' => $this,
- 'model' => $this->model,
- 'value' => $this->getValue(),
- ], $variables);
-
- return $GLOBALS['template_factory']->render('datafields/' . $this->template, $variables);
- }
-
- /**
- * Sets the value
- *
- * @param mixed $value The value
- */
- public function setValue($value)
- {
- $this->value = $value;
- }
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($submitted_value)
- {
- $this->setValue($submitted_value);
- }
-
- /**
- * Sets the range id
- *
- * @param String $range_id Range id
- */
- public function setRangeID($range_id)
- {
- $this->rangeID = $range_id;
- }
-
- /**
- * Sets the secondary range id
- *
- * @param String $sec_range_id Secondary range id
- */
- public function setSecondRangeID($sec_range_id)
- {
- $this->rangeID = [$this->getRangeID(), $sec_range_id];
- }
-
- /**
- * Sets the prefered content language if this is an i18n datafield.
- *
- * @param string $language The prefered display language
- */
- public function setContentLanguage($language)
- {
- if (!Config::get()->CONTENT_LANGUAGES[$language]) {
- throw new InvalidArgumentException('Language not configured.');
- }
-
- $languages = array_keys(Config::get()->CONTENT_LANGUAGES);
- if ($language == reset($languages)) {
- $language = '';
- }
-
- $this->language = $language;
- }
-
- /**
- * Checks if datafield is empty (was not set)
- *
- * @return bool true if empty, else false
- */
- public function isEmpty()
- {
- return $this->getValue() == '';
- }
-
- /**
- * Returns whether the datafield contents are valid
- *
- * @return boolean indicating whether the datafield contents are valid
- */
- public function isValid()
- {
- return trim($this->getValue())
- || !$this->model->is_required;
- }
-
- /**
- * Returns the number of html fields this datafield uses for input.
- *
- * @return int representing the number of html fields
- */
- public function numberOfHTMLFields()
- {
- return 1;
- }
-
- /**
- * Returns the range id
- *
- * @return String containing the range id
- */
- public function getRangeID()
- {
- if (is_array($this->rangeID)) {
- return reset($this->rangeID);
- }
- return $this->rangeID;
- }
-
- /**
- * Returns the secondary range id
- *
- * @return String containing the secondary range id
- */
- public function getSecondRangeID()
- {
- if (is_array($this->rangeID)) {
- list (, $secRangeID) = $this->rangeID;
- return $secRangeID;
- }
- return '';
- }
-
- /**
- * Returns whether the datafield is visible for the current user
- *
- * @param String $perm Permissions to test against (optional, default to
- * the current user's permissions)
- * @param bool $test_ownership Defines whether the ownership of the field
- * should be taken into account; a field may
- * be invisible for a user according to the
- * permissions but since the datafield belongs
- * to the user, it is visible.
- * @return boolean indicating whether the datafield is visible
- */
- public function isVisible($perm = null, $test_ownership = true)
- {
- if ($test_ownership) {
- return $this->model->accessAllowed($perm,
- $GLOBALS['user']->id,
- $this->getRangeID());
- }
- return $this->model->accessAllowed($perm);
- }
-
- /**
- * Returns whether the datafield is editable for the current user
- *
- * @param mixed $perms Perms to test against (optional, defaults to logged
- * in user's perms)
- * @return boolean indicating whether the datafield is editable
- */
- public function isEditable($perms = null)
- {
- return $this->model->editAllowed($perms ?: $GLOBALS['perm']->get_perm());
- }
-
- /**
- * Returns a human readable string describing the view permissions
- *
- * @return String containing the descriptons of the view permissions
- */
- public function getPermsDescription()
- {
- if ($this->model->view_perms === 'all') {
- return _('sichtbar für alle');
- }
- return sprintf(_('sichtbar nur für Sie und alle %s'),
- $this->prettyPrintViewPerms());
- }
-
- /**
- * Generates a full status description depending on the the perms
- *
- * @return string
- */
- protected function prettyPrintViewPerms()
- {
- switch ($this->model->view_perms) {
- case 'all':
- return _('alle');
- break;
- case 'root':
- return _('Systemadministrator/-innen');
- break;
- case 'admin':
- return _('Administrator/-innen');
- break;
- case 'dozent':
- return _('Lehrenden');
- break;
- case 'tutor':
- return _('Tutor/-innen');
- break;
- case 'autor':
- return _('Studierenden');
- break;
- case 'user':
- return _('Nutzer/-innen');
- break;
- }
- return '';
- }
-
-}
diff --git a/lib/classes/DataFieldEntry.php b/lib/classes/DataFieldEntry.php
new file mode 100644
index 0000000..fd50a0b
--- /dev/null
+++ b/lib/classes/DataFieldEntry.php
@@ -0,0 +1,588 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+abstract class DataFieldEntry
+{
+ protected static $supported_types = [
+ 'bool',
+ 'textline',
+ 'textlinei18n',
+ 'textarea',
+ 'textareai18n',
+ 'textmarkup',
+ 'textmarkupi18n',
+ 'selectbox',
+ 'selectboxmultiple',
+ 'date',
+ 'time',
+ 'email',
+ 'phone',
+ 'radio',
+ 'combo',
+ 'link',
+ ];
+
+ protected $language = '';
+ protected $template = null;
+
+ /**
+ * Returns all supported datafield types.
+ *
+ * @param string Object type of the datafield.
+ * @return array of supported types
+ */
+ public static function getSupportedTypes($object_type = null)
+ {
+ // no i18n fields for descriptors
+ if (in_array($object_type, ['moduldeskriptor', 'modulteildeskriptor'])) {
+ return array_diff(
+ self::$supported_types,
+ [
+ 'textlinei18n',
+ 'textareai18n',
+ 'textmarkupi18n'
+ ]);
+ }
+ return self::$supported_types;
+ }
+
+ /**
+ * Returns the according class for a given type.
+ *
+ * @param String $type Type of the datafield
+ * @return String class name
+ */
+ private static function getClassForType($type)
+ {
+ if ($type === 'selectboxmultiple') {
+ return 'DataFieldSelectboxMultipleEntry';
+ }
+ return 'DataField' . ucfirst($type) . 'Entry';
+ }
+
+ /**
+ * Factory method that returns the appropriate datafield object
+ * for the given parameters.
+ *
+ * @param DataField $datafield Underlying structure
+ * @param String $rangeID Range id
+ * @param mixed $value Value of the entry
+ * @return DataFieldEntry instance of appropriate type
+ */
+ public static function createDataFieldEntry(DataField $datafield, $rangeID = '', $value = null)
+ {
+ $type = $datafield->type;
+ if (!in_array($type, self::getSupportedTypes())) {
+ return false;
+ }
+
+ $entry_class = self::getClassForType($type);
+ $entry = new $entry_class($datafield, $rangeID, $value);
+
+ return $entry;
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param string $range_id
+ * @param string $object_type
+ * @param string $object_class_hint
+ * @return array
+ */
+ public static function getDataFieldEntries($range_id, $object_type = '', $object_class_hint = '')
+ {
+ if (!$range_id) {
+ return []; // we necessarily need a range ID
+ }
+ $parameters = [];
+ $clause1 = '';
+ $clause2 = '';
+ $clause3 = '';
+ if(is_array($range_id)) {
+ // rangeID may be an array ("classic" rangeID and second rangeID used for user roles)
+ $secRangeID = $range_id[1];
+ $rangeID = $range_id[0]; // to keep compatible with following code
+ if('usersemdata' !== $object_type && 'roleinstdata' !== $object_type) {
+ $object_type = 'userinstrole';
+ }
+ $clause1 = "AND sec_range_id= :sec_range_id";
+ $parameters[':sec_range_id'] = $secRangeID;
+ } else {
+ $rangeID = $range_id;
+ }
+ if (!$object_type) {
+ $object_type = get_object_type($rangeID);
+ }
+
+ if($object_type) {
+ switch ($object_type) {
+ case 'sem':
+ if ($object_class_hint) {
+ $object_class = SeminarCategories::GetByTypeId($object_class_hint);
+ } else {
+ $object_class = SeminarCategories::GetBySeminarId($rangeID);
+ }
+
+ $clause2 = "object_class = :object_class OR object_class IS NULL";
+ $parameters[':object_class'] = (int) $object_class->id;
+ $clause3 = 'a.institut_id IS NULL OR a.institut_id IN (:institut_ids)';
+ $query = "SELECT institut_id FROM seminar_inst WHERE seminar_id = :seminar_id";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([':seminar_id' => $rangeID]);
+ $parameters[':institut_ids'] = array_keys($statement->fetchGrouped());
+ break;
+ case 'inst':
+ case 'fak':
+
+ if ($object_class_hint) {
+ $object_class = $object_class_hint;
+ } else {
+ $query = "SELECT type FROM Institute WHERE Institut_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$rangeID]);
+ $object_class = $statement->fetchColumn();
+ }
+ $object_type = "inst";
+ $clause2 = "object_class = :object_class OR object_class IS NULL";
+ $parameters[':object_class'] = (int) $object_class;
+ $clause3 = 'a.institut_id IS NULL OR a.institut_id = :institut_id';
+ $parameters[':institut_id'] = $rangeID;
+ break;
+ case 'roleinstdata': //hmm tja, vermutlich so
+ case 'moduldeskriptor':
+ case 'modulteildeskriptor':
+ case 'studycourse':
+ $clause2 = '1';
+ $clause3 = '1';
+ if (is_array($range_id) && isset($range_id[0])) {
+ $clause3 = 'a.institut_id IS NULL OR a.institut_id = :institut_id';
+ $parameters[':institut_id'] = $range_id[0];
+ }
+ break;
+ case 'user':
+ case 'userinstrole':
+ case 'usersemdata':
+ $object_class = is_object($GLOBALS['perm']) ? DataField::permMask($GLOBALS['perm']->get_perm($rangeID)) : 0;
+ $clause2 = "((object_class & :object_class) OR object_class IS NULL)";
+ $parameters[':object_class'] = (int) $object_class;
+
+ $clause3 = 'a.institut_id IS NULL OR a.institut_id IN (:institut_ids)';
+ $query = "SELECT institut_id FROM user_inst WHERE user_id = :user_id";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([':user_id' => $rangeID]);
+ $parameters[':institut_ids'] = array_keys($statement->fetchGrouped());
+ break;
+ }
+ $query = "SELECT a.*, content
+ FROM datafields AS a
+ LEFT JOIN datafields_entries AS b
+ ON (a.datafield_id = b.datafield_id AND range_id = :range_id {$clause1})
+ WHERE object_type = :object_type AND ({$clause2}) AND ($clause3)
+ ORDER BY priority";
+ $parameters[':range_id'] = $rangeID;
+ $parameters[':object_type'] = $object_type;
+
+ $rs = DBManager::get()->prepare($query);
+ $rs->execute($parameters);
+
+ $entries = [];
+ while ($data = $rs->fetch(PDO::FETCH_ASSOC)) {
+ $datafield = DataField::buildExisting($data);
+ $entries[$data['datafield_id']] = DataFieldEntry::createDataFieldEntry($datafield, $range_id, $data['content']);
+ }
+ }
+ return $entries ?: [];
+ }
+
+ /**
+ * Removes all datafields from a given range_id (and secondary range
+ * id if passed as array)
+ *
+ * @param mixed $range_id Range id (or array with range id and secondary
+ * range id)
+ * @return int representing the number of deleted entries
+ */
+ public static function removeAll($range_id)
+ {
+ if (is_array($range_id)) {
+ list ($rangeID, $secRangeID) = $range_id;
+ } else {
+ $rangeID = $range_id;
+ $secRangeID = "";
+ }
+
+ if (!$rangeID && !$secRangeID) {
+ return;
+ }
+
+ $conditions = [];
+ $parameters = [];
+
+ if ($rangeID) {
+ $conditions[] = 'range_id = ?';
+ $parameters[] = $rangeID;
+ }
+ if ($secRangeID) {
+ $conditions[] = 'sec_range_id = ?';
+ $parameters[] = $secRangeID;
+ }
+
+ $where = implode(' AND ', $conditions);
+
+ return DatafieldEntryModel::deleteBySQL($where, $parameters);
+ }
+
+ public $value;
+ public $model;
+ public $rangeID;
+
+ /**
+ * Constructs this datafield
+ *
+ * @param DataField $datafield Underlying model
+ * @param mixed $range_id Range id (or array with range id and secondary
+ * range id)
+ * @param mixed $value Value
+ */
+ public function __construct(DataField $datafield = null, $rangeID = '', $value = null)
+ {
+ $this->model = $datafield;
+ $this->rangeID = $rangeID;
+ $this->value = isset($value) ? $value : $datafield->default_value;
+ }
+
+ /**
+ * Stores this datafield entry
+ *
+ * @return int representing the number of changed entries
+ */
+ public function store()
+ {
+ $id = [
+ $this->model->id,
+ (string) $this->getRangeID(),
+ (string) $this->getSecondRangeID(),
+ (string) $this->language
+ ];
+ $entry = new DatafieldEntryModel($id);
+ $entry->lang = (string) $this->language;
+
+ $old_value = $entry->content;
+ $entry->content = $this->getValue();
+
+ if ($entry->content == $this->model->default_value) {
+ $result = $entry->isNew() ? 0 : $entry->delete();
+ } else {
+ $result = $entry->store();
+ }
+
+ if ($result) {
+ NotificationCenter::postNotification('DatafieldDidUpdate', $this, [
+ 'changed' => $result,
+ 'old_value' => $old_value,
+ ]);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns whether this datafield is required
+ *
+ * @return bool indicating whether the datafield is required or not
+ */
+ public function isRequired()
+ {
+ return $this->model->is_required;
+ }
+
+ /**
+ * Returns the description of this datafield
+ *
+ * @return String containing the description
+ */
+ public function getDescription()
+ {
+ return $this->model->description;
+ }
+
+ /**
+ * Returns the type of this datafield
+ *
+ * @return string type of entry
+ */
+ public function getType()
+ {
+ $class = mb_strtolower(get_class($this));
+ return mb_substr($class, 9, mb_strpos($class, 'entry') - 9);
+ }
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ if ($entities) {
+ return htmlReady($this->getValue());
+ }
+ return $this->getValue();
+ }
+
+ /**
+ * Returns the value of the datafield
+ *
+ * @return mixed containing the value
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns the name of the datafield
+ *
+ * @return String containing the name
+ */
+ public function getName()
+ {
+ return $this->model->name;
+ }
+
+ /**
+ * Returns the id of the datafield
+ *
+ * @return String containing the id
+ */
+ public function getId()
+ {
+ return $this->model->id;
+ }
+
+ /**
+ * Returns the according input elements as html for this datafield
+ *
+ * @param String $name Name prefix of the associated input
+ * @param Array $variables Additional variables
+ * @return String containing the required html
+ */
+ public function getHTML($name = '', $variables = [])
+ {
+ $variables = array_merge([
+ 'name' => $name,
+ 'entry' => $this,
+ 'model' => $this->model,
+ 'value' => $this->getValue(),
+ ], $variables);
+
+ return $GLOBALS['template_factory']->render('datafields/' . $this->template, $variables);
+ }
+
+ /**
+ * Sets the value
+ *
+ * @param mixed $value The value
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($submitted_value)
+ {
+ $this->setValue($submitted_value);
+ }
+
+ /**
+ * Sets the range id
+ *
+ * @param String $range_id Range id
+ */
+ public function setRangeID($range_id)
+ {
+ $this->rangeID = $range_id;
+ }
+
+ /**
+ * Sets the secondary range id
+ *
+ * @param String $sec_range_id Secondary range id
+ */
+ public function setSecondRangeID($sec_range_id)
+ {
+ $this->rangeID = [$this->getRangeID(), $sec_range_id];
+ }
+
+ /**
+ * Sets the prefered content language if this is an i18n datafield.
+ *
+ * @param string $language The prefered display language
+ */
+ public function setContentLanguage($language)
+ {
+ if (!Config::get()->CONTENT_LANGUAGES[$language]) {
+ throw new InvalidArgumentException('Language not configured.');
+ }
+
+ $languages = array_keys(Config::get()->CONTENT_LANGUAGES);
+ if ($language == reset($languages)) {
+ $language = '';
+ }
+
+ $this->language = $language;
+ }
+
+ /**
+ * Checks if datafield is empty (was not set)
+ *
+ * @return bool true if empty, else false
+ */
+ public function isEmpty()
+ {
+ return $this->getValue() == '';
+ }
+
+ /**
+ * Returns whether the datafield contents are valid
+ *
+ * @return boolean indicating whether the datafield contents are valid
+ */
+ public function isValid()
+ {
+ return trim($this->getValue())
+ || !$this->model->is_required;
+ }
+
+ /**
+ * Returns the number of html fields this datafield uses for input.
+ *
+ * @return int representing the number of html fields
+ */
+ public function numberOfHTMLFields()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the range id
+ *
+ * @return String containing the range id
+ */
+ public function getRangeID()
+ {
+ if (is_array($this->rangeID)) {
+ return reset($this->rangeID);
+ }
+ return $this->rangeID;
+ }
+
+ /**
+ * Returns the secondary range id
+ *
+ * @return String containing the secondary range id
+ */
+ public function getSecondRangeID()
+ {
+ if (is_array($this->rangeID)) {
+ list (, $secRangeID) = $this->rangeID;
+ return $secRangeID;
+ }
+ return '';
+ }
+
+ /**
+ * Returns whether the datafield is visible for the current user
+ *
+ * @param String $perm Permissions to test against (optional, default to
+ * the current user's permissions)
+ * @param bool $test_ownership Defines whether the ownership of the field
+ * should be taken into account; a field may
+ * be invisible for a user according to the
+ * permissions but since the datafield belongs
+ * to the user, it is visible.
+ * @return boolean indicating whether the datafield is visible
+ */
+ public function isVisible($perm = null, $test_ownership = true)
+ {
+ if ($test_ownership) {
+ return $this->model->accessAllowed($perm,
+ $GLOBALS['user']->id,
+ $this->getRangeID());
+ }
+ return $this->model->accessAllowed($perm);
+ }
+
+ /**
+ * Returns whether the datafield is editable for the current user
+ *
+ * @param mixed $perms Perms to test against (optional, defaults to logged
+ * in user's perms)
+ * @return boolean indicating whether the datafield is editable
+ */
+ public function isEditable($perms = null)
+ {
+ return $this->model->editAllowed($perms ?: $GLOBALS['perm']->get_perm());
+ }
+
+ /**
+ * Returns a human readable string describing the view permissions
+ *
+ * @return String containing the descriptons of the view permissions
+ */
+ public function getPermsDescription()
+ {
+ if ($this->model->view_perms === 'all') {
+ return _('sichtbar für alle');
+ }
+ return sprintf(_('sichtbar nur für Sie und alle %s'),
+ $this->prettyPrintViewPerms());
+ }
+
+ /**
+ * Generates a full status description depending on the the perms
+ *
+ * @return string
+ */
+ protected function prettyPrintViewPerms()
+ {
+ switch ($this->model->view_perms) {
+ case 'all':
+ return _('alle');
+ break;
+ case 'root':
+ return _('Systemadministrator/-innen');
+ break;
+ case 'admin':
+ return _('Administrator/-innen');
+ break;
+ case 'dozent':
+ return _('Lehrenden');
+ break;
+ case 'tutor':
+ return _('Tutor/-innen');
+ break;
+ case 'autor':
+ return _('Studierenden');
+ break;
+ case 'user':
+ return _('Nutzer/-innen');
+ break;
+ }
+ return '';
+ }
+
+}
diff --git a/lib/classes/DataFieldLinkEntry.class.php b/lib/classes/DataFieldLinkEntry.class.php
deleted file mode 100644
index 702907f..0000000
--- a/lib/classes/DataFieldLinkEntry.class.php
+++ /dev/null
@@ -1,54 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldLinkEntry extends DataFieldEntry
-{
- protected $template = 'link.php';
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- if ($entities) {
- return formatLinks($this->getValue());
- }
- return $this->getValue();
- }
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($submitted_value)
- {
- if ($submitted_value === 'http://') {
- $submitted_value = '';
- }
- $this->setValue($submitted_value);
- }
-
- /**
- * Returns whether the datafield contents are valid
- *
- * @return boolean indicating whether the datafield contents are valid
- */
- public function isValid()
- {
- return parent::isValid()
- && (!$this->getValue()
- || (filter_var($this->getValue(), FILTER_VALIDATE_URL)
- && preg_match('%^(https?|ftp)://%', $this->getValue())));
- }
-}
diff --git a/lib/classes/DataFieldLinkEntry.php b/lib/classes/DataFieldLinkEntry.php
new file mode 100644
index 0000000..702907f
--- /dev/null
+++ b/lib/classes/DataFieldLinkEntry.php
@@ -0,0 +1,54 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldLinkEntry extends DataFieldEntry
+{
+ protected $template = 'link.php';
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ if ($entities) {
+ return formatLinks($this->getValue());
+ }
+ return $this->getValue();
+ }
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($submitted_value)
+ {
+ if ($submitted_value === 'http://') {
+ $submitted_value = '';
+ }
+ $this->setValue($submitted_value);
+ }
+
+ /**
+ * Returns whether the datafield contents are valid
+ *
+ * @return boolean indicating whether the datafield contents are valid
+ */
+ public function isValid()
+ {
+ return parent::isValid()
+ && (!$this->getValue()
+ || (filter_var($this->getValue(), FILTER_VALIDATE_URL)
+ && preg_match('%^(https?|ftp)://%', $this->getValue())));
+ }
+}
diff --git a/lib/classes/DataFieldPhoneEntry.class.php b/lib/classes/DataFieldPhoneEntry.class.php
deleted file mode 100644
index d3a0e70..0000000
--- a/lib/classes/DataFieldPhoneEntry.class.php
+++ /dev/null
@@ -1,122 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldPhoneEntry extends DataFieldEntry
-{
- protected $template = 'phone.php';
-
- /**
- * Returns the number of html fields this datafield uses for input.
- *
- * @return int representing the number of html fields
- */
- public function numberOfHTMLFields()
- {
- return 3;
- }
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($value)
- {
- if (is_array($value)) {
- $value = array_slice($value, 0, 3);
- $value = implode("\n", $value);
- $value = str_replace(' ', '', $value);
-
- parent::setValueFromSubmit($value);
- }
- }
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- list($country, $area, $phone) = $this->getNumberParts();
-
- if ($country || $area || $phone) {
- if ($country) {
- $country = "+$country";
- }
-
- return "$country $area $phone";
- }
-
- return '';
- }
-
- /**
- * Returns the according input elements as html for this datafield
- *
- * @param String $name Name prefix of the associated input
- * @param Array $variables Additional variables
- * @return String containing the required html
- */
- public function getHTML($name = '', $variables = [])
- {
- return parent::getHTML($name, $variables + [
- 'values' => $this->getNumberParts()
- ]);
- }
-
- /**
- * Checks if the datafield is empty (was not set)
- *
- * @return bool true if empty, else false
- */
- public function isEmpty()
- {
- return $this->getValue() == "\n\n";
- }
-
- /**
- * Returns whether the datafield contents are valid
- *
- * @return boolean indicating whether the datafield contents are valid
- */
- public function isValid()
- {
- $value = trim($this->value);
-
- if (!$value) {
- return parent::isValid();
- }
-
- return parent::isValid()
- && preg_match('/^([1-9]\d*)?\n[1-9]\d+\n[1-9]\d+(-\d+)?$/', $value);
- }
-
- /**
- * Retturns the individual parts of the telephone number.
- * The resulting array is always padded to contain at least
- * three items.
- *
- * @return array containing the individual parts.
- */
- protected function getNumberParts()
- {
- $values = explode("\n", $this->value);
-
- // pad values array to a size of 3 by inserting empty values from left
- while (count($values) < 3) {
- array_unshift($values, '');
- }
-
- return $values;
- }
-}
-
diff --git a/lib/classes/DataFieldPhoneEntry.php b/lib/classes/DataFieldPhoneEntry.php
new file mode 100644
index 0000000..d3a0e70
--- /dev/null
+++ b/lib/classes/DataFieldPhoneEntry.php
@@ -0,0 +1,122 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldPhoneEntry extends DataFieldEntry
+{
+ protected $template = 'phone.php';
+
+ /**
+ * Returns the number of html fields this datafield uses for input.
+ *
+ * @return int representing the number of html fields
+ */
+ public function numberOfHTMLFields()
+ {
+ return 3;
+ }
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($value)
+ {
+ if (is_array($value)) {
+ $value = array_slice($value, 0, 3);
+ $value = implode("\n", $value);
+ $value = str_replace(' ', '', $value);
+
+ parent::setValueFromSubmit($value);
+ }
+ }
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ list($country, $area, $phone) = $this->getNumberParts();
+
+ if ($country || $area || $phone) {
+ if ($country) {
+ $country = "+$country";
+ }
+
+ return "$country $area $phone";
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns the according input elements as html for this datafield
+ *
+ * @param String $name Name prefix of the associated input
+ * @param Array $variables Additional variables
+ * @return String containing the required html
+ */
+ public function getHTML($name = '', $variables = [])
+ {
+ return parent::getHTML($name, $variables + [
+ 'values' => $this->getNumberParts()
+ ]);
+ }
+
+ /**
+ * Checks if the datafield is empty (was not set)
+ *
+ * @return bool true if empty, else false
+ */
+ public function isEmpty()
+ {
+ return $this->getValue() == "\n\n";
+ }
+
+ /**
+ * Returns whether the datafield contents are valid
+ *
+ * @return boolean indicating whether the datafield contents are valid
+ */
+ public function isValid()
+ {
+ $value = trim($this->value);
+
+ if (!$value) {
+ return parent::isValid();
+ }
+
+ return parent::isValid()
+ && preg_match('/^([1-9]\d*)?\n[1-9]\d+\n[1-9]\d+(-\d+)?$/', $value);
+ }
+
+ /**
+ * Retturns the individual parts of the telephone number.
+ * The resulting array is always padded to contain at least
+ * three items.
+ *
+ * @return array containing the individual parts.
+ */
+ protected function getNumberParts()
+ {
+ $values = explode("\n", $this->value);
+
+ // pad values array to a size of 3 by inserting empty values from left
+ while (count($values) < 3) {
+ array_unshift($values, '');
+ }
+
+ return $values;
+ }
+}
+
diff --git a/lib/classes/DataFieldRadioEntry.class.php b/lib/classes/DataFieldRadioEntry.class.php
deleted file mode 100644
index 8699b69..0000000
--- a/lib/classes/DataFieldRadioEntry.class.php
+++ /dev/null
@@ -1,39 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldRadioEntry extends DataFieldSelectboxEntry
-{
- protected $template = 'radio.php';
-
- /**
- * Returns the number of html fields this datafield uses for input.
- *
- * @return int representing the number of html fields
- */
- public function numberOfHTMLFields()
- {
- return count($this->type_param);
- }
-
- /**
- * Returns the according input elements as html for this datafield
- *
- * @param String $name Name prefix of the associated input
- * @param Array $variables Additional variables
- * @return String containing the required html
- */
- public function getHTML($name = '', $variables = [])
- {
- return parent::getHTML($name, $variables + [
- 'type_param' => $this->type_param,
- 'is_assoc' => $this->is_assoc_param
- ]);
- }
-}
diff --git a/lib/classes/DataFieldRadioEntry.php b/lib/classes/DataFieldRadioEntry.php
new file mode 100644
index 0000000..8699b69
--- /dev/null
+++ b/lib/classes/DataFieldRadioEntry.php
@@ -0,0 +1,39 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldRadioEntry extends DataFieldSelectboxEntry
+{
+ protected $template = 'radio.php';
+
+ /**
+ * Returns the number of html fields this datafield uses for input.
+ *
+ * @return int representing the number of html fields
+ */
+ public function numberOfHTMLFields()
+ {
+ return count($this->type_param);
+ }
+
+ /**
+ * Returns the according input elements as html for this datafield
+ *
+ * @param String $name Name prefix of the associated input
+ * @param Array $variables Additional variables
+ * @return String containing the required html
+ */
+ public function getHTML($name = '', $variables = [])
+ {
+ return parent::getHTML($name, $variables + [
+ 'type_param' => $this->type_param,
+ 'is_assoc' => $this->is_assoc_param
+ ]);
+ }
+}
diff --git a/lib/classes/DataFieldSelectboxEntry.class.php b/lib/classes/DataFieldSelectboxEntry.class.php
deleted file mode 100644
index 8ac1a3c..0000000
--- a/lib/classes/DataFieldSelectboxEntry.class.php
+++ /dev/null
@@ -1,100 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldSelectboxEntry extends DataFieldEntry
-{
- protected $template = 'selectbox.php';
- protected $type_param;
- protected $is_assoc_param = false;
-
- /**
- * Constructs this datafield
- *
- * @param DataField $datafield Underlying model
- * @param String $rangeID Range id
- * @param mixed $value Value
- */
- public function __construct(DataField $struct = null, $range_id = '', $value = null)
- {
- parent::__construct($struct, $range_id, $value);
-
- list($values, $is_assoc) = $this->getParameters();
- $this->is_assoc_param = $is_assoc;
- $this->type_param = $values;
-
- if ($this->getValue() === null) {
- reset($values);
-
- if ($is_assoc) {
- $this->setValue((string)key($values));
- } else {
- $this->setValue(current($values)); // first selectbox entry is default
- }
- }
- }
-
- /**
- * Returns the according input elements as html for this datafield
- *
- * @param String $name Name prefix of the associated input
- * @param Array $variables Additional variables
- * @return String containing the required html
- */
- public function getHTML($name = '', $variables = [])
- {
- $variables = array_merge([
- 'multiple' => false,
- 'type_param' => $this->type_param,
- 'is_assoc' => $this->is_assoc_param,
- ], $variables);
-
- return parent::getHTML($name, $variables);
- }
-
- /**
- * Returns the individual type parameters.
- *
- * @return array containing the individual type parameters
- */
- public function getParameters()
- {
- $params = explode("\n", rtrim($this->model->typeparam));
- $params = array_map('trim', $params);
-
- $ret = [];
- $is_assoc = false;
-
- foreach ($params as $i => $p) {
- if (mb_strpos($p, '=>') !== false) {
- $is_assoc = true;
-
- list($key, $value) = array_map('trim', explode('=>', $p, 2));
- $ret[$key] = $value;
- } else {
- $ret[$i] = $p;
- }
- }
- return [$ret, $is_assoc];
- }
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- $value = $this->is_assoc_param
- ? $this->type_param[$this->getValue()]
- : $this->getValue();
- return $entities ? htmlReady($value) : $value;
- }
-}
diff --git a/lib/classes/DataFieldSelectboxEntry.php b/lib/classes/DataFieldSelectboxEntry.php
new file mode 100644
index 0000000..8ac1a3c
--- /dev/null
+++ b/lib/classes/DataFieldSelectboxEntry.php
@@ -0,0 +1,100 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldSelectboxEntry extends DataFieldEntry
+{
+ protected $template = 'selectbox.php';
+ protected $type_param;
+ protected $is_assoc_param = false;
+
+ /**
+ * Constructs this datafield
+ *
+ * @param DataField $datafield Underlying model
+ * @param String $rangeID Range id
+ * @param mixed $value Value
+ */
+ public function __construct(DataField $struct = null, $range_id = '', $value = null)
+ {
+ parent::__construct($struct, $range_id, $value);
+
+ list($values, $is_assoc) = $this->getParameters();
+ $this->is_assoc_param = $is_assoc;
+ $this->type_param = $values;
+
+ if ($this->getValue() === null) {
+ reset($values);
+
+ if ($is_assoc) {
+ $this->setValue((string)key($values));
+ } else {
+ $this->setValue(current($values)); // first selectbox entry is default
+ }
+ }
+ }
+
+ /**
+ * Returns the according input elements as html for this datafield
+ *
+ * @param String $name Name prefix of the associated input
+ * @param Array $variables Additional variables
+ * @return String containing the required html
+ */
+ public function getHTML($name = '', $variables = [])
+ {
+ $variables = array_merge([
+ 'multiple' => false,
+ 'type_param' => $this->type_param,
+ 'is_assoc' => $this->is_assoc_param,
+ ], $variables);
+
+ return parent::getHTML($name, $variables);
+ }
+
+ /**
+ * Returns the individual type parameters.
+ *
+ * @return array containing the individual type parameters
+ */
+ public function getParameters()
+ {
+ $params = explode("\n", rtrim($this->model->typeparam));
+ $params = array_map('trim', $params);
+
+ $ret = [];
+ $is_assoc = false;
+
+ foreach ($params as $i => $p) {
+ if (mb_strpos($p, '=>') !== false) {
+ $is_assoc = true;
+
+ list($key, $value) = array_map('trim', explode('=>', $p, 2));
+ $ret[$key] = $value;
+ } else {
+ $ret[$i] = $p;
+ }
+ }
+ return [$ret, $is_assoc];
+ }
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ $value = $this->is_assoc_param
+ ? $this->type_param[$this->getValue()]
+ : $this->getValue();
+ return $entities ? htmlReady($value) : $value;
+ }
+}
diff --git a/lib/classes/DataFieldSelectboxMultipleEntry.class.php b/lib/classes/DataFieldSelectboxMultipleEntry.class.php
deleted file mode 100644
index 3abb373..0000000
--- a/lib/classes/DataFieldSelectboxMultipleEntry.class.php
+++ /dev/null
@@ -1,93 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldSelectboxMultipleEntry extends DataFieldSelectboxEntry
-{
- const SEPARATOR = '|';
-
- /**
- * Constructs this datafield
- *
- * @param DataField $datafield Underlying model
- * @param String $rangeID Range id
- * @param mixed $value Value
- */
- public function __construct(DataField $datafield = null, $rangeID = '', $value = null)
- {
- parent::__construct($datafield, $rangeID, $value);
-
- if ($this->getValue() === null) {
- $this->setValue('');
- }
- }
-
- /**
- * Returns the according input elements as html for this datafield
- *
- * @param String $name Name prefix of the associated input
- * @param Array $variables Additional variables
- * @return String containing the required html
- */
- public function getHTML($name = '', $variables = [])
- {
- return parent::getHTML($name, $variables + [
- 'multiple' => true,
- 'value' => explode(self::SEPARATOR, $this->value)
- ]);
- }
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- $value = $this->getValue();
- if ($value) {
- $type_param = $this->type_param;
-
- $mapper = 'trim';
- if ($this->is_assoc_param) {
- $mapper = function ($a) use ($type_param) {
- $a = trim($a);
- return $type_param[$a];
- };
- }
-
- $value = explode(self::SEPARATOR, $value);
- $value = array_map($mapper, $value);
- $value = implode('; ', $value);
- }
- return $entities
- ? htmlReady($value)
- : $value;
- }
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($value)
- {
- if (is_array($value)) {
- $value = array_map('trim', $value);
- $value = array_filter($value);
- $value = array_unique($value);
- $value = implode(self::SEPARATOR, $value);
- } else {
- $value = '';
- }
-
- parent::setValueFromSubmit($value);
- }
-}
diff --git a/lib/classes/DataFieldSelectboxMultipleEntry.php b/lib/classes/DataFieldSelectboxMultipleEntry.php
new file mode 100644
index 0000000..3abb373
--- /dev/null
+++ b/lib/classes/DataFieldSelectboxMultipleEntry.php
@@ -0,0 +1,93 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldSelectboxMultipleEntry extends DataFieldSelectboxEntry
+{
+ const SEPARATOR = '|';
+
+ /**
+ * Constructs this datafield
+ *
+ * @param DataField $datafield Underlying model
+ * @param String $rangeID Range id
+ * @param mixed $value Value
+ */
+ public function __construct(DataField $datafield = null, $rangeID = '', $value = null)
+ {
+ parent::__construct($datafield, $rangeID, $value);
+
+ if ($this->getValue() === null) {
+ $this->setValue('');
+ }
+ }
+
+ /**
+ * Returns the according input elements as html for this datafield
+ *
+ * @param String $name Name prefix of the associated input
+ * @param Array $variables Additional variables
+ * @return String containing the required html
+ */
+ public function getHTML($name = '', $variables = [])
+ {
+ return parent::getHTML($name, $variables + [
+ 'multiple' => true,
+ 'value' => explode(self::SEPARATOR, $this->value)
+ ]);
+ }
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ $value = $this->getValue();
+ if ($value) {
+ $type_param = $this->type_param;
+
+ $mapper = 'trim';
+ if ($this->is_assoc_param) {
+ $mapper = function ($a) use ($type_param) {
+ $a = trim($a);
+ return $type_param[$a];
+ };
+ }
+
+ $value = explode(self::SEPARATOR, $value);
+ $value = array_map($mapper, $value);
+ $value = implode('; ', $value);
+ }
+ return $entities
+ ? htmlReady($value)
+ : $value;
+ }
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($value)
+ {
+ if (is_array($value)) {
+ $value = array_map('trim', $value);
+ $value = array_filter($value);
+ $value = array_unique($value);
+ $value = implode(self::SEPARATOR, $value);
+ } else {
+ $value = '';
+ }
+
+ parent::setValueFromSubmit($value);
+ }
+}
diff --git a/lib/classes/DataFieldTextareaEntry.class.php b/lib/classes/DataFieldTextareaEntry.class.php
deleted file mode 100644
index a48be98..0000000
--- a/lib/classes/DataFieldTextareaEntry.class.php
+++ /dev/null
@@ -1,29 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldTextareaEntry extends DataFieldEntry
-{
- protected $template = 'textarea.php';
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- if ($entities) {
- return htmlReady($this->getValue(), true, true);
- }
-
- return $this->getValue();
- }
-}
diff --git a/lib/classes/DataFieldTextareaEntry.php b/lib/classes/DataFieldTextareaEntry.php
new file mode 100644
index 0000000..a48be98
--- /dev/null
+++ b/lib/classes/DataFieldTextareaEntry.php
@@ -0,0 +1,29 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldTextareaEntry extends DataFieldEntry
+{
+ protected $template = 'textarea.php';
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ if ($entities) {
+ return htmlReady($this->getValue(), true, true);
+ }
+
+ return $this->getValue();
+ }
+}
diff --git a/lib/classes/DataFieldTextareai18nEntry.class.php b/lib/classes/DataFieldTextareai18nEntry.class.php
deleted file mode 100644
index 9b6db40..0000000
--- a/lib/classes/DataFieldTextareai18nEntry.class.php
+++ /dev/null
@@ -1,21 +0,0 @@
-
- * @copyright 2017 Stud.IP Core-Group
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @since 4.1
- *
- */
-class DataFieldTextareai18nEntry extends DataFieldI18NEntry
-{
- protected $template = 'textareai18n.php';
-}
diff --git a/lib/classes/DataFieldTextareai18nEntry.php b/lib/classes/DataFieldTextareai18nEntry.php
new file mode 100644
index 0000000..9b6db40
--- /dev/null
+++ b/lib/classes/DataFieldTextareai18nEntry.php
@@ -0,0 +1,21 @@
+
+ * @copyright 2017 Stud.IP Core-Group
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 4.1
+ *
+ */
+class DataFieldTextareai18nEntry extends DataFieldI18NEntry
+{
+ protected $template = 'textareai18n.php';
+}
diff --git a/lib/classes/DataFieldTextlineEntry.class.php b/lib/classes/DataFieldTextlineEntry.class.php
deleted file mode 100644
index 6062ed1..0000000
--- a/lib/classes/DataFieldTextlineEntry.class.php
+++ /dev/null
@@ -1,14 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldTextlineEntry extends DataFieldEntry
-{
- protected $template = 'textline.php';
-}
diff --git a/lib/classes/DataFieldTextlineEntry.php b/lib/classes/DataFieldTextlineEntry.php
new file mode 100644
index 0000000..6062ed1
--- /dev/null
+++ b/lib/classes/DataFieldTextlineEntry.php
@@ -0,0 +1,14 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldTextlineEntry extends DataFieldEntry
+{
+ protected $template = 'textline.php';
+}
diff --git a/lib/classes/DataFieldTextlinei18nEntry.class.php b/lib/classes/DataFieldTextlinei18nEntry.class.php
deleted file mode 100644
index 9d1f344..0000000
--- a/lib/classes/DataFieldTextlinei18nEntry.class.php
+++ /dev/null
@@ -1,21 +0,0 @@
-
- * @copyright 2017 Stud.IP Core-Group
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @since 4.1
- *
- */
-class DataFieldTextlinei18nEntry extends DataFieldI18NEntry
-{
- protected $template = 'textlinei18n.php';
-}
diff --git a/lib/classes/DataFieldTextlinei18nEntry.php b/lib/classes/DataFieldTextlinei18nEntry.php
new file mode 100644
index 0000000..9d1f344
--- /dev/null
+++ b/lib/classes/DataFieldTextlinei18nEntry.php
@@ -0,0 +1,21 @@
+
+ * @copyright 2017 Stud.IP Core-Group
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 4.1
+ *
+ */
+class DataFieldTextlinei18nEntry extends DataFieldI18NEntry
+{
+ protected $template = 'textlinei18n.php';
+}
diff --git a/lib/classes/DataFieldTextmarkupEntry.class.php b/lib/classes/DataFieldTextmarkupEntry.class.php
deleted file mode 100644
index 50115b9..0000000
--- a/lib/classes/DataFieldTextmarkupEntry.class.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
- * @license GPL2 or any later version
- */
-class DataFieldTextmarkupEntry extends DataFieldTextareaEntry
-{
- protected $template = 'textmarkup.php';
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($submitted_value)
- {
- $this->setValue(Studip\Markup::purifyHtml($submitted_value));
- }
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- if ($entities) {
- return formatReady($this->getValue());
- }
-
- return $this->getValue();
- }
-}
diff --git a/lib/classes/DataFieldTextmarkupEntry.php b/lib/classes/DataFieldTextmarkupEntry.php
new file mode 100644
index 0000000..50115b9
--- /dev/null
+++ b/lib/classes/DataFieldTextmarkupEntry.php
@@ -0,0 +1,35 @@
+
+ * @license GPL2 or any later version
+ */
+class DataFieldTextmarkupEntry extends DataFieldTextareaEntry
+{
+ protected $template = 'textmarkup.php';
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($submitted_value)
+ {
+ $this->setValue(Studip\Markup::purifyHtml($submitted_value));
+ }
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ if ($entities) {
+ return formatReady($this->getValue());
+ }
+
+ return $this->getValue();
+ }
+}
diff --git a/lib/classes/DataFieldTextmarkupi18nEntry.class.php b/lib/classes/DataFieldTextmarkupi18nEntry.class.php
deleted file mode 100644
index 0b3d7e6..0000000
--- a/lib/classes/DataFieldTextmarkupi18nEntry.class.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- * @copyright 2017 Stud.IP Core-Group
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @since 4.1
- *
- */
-class DataFieldTextmarkupi18nEntry extends DataFieldTextareai18nEntry
-{
- protected $template = 'textmarkupi18n.php';
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($submitted_value)
- {
- array_walk($submitted_value, 'Studip\Markup::purifyHtml');
- parent::setValueFromSubmit($submitted_value);
- }
-
- /**
- * Returns the display/rendered value of this datafield
- *
- * @param bool $entities Should html entities be encoded (defaults to true)
- * @return String containg the rendered value
- */
- public function getDisplayValue($entities = true)
- {
- if ($entities) {
- return formatReady($this->getValue());
- }
-
- return $this->getValue();
- }
-}
diff --git a/lib/classes/DataFieldTextmarkupi18nEntry.php b/lib/classes/DataFieldTextmarkupi18nEntry.php
new file mode 100644
index 0000000..0b3d7e6
--- /dev/null
+++ b/lib/classes/DataFieldTextmarkupi18nEntry.php
@@ -0,0 +1,47 @@
+
+ * @copyright 2017 Stud.IP Core-Group
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 4.1
+ *
+ */
+class DataFieldTextmarkupi18nEntry extends DataFieldTextareai18nEntry
+{
+ protected $template = 'textmarkupi18n.php';
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($submitted_value)
+ {
+ array_walk($submitted_value, 'Studip\Markup::purifyHtml');
+ parent::setValueFromSubmit($submitted_value);
+ }
+
+ /**
+ * Returns the display/rendered value of this datafield
+ *
+ * @param bool $entities Should html entities be encoded (defaults to true)
+ * @return String containg the rendered value
+ */
+ public function getDisplayValue($entities = true)
+ {
+ if ($entities) {
+ return formatReady($this->getValue());
+ }
+
+ return $this->getValue();
+ }
+}
diff --git a/lib/classes/DataFieldTimeEntry.class.php b/lib/classes/DataFieldTimeEntry.class.php
deleted file mode 100644
index 96e5835..0000000
--- a/lib/classes/DataFieldTimeEntry.class.php
+++ /dev/null
@@ -1,50 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @author Martin Gieseking
- * @license GPL2 or any later version
- */
-class DataFieldTimeEntry extends DataFieldEntry
-{
- protected $template = 'time.php';
-
- /**
- * Sets the value from a post request
- *
- * @param mixed $submitted_value The value from request
- */
- public function setValueFromSubmit($value)
- {
- if ($value) {
- parent::setValueFromSubmit($value);
- }
- }
-
- /**
- * Checks if the datafield is empty (was not set)
- *
- * @return bool true if empty, else false
- */
- public function isEmpty()
- {
- return $this->getValue() == ':';
- }
-
- /**
- * Returns whether the datafield contents are valid
- *
- * @return boolean indicating whether the datafield contents are valid
- */
- public function isValid()
- {
- $parts = explode(':', $this->value);
-
- return parent::isValid()
- && $parts[0] >= 0 && $parts[0] <= 24
- && $parts[1] >= 0 && $parts[1] <= 59;
- }
-}
diff --git a/lib/classes/DataFieldTimeEntry.php b/lib/classes/DataFieldTimeEntry.php
new file mode 100644
index 0000000..96e5835
--- /dev/null
+++ b/lib/classes/DataFieldTimeEntry.php
@@ -0,0 +1,50 @@
+
+ * @author Marcus Lunzenauer
+ * @author Martin Gieseking
+ * @license GPL2 or any later version
+ */
+class DataFieldTimeEntry extends DataFieldEntry
+{
+ protected $template = 'time.php';
+
+ /**
+ * Sets the value from a post request
+ *
+ * @param mixed $submitted_value The value from request
+ */
+ public function setValueFromSubmit($value)
+ {
+ if ($value) {
+ parent::setValueFromSubmit($value);
+ }
+ }
+
+ /**
+ * Checks if the datafield is empty (was not set)
+ *
+ * @return bool true if empty, else false
+ */
+ public function isEmpty()
+ {
+ return $this->getValue() == ':';
+ }
+
+ /**
+ * Returns whether the datafield contents are valid
+ *
+ * @return boolean indicating whether the datafield contents are valid
+ */
+ public function isValid()
+ {
+ $parts = explode(':', $this->value);
+
+ return parent::isValid()
+ && $parts[0] >= 0 && $parts[0] <= 24
+ && $parts[1] >= 0 && $parts[1] <= 59;
+ }
+}
diff --git a/lib/classes/DatabaseObject.class.php b/lib/classes/DatabaseObject.class.php
deleted file mode 100644
index 2258adb..0000000
--- a/lib/classes/DatabaseObject.class.php
+++ /dev/null
@@ -1,149 +0,0 @@
-
-// +--------------------------------------------------------------------------+
-// 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 any later version.
-// +--------------------------------------------------------------------------+
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// +--------------------------------------------------------------------------+
-
-# Define all required constants ============================================= #
-/**
- * @const INSTANCEOF_STUDIPOBJECT Is instance of a studip object
- * @access public
- */
-define("INSTANCEOF_DATABASEOBJECT", "DatabaseObject");
-# =========================================================================== #
-
-
-/**
- * DatabaseObject.class.php
- *
- * Class to provide basic properties of an DatabaseObject in Stud.IP
- *
- * @author Alexander Willner
- * @copyright 2003 Stud.IP-Project
- * @access public
- * @package studip_core
- * @modulegroup core
- */
-class DatabaseObject extends AuthorObject
-{
- public $authorID;
- public $objectID;
- public $rangeID;
-
-# Define constructor and destructor ========================================= #
- /**
- * Constructor
- *
- * @access public
- */
- public function __construct()
- {
- /* For good OOP: Call constructor ------------------------------------- */
- parent::__construct();
- $this->instanceof = INSTANCEOF_DATABASEOBJECT;
- /* -------------------------------------------------------------------- */
- }
-# =========================================================================== #
-
-
-# Define public functions =================================================== #
- /**
- * Gets the objectID
- *
- * @access public
- * @return string The objectID
- */
- public function getObjectID()
- {
- return $this->objectID;
- }
-
- /**
- * Sets the objectID
- *
- * @access public
- *
- * @param String $objectID The object ID
- */
- public function setObjectID($objectID)
- {
- if (empty ($objectID))
- $this->throwError(1, _("Die ObjectID darf nicht leer sein."));
- else
- $this->objectID = $objectID;
- }
-
- /**
- * Gets the authorID
- *
- * @access public
- * @return string The authorID
- */
- public function getAuthorID()
- {
- return $this->authorID;
- }
-
- /**
- * Sets the authorID
- *
- * @access public
- *
- * @param String $authorID The author ID
- */
- public function setAuthorID($authorID)
- {
- if (empty ($authorID))
- $this->throwError(1, _("Die AuthorID darf nicht leer sein."));
- else
- $this->authorID = $authorID;
- }
-
- /**
- * Gets the rangeID
- *
- * @access public
- * @return string The rangeID
- */
- public function getRangeID()
- {
- return $this->objectID;
- }
-
- /**
- * Sets the rangeID
- *
- * @access public
- *
- * @param String $rangeID The range ID
- */
- public function setRangeID($rangeID)
- {
- if (empty ($rangeID))
- $this->throwError(1, _("Die RangeID darf nicht leer sein."));
- else
- $this->rangeID = $rangeID;
- }
-
-}
diff --git a/lib/classes/DatabaseObject.php b/lib/classes/DatabaseObject.php
new file mode 100644
index 0000000..2258adb
--- /dev/null
+++ b/lib/classes/DatabaseObject.php
@@ -0,0 +1,149 @@
+
+// +--------------------------------------------------------------------------+
+// 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 any later version.
+// +--------------------------------------------------------------------------+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// +--------------------------------------------------------------------------+
+
+# Define all required constants ============================================= #
+/**
+ * @const INSTANCEOF_STUDIPOBJECT Is instance of a studip object
+ * @access public
+ */
+define("INSTANCEOF_DATABASEOBJECT", "DatabaseObject");
+# =========================================================================== #
+
+
+/**
+ * DatabaseObject.class.php
+ *
+ * Class to provide basic properties of an DatabaseObject in Stud.IP
+ *
+ * @author Alexander Willner
+ * @copyright 2003 Stud.IP-Project
+ * @access public
+ * @package studip_core
+ * @modulegroup core
+ */
+class DatabaseObject extends AuthorObject
+{
+ public $authorID;
+ public $objectID;
+ public $rangeID;
+
+# Define constructor and destructor ========================================= #
+ /**
+ * Constructor
+ *
+ * @access public
+ */
+ public function __construct()
+ {
+ /* For good OOP: Call constructor ------------------------------------- */
+ parent::__construct();
+ $this->instanceof = INSTANCEOF_DATABASEOBJECT;
+ /* -------------------------------------------------------------------- */
+ }
+# =========================================================================== #
+
+
+# Define public functions =================================================== #
+ /**
+ * Gets the objectID
+ *
+ * @access public
+ * @return string The objectID
+ */
+ public function getObjectID()
+ {
+ return $this->objectID;
+ }
+
+ /**
+ * Sets the objectID
+ *
+ * @access public
+ *
+ * @param String $objectID The object ID
+ */
+ public function setObjectID($objectID)
+ {
+ if (empty ($objectID))
+ $this->throwError(1, _("Die ObjectID darf nicht leer sein."));
+ else
+ $this->objectID = $objectID;
+ }
+
+ /**
+ * Gets the authorID
+ *
+ * @access public
+ * @return string The authorID
+ */
+ public function getAuthorID()
+ {
+ return $this->authorID;
+ }
+
+ /**
+ * Sets the authorID
+ *
+ * @access public
+ *
+ * @param String $authorID The author ID
+ */
+ public function setAuthorID($authorID)
+ {
+ if (empty ($authorID))
+ $this->throwError(1, _("Die AuthorID darf nicht leer sein."));
+ else
+ $this->authorID = $authorID;
+ }
+
+ /**
+ * Gets the rangeID
+ *
+ * @access public
+ * @return string The rangeID
+ */
+ public function getRangeID()
+ {
+ return $this->objectID;
+ }
+
+ /**
+ * Sets the rangeID
+ *
+ * @access public
+ *
+ * @param String $rangeID The range ID
+ */
+ public function setRangeID($rangeID)
+ {
+ if (empty ($rangeID))
+ $this->throwError(1, _("Die RangeID darf nicht leer sein."));
+ else
+ $this->rangeID = $rangeID;
+ }
+
+}
diff --git a/lib/classes/DateFormatter.class.php b/lib/classes/DateFormatter.class.php
deleted file mode 100644
index 5f7c558..0000000
--- a/lib/classes/DateFormatter.class.php
+++ /dev/null
@@ -1,176 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-/**
- * Formates one SingleDate object or a series of SingleDate objects into a nice format.
- */
-class DateFormatter {
- /**
- * @var array holds the dates use for formatting
- */
- private $dates;
-
- /**
- * @var string holds the return-type, may be int or string
- */
- private $return_mode;
-
- /**
- * @param $dates an array with an array of SingleDate objects.
- * @param string $return_mode expected values are 'int', 'string' and 'export'. The default value is 'string'.
- * @return void
- */
- private function __construct($dates, $return_mode = 'string')
- {
- $this->dates = $dates;
- $this->return_mode = $return_mode;
- }
-
- /**
- * Formats one single date into a nice format.
- * @static
- * @param $date SingleDate object
- * @param string $return_mode expected values are 'int', 'string' and 'export'. The default value is 'string'.
- * @return string
- */
- public static function formatDateAndRoom($date, $return_mode = 'string')
- {
- $dates = DateFormatter::wrapDateWithArray($date);
- return DateFormatter::formatDateWithAllRooms($dates, $return_mode);
- }
-
- /**
- * Formats a series of SingleDate objects into a nice format. The dates parameter is an array of dates.
- * The array has to have the key 'termin' with an array of SingleDate objects as value.
- * @static
- * @param $dates an array with an array of SingleDate objects
- * @param string $return_mode expected values are 'int', 'string' and 'export'. The default value is 'string'.
- * @return string
- */
- public static function formatDateWithAllRooms($dates, $return_mode = 'string')
- {
- $dateFormatter = new DateFormatter($dates, $return_mode);
- return $dateFormatter->internalFormatDateWithAllRooms();
- }
-
- private static function wrapDateWithArray($date)
- {
- $dates = [];
- $dates['termin'] = [$date];
- return $dates;
- }
-
- private function internalFormatDateWithAllRooms()
- {
- $dateWithRooms = '';
- if ($this->dates['termin']) {
- // if we have multiple rooms at the same time we display them all
- foreach ($this->dates['termin'] as $num => $termin_id) {
- $date = new SingleDate($termin_id);
-
- // if we want an int and format the date ourself
- if ($this->return_mode == 'int') {
- return $date->getStartTime();
- }
-
- $isFirstDate = ($num == 0);
- if ($isFirstDate) {
- $dateWithRooms = $this->internalFormatDateAndRoom($date);
- } else {
- $dateWithRooms .= ', ' . $this->formatRoom($date);
- }
- }
- }
- return $dateWithRooms;
- }
-
- private function internalFormatDateAndRoom($date)
- {
- $ret = $this->formatDate($date);
-
- if ($this->return_mode != 'int') {
- $formatedRooms = $this->formatRoom($date);
- if ($formatedRooms) {
- $ret .= ', ';
- $ret .= _("Ort:") . ' ';
- $ret .= $formatedRooms;
- }
- }
-
- return $ret;
- }
-
- private function formatDate($date)
- {
- if ($this->return_mode == 'int') {
- return $date->getStartTime();
- }
- else {
- return $date->toString();
- }
- }
-
- private function formatRoom($date)
- {
- if ($this->return_mode == 'int') {
- return '';
- }
- else {
- return $this->formatLocationText($date);
- }
- }
-
- private function formatLocationText($date)
- {
- if ($this->hasResource($date)) {
- $resObj = Resource::find($date->getResourceID());
- return $this->generateLocationTextFromResourceObject($resObj);
- } else if ($this->hasFreeRoomText($date)) {
- return $this->generateLocationTextFromFreeRoomText($date);
- } else {
- return '';
- }
- }
-
- private function generateLocationTextFromResourceObject($resObj)
- {
- if ($resObj) {
- if ($this->return_mode == 'string') {
- return ''
- . htmlReady($resObj->name)
- . '';
- }
- else {
- return htmlReady($resObj->name);
- }
- }
- return '';
- }
-
- private function generateLocationTextFromFreeRoomText($date)
- {
- return '(' . htmlReady($date->getFreeRoomText()) . ')';
- }
-
- private function hasResource($date)
- {
- return $date && $date->getResourceID();
- }
-
- private function hasFreeRoomText($date)
- {
- return $date && $date->getFreeRoomText();
- }
-}
diff --git a/lib/classes/DateFormatter.php b/lib/classes/DateFormatter.php
new file mode 100644
index 0000000..5f7c558
--- /dev/null
+++ b/lib/classes/DateFormatter.php
@@ -0,0 +1,176 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ */
+
+/**
+ * Formates one SingleDate object or a series of SingleDate objects into a nice format.
+ */
+class DateFormatter {
+ /**
+ * @var array holds the dates use for formatting
+ */
+ private $dates;
+
+ /**
+ * @var string holds the return-type, may be int or string
+ */
+ private $return_mode;
+
+ /**
+ * @param $dates an array with an array of SingleDate objects.
+ * @param string $return_mode expected values are 'int', 'string' and 'export'. The default value is 'string'.
+ * @return void
+ */
+ private function __construct($dates, $return_mode = 'string')
+ {
+ $this->dates = $dates;
+ $this->return_mode = $return_mode;
+ }
+
+ /**
+ * Formats one single date into a nice format.
+ * @static
+ * @param $date SingleDate object
+ * @param string $return_mode expected values are 'int', 'string' and 'export'. The default value is 'string'.
+ * @return string
+ */
+ public static function formatDateAndRoom($date, $return_mode = 'string')
+ {
+ $dates = DateFormatter::wrapDateWithArray($date);
+ return DateFormatter::formatDateWithAllRooms($dates, $return_mode);
+ }
+
+ /**
+ * Formats a series of SingleDate objects into a nice format. The dates parameter is an array of dates.
+ * The array has to have the key 'termin' with an array of SingleDate objects as value.
+ * @static
+ * @param $dates an array with an array of SingleDate objects
+ * @param string $return_mode expected values are 'int', 'string' and 'export'. The default value is 'string'.
+ * @return string
+ */
+ public static function formatDateWithAllRooms($dates, $return_mode = 'string')
+ {
+ $dateFormatter = new DateFormatter($dates, $return_mode);
+ return $dateFormatter->internalFormatDateWithAllRooms();
+ }
+
+ private static function wrapDateWithArray($date)
+ {
+ $dates = [];
+ $dates['termin'] = [$date];
+ return $dates;
+ }
+
+ private function internalFormatDateWithAllRooms()
+ {
+ $dateWithRooms = '';
+ if ($this->dates['termin']) {
+ // if we have multiple rooms at the same time we display them all
+ foreach ($this->dates['termin'] as $num => $termin_id) {
+ $date = new SingleDate($termin_id);
+
+ // if we want an int and format the date ourself
+ if ($this->return_mode == 'int') {
+ return $date->getStartTime();
+ }
+
+ $isFirstDate = ($num == 0);
+ if ($isFirstDate) {
+ $dateWithRooms = $this->internalFormatDateAndRoom($date);
+ } else {
+ $dateWithRooms .= ', ' . $this->formatRoom($date);
+ }
+ }
+ }
+ return $dateWithRooms;
+ }
+
+ private function internalFormatDateAndRoom($date)
+ {
+ $ret = $this->formatDate($date);
+
+ if ($this->return_mode != 'int') {
+ $formatedRooms = $this->formatRoom($date);
+ if ($formatedRooms) {
+ $ret .= ', ';
+ $ret .= _("Ort:") . ' ';
+ $ret .= $formatedRooms;
+ }
+ }
+
+ return $ret;
+ }
+
+ private function formatDate($date)
+ {
+ if ($this->return_mode == 'int') {
+ return $date->getStartTime();
+ }
+ else {
+ return $date->toString();
+ }
+ }
+
+ private function formatRoom($date)
+ {
+ if ($this->return_mode == 'int') {
+ return '';
+ }
+ else {
+ return $this->formatLocationText($date);
+ }
+ }
+
+ private function formatLocationText($date)
+ {
+ if ($this->hasResource($date)) {
+ $resObj = Resource::find($date->getResourceID());
+ return $this->generateLocationTextFromResourceObject($resObj);
+ } else if ($this->hasFreeRoomText($date)) {
+ return $this->generateLocationTextFromFreeRoomText($date);
+ } else {
+ return '';
+ }
+ }
+
+ private function generateLocationTextFromResourceObject($resObj)
+ {
+ if ($resObj) {
+ if ($this->return_mode == 'string') {
+ return ''
+ . htmlReady($resObj->name)
+ . '';
+ }
+ else {
+ return htmlReady($resObj->name);
+ }
+ }
+ return '';
+ }
+
+ private function generateLocationTextFromFreeRoomText($date)
+ {
+ return '(' . htmlReady($date->getFreeRoomText()) . ')';
+ }
+
+ private function hasResource($date)
+ {
+ return $date && $date->getResourceID();
+ }
+
+ private function hasFreeRoomText($date)
+ {
+ return $date && $date->getFreeRoomText();
+ }
+}
diff --git a/lib/classes/DbSnapshot.class.php b/lib/classes/DbSnapshot.class.php
deleted file mode 100644
index dfb4f89..0000000
--- a/lib/classes/DbSnapshot.class.php
+++ /dev/null
@@ -1,370 +0,0 @@
-
-// +---------------------------------------------------------------------------+
-// 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 any later version.
-// +---------------------------------------------------------------------------+
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// +---------------------------------------------------------------------------+
-
-
-/**
- * Class to provide snapshots of mysql result sets
- *
- * Uses DB abstraction layer of PHPLib
- *
- * @access public
- * @author André Noack
- * @package DBTools
- **/
-class DbSnapshot
-{
-
- /**
- * the used db abstraction class
- *
- *
- * @access private
- * @var string $DbClass
- */
- var $DbClass = "DB_Sql";
- /**
- * the used db result set
- *
- *
- * @access private
- * @var object DB_Sql $dbResult
- */
- var $dbResult = null;
- /**
- * array to store the result set
- *
- *
- * @access private
- * @var array $result
- */
- var $result = [];
- /**
- * array to store metadata oh the result set
- *
- *
- * @access private
- * @var array $metaData
- */
- var $metaData = [];
- /**
- * the number of fields in the result set
- *
- *
- * @access public
- * @var integer $numFields
- */
- var $numFields = 0;
- /**
- * the number of rows in the result set
- *
- *
- * @access public
- * @var integer $numRows
- */
- var $numRows = 0;
- /**
- * the internal row pointer
- *
- *
- * @access private
- * @var mixed $pos
- */
- var $pos = false;
- /**
- * turn on/off debugging
- *
- *
- * @access public
- * @var boolean $debug
- */
- var $debug = false;
-
- /**
- * Constructor
- *
- * Pass instance of DbClass or nothing to specify result set later
- *
- * @access public
- *
- * @param object DB_Sql $dbresult
- */
- public function __construct($dbresult = null)
- {
- if (is_object($dbresult)) {
- $this->dbResult = $dbresult;
- $this->getSnapshot();
- }
-
- }
-
- function isDbResult()
- {
- if (!is_subclass_of($this->dbResult, $this->DbClass))
- $this->halt("Result set has wrong type!");
- if (!$this->dbResult->query_id())
- $this->halt("No result set (missing query?)");
-
- return true;
- }
-
- public function getSnapshot()
- {
- if ($this->isDbResult()) {
- $this->numFields = $this->dbResult->num_fields();
- $this->numRows = $this->dbResult->num_rows();
- $this->metaData = $this->dbResult->metadata();
- $this->result = [];
- while ($this->dbResult->next_record()) {
- $this->result[] = $this->dbResult->Record;
- }
- unset($this->dbResult);
- $this->pos = false;
-
- return true;
- }
-
- return false;
- }
-
- public function nextRow()
- {
- if (!$this->numRows)
- $this->halt("No snapshot available or empty result!");
- if ($this->pos === false) {
- $this->pos = 0;
-
- return true;
- }
- if (++$this->pos < $this->numRows)
- return true;
- else
- return false;
- }
-
- public function resetPos()
- {
- $this->pos = false;
- }
-
- public function isField($name)
- {
- for ($i = 0; $i < $this->numFields; ++$i) {
- if ($name == $this->metaData[$i]['name']) {
- return true;
- }
- }
-
- return false;
- }
-
- public function getRow($row = false)
- {
- if (!$row === false AND !$this->result[$row])
- $this->halt("Snapshot has only " . ($this->numRows - 1) . " rows!");
-
- return ($row === false) ? $this->result[$this->pos] : $this->result[$row];
- }
-
- public function getFieldList()
- {
- if (!$this->numRows)
- $this->halt("No snapshot available or empty result!");
- $ret = [];
- for ($i = 0; $i < $this->numFields; ++$i) {
- $ret[] = $this->metaData[$i]['name'];
- }
-
- return $ret;
- }
-
- public function getField($field = 0)
- {
- if (!$this->numRows)
- $this->halt("No snapshot available or empty result!");
-
- return ($this->pos === false) ? false : $this->result[$this->pos][$field];
- }
-
- public function getRows($fieldname = 0)
- {
- if (!$this->numRows)
- $this->halt("No snapshot available or empty result!");
- $ret = [];
- for ($i = 0; $i < $this->numRows; ++$i) {
- $ret[] = $this->result[$i][$fieldname];
- }
-
- return $ret;
- }
-
- public function getDistinctRows($fieldname)
- {
- if (!$this->isField($fieldname))
- $this->halt("Field: $fieldname not found in result set!");
- $ret = [];
- for ($i = 0; $i < $this->numRows; ++$i) {
- $ret[$this->result[$i][$fieldname]] = $this->result[$i];
- $ret[$this->result[$i][$fieldname]]['row'] = $i;
- }
-
- return $ret;
- }
-
- public function sortRows($fieldname = 0, $order = "ASC", $stype = false)
- {
- if (!$this->numRows)
- $this->halt("No snapshot available or empty result!");
- $sortfields = $this->getRows($fieldname);
- if ($stype !== false) {
- $sortfunc = ($order == "ASC") ? "asort" : "arsort";
- $sortfunc($sortfields, $stype);
- } else {
- uasort($sortfields, function ($a,$b) {
- $a = mb_strtolower($a);
- $a = str_replace('ä', 'ae', $a);
- $a = str_replace('ö', 'oe', $a);
- $a = str_replace('ü', 'ue', $a);
-
- $b = mb_strtolower($b);
- $b = str_replace('ä', 'ae', $b);
- $b = str_replace('ö', 'oe', $b);
- $b = str_replace('ü', 'ue', $b);
-
- return strnatcasecmp($a, $b);
- });
- if ($order == "DESC") {
- $sortfields = array_reverse($sortfields, true);
- }
- }
- $sortresult = [];
- foreach ($sortfields as $key => $value) {
- $sortresult[] = $this->result[$key];
- }
- $this->result = $sortresult;
- $this->resetPos();
-
- return true;
- }
-
- public function searchFields($fieldname, $searchstr)
- {
- if (!$this->numRows)
- $this->halt("No snapshot available or empty result!");
- $ret = false;
- $sortfields = $this->getRows($fieldname);
- foreach ($sortfields as $key => $value) {
- if (preg_match($searchstr, $value)) {
- $ret = true;
- $this->pos = $key;
- break;
- }
- }
-
- return $ret;
- }
-
- public function getGroupedResult($group_by_field, $fields_to_group = null)
- {
- if (!$this->numRows)
- $this->halt("No snapshot available or empty result!");
- $fieldlist = $this->getFieldList();
- if (!in_array($group_by_field, $fieldlist))
- $this->halt("group_by_field not found in result set!");
- if (is_array($fields_to_group))
- $fieldlist = $fields_to_group;
- $num_fields = count($fieldlist);
- $ret = [];
- for ($i = 0; $i < $this->numRows; ++$i) {
- for ($j = 0; $j < $num_fields; ++$j) {
- if ($fieldlist[$j] != $group_by_field) {
- if (empty($ret[$this->result[$i][$group_by_field]][$fieldlist[$j]][$this->result[$i][$fieldlist[$j]]])) {
- $ret[$this->result[$i][$group_by_field]][$fieldlist[$j]][$this->result[$i][$fieldlist[$j]]] = 0;
- }
- ++$ret[$this->result[$i][$group_by_field]][$fieldlist[$j]][$this->result[$i][$fieldlist[$j]]];
- }
- }
- }
-
- return $ret;
- }
-
- public function mergeSnapshot($m_snap, $key_field = false, $mode = "ADD")
- {
- if ($mode == "ADD") {
- for ($i = 0; $i < $m_snap->numRows; ++$i) {
- $this->result[] = $m_snap->result[$i];
- }
- } elseif ($mode == "AND") {
- if (!$this->numRows || !$m_snap->numRows) {
- $this->result = [];
- } elseif ($m_snap->numRows) {
- $m_result = $m_snap->getDistinctRows($key_field);
- for ($i = 0; $i < $this->numRows; ++$i) {
- if (!($m_result[$this->result[$i][$key_field]] && $this->result[$i][$key_field])) {
- unset($this->result[$i]);
- }
- }
- }
- } elseif ($mode == "OR") {
- if (!$this->numRows) {
- $this->result = $m_snap->result;
- } elseif ($m_snap->numRows) {
- $result = $this->getDistinctRows($key_field);
- for ($i = 0; $i < $m_snap->numRows; ++$i) {
- if (empty($result[$m_snap->result[$i][$key_field]])) {
- $this->result[] = $m_snap->result[$i];
- }
- }
- }
- }
- $this->result = array_merge([], (array)$this->result);
- $this->numRows = count($this->result);
- $this->resetPos();
-
- return $this->numRows;
- }
-
- /**
- * print error message and exit script
- *
- * @access private
- *
- * @param string $msg the message to print
- */
- public function halt($msg)
- {
- echo "$msg";
- if ($this->debug) {
- echo "
";
- print_r($this);
- echo "
";
- die;
- }
-
- }
-}
-
-?>
diff --git a/lib/classes/DbSnapshot.php b/lib/classes/DbSnapshot.php
new file mode 100644
index 0000000..dfb4f89
--- /dev/null
+++ b/lib/classes/DbSnapshot.php
@@ -0,0 +1,370 @@
+
+// +---------------------------------------------------------------------------+
+// 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 any later version.
+// +---------------------------------------------------------------------------+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// +---------------------------------------------------------------------------+
+
+
+/**
+ * Class to provide snapshots of mysql result sets
+ *
+ * Uses DB abstraction layer of PHPLib
+ *
+ * @access public
+ * @author André Noack
+ * @package DBTools
+ **/
+class DbSnapshot
+{
+
+ /**
+ * the used db abstraction class
+ *
+ *
+ * @access private
+ * @var string $DbClass
+ */
+ var $DbClass = "DB_Sql";
+ /**
+ * the used db result set
+ *
+ *
+ * @access private
+ * @var object DB_Sql $dbResult
+ */
+ var $dbResult = null;
+ /**
+ * array to store the result set
+ *
+ *
+ * @access private
+ * @var array $result
+ */
+ var $result = [];
+ /**
+ * array to store metadata oh the result set
+ *
+ *
+ * @access private
+ * @var array $metaData
+ */
+ var $metaData = [];
+ /**
+ * the number of fields in the result set
+ *
+ *
+ * @access public
+ * @var integer $numFields
+ */
+ var $numFields = 0;
+ /**
+ * the number of rows in the result set
+ *
+ *
+ * @access public
+ * @var integer $numRows
+ */
+ var $numRows = 0;
+ /**
+ * the internal row pointer
+ *
+ *
+ * @access private
+ * @var mixed $pos
+ */
+ var $pos = false;
+ /**
+ * turn on/off debugging
+ *
+ *
+ * @access public
+ * @var boolean $debug
+ */
+ var $debug = false;
+
+ /**
+ * Constructor
+ *
+ * Pass instance of DbClass or nothing to specify result set later
+ *
+ * @access public
+ *
+ * @param object DB_Sql $dbresult
+ */
+ public function __construct($dbresult = null)
+ {
+ if (is_object($dbresult)) {
+ $this->dbResult = $dbresult;
+ $this->getSnapshot();
+ }
+
+ }
+
+ function isDbResult()
+ {
+ if (!is_subclass_of($this->dbResult, $this->DbClass))
+ $this->halt("Result set has wrong type!");
+ if (!$this->dbResult->query_id())
+ $this->halt("No result set (missing query?)");
+
+ return true;
+ }
+
+ public function getSnapshot()
+ {
+ if ($this->isDbResult()) {
+ $this->numFields = $this->dbResult->num_fields();
+ $this->numRows = $this->dbResult->num_rows();
+ $this->metaData = $this->dbResult->metadata();
+ $this->result = [];
+ while ($this->dbResult->next_record()) {
+ $this->result[] = $this->dbResult->Record;
+ }
+ unset($this->dbResult);
+ $this->pos = false;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public function nextRow()
+ {
+ if (!$this->numRows)
+ $this->halt("No snapshot available or empty result!");
+ if ($this->pos === false) {
+ $this->pos = 0;
+
+ return true;
+ }
+ if (++$this->pos < $this->numRows)
+ return true;
+ else
+ return false;
+ }
+
+ public function resetPos()
+ {
+ $this->pos = false;
+ }
+
+ public function isField($name)
+ {
+ for ($i = 0; $i < $this->numFields; ++$i) {
+ if ($name == $this->metaData[$i]['name']) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function getRow($row = false)
+ {
+ if (!$row === false AND !$this->result[$row])
+ $this->halt("Snapshot has only " . ($this->numRows - 1) . " rows!");
+
+ return ($row === false) ? $this->result[$this->pos] : $this->result[$row];
+ }
+
+ public function getFieldList()
+ {
+ if (!$this->numRows)
+ $this->halt("No snapshot available or empty result!");
+ $ret = [];
+ for ($i = 0; $i < $this->numFields; ++$i) {
+ $ret[] = $this->metaData[$i]['name'];
+ }
+
+ return $ret;
+ }
+
+ public function getField($field = 0)
+ {
+ if (!$this->numRows)
+ $this->halt("No snapshot available or empty result!");
+
+ return ($this->pos === false) ? false : $this->result[$this->pos][$field];
+ }
+
+ public function getRows($fieldname = 0)
+ {
+ if (!$this->numRows)
+ $this->halt("No snapshot available or empty result!");
+ $ret = [];
+ for ($i = 0; $i < $this->numRows; ++$i) {
+ $ret[] = $this->result[$i][$fieldname];
+ }
+
+ return $ret;
+ }
+
+ public function getDistinctRows($fieldname)
+ {
+ if (!$this->isField($fieldname))
+ $this->halt("Field: $fieldname not found in result set!");
+ $ret = [];
+ for ($i = 0; $i < $this->numRows; ++$i) {
+ $ret[$this->result[$i][$fieldname]] = $this->result[$i];
+ $ret[$this->result[$i][$fieldname]]['row'] = $i;
+ }
+
+ return $ret;
+ }
+
+ public function sortRows($fieldname = 0, $order = "ASC", $stype = false)
+ {
+ if (!$this->numRows)
+ $this->halt("No snapshot available or empty result!");
+ $sortfields = $this->getRows($fieldname);
+ if ($stype !== false) {
+ $sortfunc = ($order == "ASC") ? "asort" : "arsort";
+ $sortfunc($sortfields, $stype);
+ } else {
+ uasort($sortfields, function ($a,$b) {
+ $a = mb_strtolower($a);
+ $a = str_replace('ä', 'ae', $a);
+ $a = str_replace('ö', 'oe', $a);
+ $a = str_replace('ü', 'ue', $a);
+
+ $b = mb_strtolower($b);
+ $b = str_replace('ä', 'ae', $b);
+ $b = str_replace('ö', 'oe', $b);
+ $b = str_replace('ü', 'ue', $b);
+
+ return strnatcasecmp($a, $b);
+ });
+ if ($order == "DESC") {
+ $sortfields = array_reverse($sortfields, true);
+ }
+ }
+ $sortresult = [];
+ foreach ($sortfields as $key => $value) {
+ $sortresult[] = $this->result[$key];
+ }
+ $this->result = $sortresult;
+ $this->resetPos();
+
+ return true;
+ }
+
+ public function searchFields($fieldname, $searchstr)
+ {
+ if (!$this->numRows)
+ $this->halt("No snapshot available or empty result!");
+ $ret = false;
+ $sortfields = $this->getRows($fieldname);
+ foreach ($sortfields as $key => $value) {
+ if (preg_match($searchstr, $value)) {
+ $ret = true;
+ $this->pos = $key;
+ break;
+ }
+ }
+
+ return $ret;
+ }
+
+ public function getGroupedResult($group_by_field, $fields_to_group = null)
+ {
+ if (!$this->numRows)
+ $this->halt("No snapshot available or empty result!");
+ $fieldlist = $this->getFieldList();
+ if (!in_array($group_by_field, $fieldlist))
+ $this->halt("group_by_field not found in result set!");
+ if (is_array($fields_to_group))
+ $fieldlist = $fields_to_group;
+ $num_fields = count($fieldlist);
+ $ret = [];
+ for ($i = 0; $i < $this->numRows; ++$i) {
+ for ($j = 0; $j < $num_fields; ++$j) {
+ if ($fieldlist[$j] != $group_by_field) {
+ if (empty($ret[$this->result[$i][$group_by_field]][$fieldlist[$j]][$this->result[$i][$fieldlist[$j]]])) {
+ $ret[$this->result[$i][$group_by_field]][$fieldlist[$j]][$this->result[$i][$fieldlist[$j]]] = 0;
+ }
+ ++$ret[$this->result[$i][$group_by_field]][$fieldlist[$j]][$this->result[$i][$fieldlist[$j]]];
+ }
+ }
+ }
+
+ return $ret;
+ }
+
+ public function mergeSnapshot($m_snap, $key_field = false, $mode = "ADD")
+ {
+ if ($mode == "ADD") {
+ for ($i = 0; $i < $m_snap->numRows; ++$i) {
+ $this->result[] = $m_snap->result[$i];
+ }
+ } elseif ($mode == "AND") {
+ if (!$this->numRows || !$m_snap->numRows) {
+ $this->result = [];
+ } elseif ($m_snap->numRows) {
+ $m_result = $m_snap->getDistinctRows($key_field);
+ for ($i = 0; $i < $this->numRows; ++$i) {
+ if (!($m_result[$this->result[$i][$key_field]] && $this->result[$i][$key_field])) {
+ unset($this->result[$i]);
+ }
+ }
+ }
+ } elseif ($mode == "OR") {
+ if (!$this->numRows) {
+ $this->result = $m_snap->result;
+ } elseif ($m_snap->numRows) {
+ $result = $this->getDistinctRows($key_field);
+ for ($i = 0; $i < $m_snap->numRows; ++$i) {
+ if (empty($result[$m_snap->result[$i][$key_field]])) {
+ $this->result[] = $m_snap->result[$i];
+ }
+ }
+ }
+ }
+ $this->result = array_merge([], (array)$this->result);
+ $this->numRows = count($this->result);
+ $this->resetPos();
+
+ return $this->numRows;
+ }
+
+ /**
+ * print error message and exit script
+ *
+ * @access private
+ *
+ * @param string $msg the message to print
+ */
+ public function halt($msg)
+ {
+ echo "$msg";
+ if ($this->debug) {
+ echo "
";
+ print_r($this);
+ echo "
";
+ die;
+ }
+
+ }
+}
+
+?>
diff --git a/lib/classes/DbView.class.php b/lib/classes/DbView.class.php
deleted file mode 100644
index 81e9b91..0000000
--- a/lib/classes/DbView.class.php
+++ /dev/null
@@ -1,378 +0,0 @@
-
-// +---------------------------------------------------------------------------+
-// 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 any later version.
-// +---------------------------------------------------------------------------+
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// +---------------------------------------------------------------------------+
-
-
-/**
- * Class to provide simple Views and Prepared Statements
- *
- * Only tested with MySql, needs MySql >= 3.23
- * Uses DB abstraction layer of PHPLib
- *
- * @access public
- * @author André Noack
- * @package DBTools
- */
-class DbView
-{
- /**
- * the processed list of queries
- *
- *
- * @access private
- * @var array $query_list
- */
- private $query_list = [];
- /**
- * list of parameters
- *
- *
- * @access public
- * @var array $params
- */
- public $params = [];
-
- /**
- * Database Object
- *
- *
- * @access private
- * @var object $db
- */
- private $db;
- /**
- * Database Object Type
- *
- * Use your subclass of db_mysql here, or pass existing object to constuctor
- *
- * @access private
- * @var string $db_class_name
- * @see DbView()
- */
- private $db_class_name = "DB_Seminar";
- /**
- * Temp Table Type
- *
- * MyISAM is always safe, HEAP may provide better performance
- *
- * @access private
- * @var string $temp_table_type
- */
- private $temp_table_type = "MyISAM";
- /**
- * Primary Key used in Temp Table
- *
- * If none is set in your view, an auto_increment row is used
- *
- * @access private
- * @var string $pk
- * @see get_temp_table()
- */
- private $pk = "";
- /**
- * delete the params array after each query execution
- *
- *
- * @access public
- * @var boolean $auto_free_params
- */
- public $auto_free_params = true;
- /**
- * turn on/off debugging
- *
- *
- * @access public
- * @var boolean $debug
- */
- public $debug = false;
-
- static protected $dbviewfiles = [];
-
- static protected $dbviews = [];
-
- public static function addView($view)
- {
- $view = mb_strtolower($view);
- if (!isset(self::$dbviewfiles[$view])) {
- self::$dbviewfiles[$view] = 0;
- }
- }
-
- /**
- * Convenience method that combines addView() and returns an instance.
- *
- * @param String $view Required view (at least this will be present in the
- * returned instance)
- * @param mixed $db classname of db abstraction or existing db object
- *
- * @return DbView Instance of self with at least the required view loaded
- */
- public static function getView($view, $db = '')
- {
- self::addView($view);
-
- return new self($db);
- }
-
- /**
- * Constructor
- *
- * Pass nothing to use a new instance of db_class_name, the classname for a new instance, or existing instance
- *
- * @access public
- *
- * @param mixed $db classname of used db abstraction or existing db object
- */
- public function __construct($db = "")
- {
- if (is_object($db)) {
- $this->db = $db;
- } else if ($db != "") {
- $this->db = new $db;
- $this->db_class_name = $db;
- } else {
- $this->db = new $this->db_class_name;
- }
- $this->init_views();
- }
-
- public function init_views()
- {
- foreach (self::$dbviewfiles as $view => $status) {
- if ($status === 0) {
- $views = include 'lib/dbviews/' . $view . '.view.php';
- self::$dbviews += $views;
-
- self::$dbviewfiles[$view] = 1;
- }
- }
- }
-
- public function __get($view)
- {
- if (isset(self::$dbviews[$view])) {
- return self::$dbviews[$view];
- } else {
- return null;
- }
- }
-
- /**
- * print error message and exit script
- *
- * @access private
- *
- * @param string $msg the message to print
- */
- public function halt($msg)
- {
- echo "$msg";
- if ($this->debug) {
- echo "
";
- print_r($this);
- echo "
";
- }
- die;
- }
-
- public function get_query()
- {
- $parsed_query = $this->get_parsed_query(func_get_args());
- $this->db->query($parsed_query);
- return $this->db;
- }
-
- public function get_parsed_query($query_list)
- {
- $parsed_query = "";
- $this->query_list = [];
- (is_array($query_list)) ? $this->query_list = $query_list : $this->query_list[] = $query_list;
- if (count($this->query_list) == 1) {
- $spl = explode(":", $this->query_list[0]);
- if ($spl[0] == "view") {
- $this->query_list = $this->get_view(trim($spl[1]));
- }
- }
- $this->parse_query($this->query_list);
- if (is_array($this->query_list)) {
- $parsed_query = $this->query_list[0];
- } else {
- $parsed_query = $this->query_list;
- }
-
- return $parsed_query;
- }
-
-
- public function parse_query(&$query)
- {
- if (is_array($query)) {
- for ($i = (count($query) - 1); $i > 0; --$i) {
- $spl = explode(":", $query[$i]);
- if ($spl[0] == "view") {
- $query[$i] = $this->get_view(trim($spl[1]), $spl[2]);
- }
- $query[$i] = $this->parse_query($query[$i]);
- $repl_query = (is_array($query[$i])) ? $query[$i][0] : $query[$i];
- for ($j = 0; $j < $i; ++$j) {
- $spl = mb_stristr($query[$j], "where");
- if (!$spl)
- $spl = mb_stristr($query[$j], "having");
- if ($spl) {
- $pos = mb_strpos($spl, "{" . $i . "}");
- if (!$pos === false)
- $repl_query = $this->get_temp_values($repl_query);
- }
- if (!$spl OR $pos === false) {
- $pos = mb_strpos($query[$j], "{" . $i . "}");
- if (!$pos === false)
- $repl_query = $this->get_temp_table($repl_query);
- }
- $query[$j] = str_replace("{" . $i . "}", $repl_query, $query[$j]);
- }
- }
- }
-
- return $query;
- }
-
-
- public function get_temp_table($sub_query)
- {
- $id = self::get_uniqid();
- $pk = $this->pk ? "PRIMARY KEY($this->pk)" : "auto_" . $id . " INT NOT NULL AUTO_INCREMENT PRIMARY KEY";
- $query = "CREATE TEMPORARY TABLE temp_$id ($pk) ENGINE=$this->temp_table_type $sub_query";
- $this->db->query($query);
-
- return " temp_" . $id . " ";
- }
-
-
- public function get_temp_values($sub_query)
- {
- $this->db->query($sub_query);
- if (!$this->db->num_rows())
- $this->halt("Sub Query: $sub_query returns nothing!");
- else {
- while ($this->db->next_record()) {
- $result[] = $this->db->Record[0];
- }
- $value_list = $this->get_value_list($result);
- }
-
- return $value_list;
- }
-
- public static function get_uniqid()
- {
- mt_srand((double)microtime() * 1000000);
-
- return md5(uniqid(mt_rand(), 1));
- }
-
- public function get_value_list($list)
- {
- $value_list = false;
- if (count($list) == 1)
- $value_list = "'$list[0]'";
- else
- $value_list = "'" . join("','", $list) . "'";
-
- return $value_list;
- }
-
- public function get_view($name)
- {
- if (!empty(self::$dbviews[$name]["pk"])) {
- $this->pk = self::$dbviews[$name]["pk"];
- }
- if (!empty(self::$dbviews[$name]["temp_table_type"])) {
- $this->temp_table_type = self::$dbviews[$name]["temp_table_type"];
- }
- if (!$query_list = self::$dbviews[$name]["query"])
- $this->halt("View not found: $name");
- (is_array($query_list)) ? $query = $query_list[0] : $query = $query_list;
- $tokens = preg_split("/[\?§\&]/u", $query);
- if (count($tokens) > 1) {
- $types = [];
- $token = 0;
- foreach (preg_split('//u', $query, null, PREG_SPLIT_NO_EMPTY) as $i => $c) {
- switch ($c) {
- case '?':
- $types[$token++] = 1;
- break;
- case '§':
- $types[$token++] = 2;
- break;
- case '&':
- $types[$token++] = 3;
- break;
- }
- }
- if (count($this->params) != count($types))
- $this->halt("Wrong parameter count in view: $name");
- $query = "";
- for ($i = 0; $i < count($this->params); ++$i) {
- $query .= $tokens[$i];
- if (is_null($this->params[$i])) {
- $query .= 'NULL';
- } else {
- switch ($types[$i]) {
- case 1:
- $query .= "'" . $this->params[$i] . "'";
- break;
- case 2:
- $query .= $this->params[$i];
- break;
- case 3:
- $query .= (is_array($this->params[$i])) ? "'" . join("','", $this->params[$i]) . "'" : "'" . $this->params[$i] . "'";
- break;
- }
- }
- }
- $query .= $tokens[$i];
- if ($this->auto_free_params)
- $this->params = [];
- }
- (is_array($query_list)) ? $query_list[0] = $query : $query_list = $query;
- return $query_list;
- }
-
- public function Get_union()
- {
- $queries = func_get_args();
- $view = new DbView();
- $union_table = $view->get_temp_table($view->get_parsed_query($queries[0]));
- if ($queries[1]) {
- for ($i = 1; $i < count($queries); ++$i) {
- $view->db->query("REPLACE INTO $union_table " . $view->get_parsed_query($queries[$i]));
- }
- }
-
- return $union_table;
- }
-}
-
-?>
diff --git a/lib/classes/DbView.php b/lib/classes/DbView.php
new file mode 100644
index 0000000..81e9b91
--- /dev/null
+++ b/lib/classes/DbView.php
@@ -0,0 +1,378 @@
+
+// +---------------------------------------------------------------------------+
+// 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 any later version.
+// +---------------------------------------------------------------------------+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// +---------------------------------------------------------------------------+
+
+
+/**
+ * Class to provide simple Views and Prepared Statements
+ *
+ * Only tested with MySql, needs MySql >= 3.23
+ * Uses DB abstraction layer of PHPLib
+ *
+ * @access public
+ * @author André Noack
+ * @package DBTools
+ */
+class DbView
+{
+ /**
+ * the processed list of queries
+ *
+ *
+ * @access private
+ * @var array $query_list
+ */
+ private $query_list = [];
+ /**
+ * list of parameters
+ *
+ *
+ * @access public
+ * @var array $params
+ */
+ public $params = [];
+
+ /**
+ * Database Object
+ *
+ *
+ * @access private
+ * @var object $db
+ */
+ private $db;
+ /**
+ * Database Object Type
+ *
+ * Use your subclass of db_mysql here, or pass existing object to constuctor
+ *
+ * @access private
+ * @var string $db_class_name
+ * @see DbView()
+ */
+ private $db_class_name = "DB_Seminar";
+ /**
+ * Temp Table Type
+ *
+ * MyISAM is always safe, HEAP may provide better performance
+ *
+ * @access private
+ * @var string $temp_table_type
+ */
+ private $temp_table_type = "MyISAM";
+ /**
+ * Primary Key used in Temp Table
+ *
+ * If none is set in your view, an auto_increment row is used
+ *
+ * @access private
+ * @var string $pk
+ * @see get_temp_table()
+ */
+ private $pk = "";
+ /**
+ * delete the params array after each query execution
+ *
+ *
+ * @access public
+ * @var boolean $auto_free_params
+ */
+ public $auto_free_params = true;
+ /**
+ * turn on/off debugging
+ *
+ *
+ * @access public
+ * @var boolean $debug
+ */
+ public $debug = false;
+
+ static protected $dbviewfiles = [];
+
+ static protected $dbviews = [];
+
+ public static function addView($view)
+ {
+ $view = mb_strtolower($view);
+ if (!isset(self::$dbviewfiles[$view])) {
+ self::$dbviewfiles[$view] = 0;
+ }
+ }
+
+ /**
+ * Convenience method that combines addView() and returns an instance.
+ *
+ * @param String $view Required view (at least this will be present in the
+ * returned instance)
+ * @param mixed $db classname of db abstraction or existing db object
+ *
+ * @return DbView Instance of self with at least the required view loaded
+ */
+ public static function getView($view, $db = '')
+ {
+ self::addView($view);
+
+ return new self($db);
+ }
+
+ /**
+ * Constructor
+ *
+ * Pass nothing to use a new instance of db_class_name, the classname for a new instance, or existing instance
+ *
+ * @access public
+ *
+ * @param mixed $db classname of used db abstraction or existing db object
+ */
+ public function __construct($db = "")
+ {
+ if (is_object($db)) {
+ $this->db = $db;
+ } else if ($db != "") {
+ $this->db = new $db;
+ $this->db_class_name = $db;
+ } else {
+ $this->db = new $this->db_class_name;
+ }
+ $this->init_views();
+ }
+
+ public function init_views()
+ {
+ foreach (self::$dbviewfiles as $view => $status) {
+ if ($status === 0) {
+ $views = include 'lib/dbviews/' . $view . '.view.php';
+ self::$dbviews += $views;
+
+ self::$dbviewfiles[$view] = 1;
+ }
+ }
+ }
+
+ public function __get($view)
+ {
+ if (isset(self::$dbviews[$view])) {
+ return self::$dbviews[$view];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * print error message and exit script
+ *
+ * @access private
+ *
+ * @param string $msg the message to print
+ */
+ public function halt($msg)
+ {
+ echo "$msg";
+ if ($this->debug) {
+ echo "
";
+ print_r($this);
+ echo "
";
+ }
+ die;
+ }
+
+ public function get_query()
+ {
+ $parsed_query = $this->get_parsed_query(func_get_args());
+ $this->db->query($parsed_query);
+ return $this->db;
+ }
+
+ public function get_parsed_query($query_list)
+ {
+ $parsed_query = "";
+ $this->query_list = [];
+ (is_array($query_list)) ? $this->query_list = $query_list : $this->query_list[] = $query_list;
+ if (count($this->query_list) == 1) {
+ $spl = explode(":", $this->query_list[0]);
+ if ($spl[0] == "view") {
+ $this->query_list = $this->get_view(trim($spl[1]));
+ }
+ }
+ $this->parse_query($this->query_list);
+ if (is_array($this->query_list)) {
+ $parsed_query = $this->query_list[0];
+ } else {
+ $parsed_query = $this->query_list;
+ }
+
+ return $parsed_query;
+ }
+
+
+ public function parse_query(&$query)
+ {
+ if (is_array($query)) {
+ for ($i = (count($query) - 1); $i > 0; --$i) {
+ $spl = explode(":", $query[$i]);
+ if ($spl[0] == "view") {
+ $query[$i] = $this->get_view(trim($spl[1]), $spl[2]);
+ }
+ $query[$i] = $this->parse_query($query[$i]);
+ $repl_query = (is_array($query[$i])) ? $query[$i][0] : $query[$i];
+ for ($j = 0; $j < $i; ++$j) {
+ $spl = mb_stristr($query[$j], "where");
+ if (!$spl)
+ $spl = mb_stristr($query[$j], "having");
+ if ($spl) {
+ $pos = mb_strpos($spl, "{" . $i . "}");
+ if (!$pos === false)
+ $repl_query = $this->get_temp_values($repl_query);
+ }
+ if (!$spl OR $pos === false) {
+ $pos = mb_strpos($query[$j], "{" . $i . "}");
+ if (!$pos === false)
+ $repl_query = $this->get_temp_table($repl_query);
+ }
+ $query[$j] = str_replace("{" . $i . "}", $repl_query, $query[$j]);
+ }
+ }
+ }
+
+ return $query;
+ }
+
+
+ public function get_temp_table($sub_query)
+ {
+ $id = self::get_uniqid();
+ $pk = $this->pk ? "PRIMARY KEY($this->pk)" : "auto_" . $id . " INT NOT NULL AUTO_INCREMENT PRIMARY KEY";
+ $query = "CREATE TEMPORARY TABLE temp_$id ($pk) ENGINE=$this->temp_table_type $sub_query";
+ $this->db->query($query);
+
+ return " temp_" . $id . " ";
+ }
+
+
+ public function get_temp_values($sub_query)
+ {
+ $this->db->query($sub_query);
+ if (!$this->db->num_rows())
+ $this->halt("Sub Query: $sub_query returns nothing!");
+ else {
+ while ($this->db->next_record()) {
+ $result[] = $this->db->Record[0];
+ }
+ $value_list = $this->get_value_list($result);
+ }
+
+ return $value_list;
+ }
+
+ public static function get_uniqid()
+ {
+ mt_srand((double)microtime() * 1000000);
+
+ return md5(uniqid(mt_rand(), 1));
+ }
+
+ public function get_value_list($list)
+ {
+ $value_list = false;
+ if (count($list) == 1)
+ $value_list = "'$list[0]'";
+ else
+ $value_list = "'" . join("','", $list) . "'";
+
+ return $value_list;
+ }
+
+ public function get_view($name)
+ {
+ if (!empty(self::$dbviews[$name]["pk"])) {
+ $this->pk = self::$dbviews[$name]["pk"];
+ }
+ if (!empty(self::$dbviews[$name]["temp_table_type"])) {
+ $this->temp_table_type = self::$dbviews[$name]["temp_table_type"];
+ }
+ if (!$query_list = self::$dbviews[$name]["query"])
+ $this->halt("View not found: $name");
+ (is_array($query_list)) ? $query = $query_list[0] : $query = $query_list;
+ $tokens = preg_split("/[\?§\&]/u", $query);
+ if (count($tokens) > 1) {
+ $types = [];
+ $token = 0;
+ foreach (preg_split('//u', $query, null, PREG_SPLIT_NO_EMPTY) as $i => $c) {
+ switch ($c) {
+ case '?':
+ $types[$token++] = 1;
+ break;
+ case '§':
+ $types[$token++] = 2;
+ break;
+ case '&':
+ $types[$token++] = 3;
+ break;
+ }
+ }
+ if (count($this->params) != count($types))
+ $this->halt("Wrong parameter count in view: $name");
+ $query = "";
+ for ($i = 0; $i < count($this->params); ++$i) {
+ $query .= $tokens[$i];
+ if (is_null($this->params[$i])) {
+ $query .= 'NULL';
+ } else {
+ switch ($types[$i]) {
+ case 1:
+ $query .= "'" . $this->params[$i] . "'";
+ break;
+ case 2:
+ $query .= $this->params[$i];
+ break;
+ case 3:
+ $query .= (is_array($this->params[$i])) ? "'" . join("','", $this->params[$i]) . "'" : "'" . $this->params[$i] . "'";
+ break;
+ }
+ }
+ }
+ $query .= $tokens[$i];
+ if ($this->auto_free_params)
+ $this->params = [];
+ }
+ (is_array($query_list)) ? $query_list[0] = $query : $query_list = $query;
+ return $query_list;
+ }
+
+ public function Get_union()
+ {
+ $queries = func_get_args();
+ $view = new DbView();
+ $union_table = $view->get_temp_table($view->get_parsed_query($queries[0]));
+ if ($queries[1]) {
+ for ($i = 1; $i < count($queries); ++$i) {
+ $view->db->query("REPLACE INTO $union_table " . $view->get_parsed_query($queries[$i]));
+ }
+ }
+
+ return $union_table;
+ }
+}
+
+?>
diff --git a/lib/classes/Event.interface.php b/lib/classes/Event.interface.php
deleted file mode 100644
index 23f092b..0000000
--- a/lib/classes/Event.interface.php
+++ /dev/null
@@ -1,184 +0,0 @@
-
- */
-class Feedback
-{
- /**
- * Returns the html code for feedback elements for a given range, if the module is activated within a course
- *
- * @return string
- */
- public static function getHTML(string $range_id, string $range_type)
- {
- if (!$range_id) {
- return null;
- }
- $course_id = null;
- if (is_subclass_of($range_type, \FeedbackRange::class)) {
- $range_object = $range_type::find($range_id);
- if ($range_object) {
- $course_id = $range_object->getRangeCourseId();
- }
- }
- if ($course_id && Feedback::isActivated($course_id) && Feedback::hasRangeAccess($range_id, $range_type)) {
- return '';
- } else {
- return null;
- }
- }
- /**
- * Returns activation status of the feedback module in currently active course
- *
- * @param string $course_id optional; use this course_id instead of the current context
- *
- * @return boolean
- */
- public static function isActivated(string $course_id = null): bool
- {
- $course_id = $course_id ?? Context::getId();
- $plugin_manager = PluginManager::getInstance();
- $feedback_module = $plugin_manager->getPluginInfo('FeedbackModule');
-
- return $plugin_manager->isPluginActivated($feedback_module['id'], $course_id) ?? false;
- }
-
- /**
- * Returns admin permission of current user within given course
- *
- * @param string $course_id the course
- * @param string $user_id optional; use this ID instead of $GLOBALS['user']->id
- *
- * @return boolean
- *
- * @SuppressWarnings(PHPMD.Superglobals)
- */
- public static function hasAdminPerm($course_id, string $user_id = null): bool
- {
- $user_id = $user_id ?? $GLOBALS['user']->id;
- $admin_perm_level = CourseConfig::get($course_id)->FEEDBACK_ADMIN_PERM;
- $admin_perm = $GLOBALS['perm']->have_studip_perm($admin_perm_level, $course_id, $user_id);
-
- return $admin_perm;
- }
-
- /**
- * Returns create permission of current user within given course
- *
- * @param string $course_id the course
- * @param string $user_id optional; use this ID instead of $GLOBALS['user']->id
- *
- * @return boolean
- *
- * @SuppressWarnings(PHPMD.Superglobals)
- */
- public static function hasCreatePerm($course_id, string $user_id = null): bool
- {
- $user_id = $user_id ?? $GLOBALS['user']->id;
- $create_perm_level = CourseConfig::get($course_id)->FEEDBACK_CREATE_PERM;
- $create_perm = $GLOBALS['perm']->have_studip_perm($create_perm_level, $course_id, $user_id);
-
- return $create_perm;
- }
-
- /**
- * Returns range access permission of current user for given range
- *
- * @param string $range_id
- * @param string $range_type
- * @param string $user_id optional; use this ID instead of $GLOBALS['user']->id
- *
- * @return boolean
- */
- public static function hasRangeAccess($range_id, $range_type, string $user_id = null): bool
- {
- $user_id = $user_id ?? $GLOBALS['user']->id;
- $range = $range_type::find($range_id);
- return $range->isRangeAccessible($user_id);
- }
-
-}
diff --git a/lib/classes/Feedback.php b/lib/classes/Feedback.php
new file mode 100644
index 0000000..26dbf55
--- /dev/null
+++ b/lib/classes/Feedback.php
@@ -0,0 +1,101 @@
+
+ */
+class Feedback
+{
+ /**
+ * Returns the html code for feedback elements for a given range, if the module is activated within a course
+ *
+ * @return string
+ */
+ public static function getHTML(string $range_id, string $range_type)
+ {
+ if (!$range_id) {
+ return null;
+ }
+ $course_id = null;
+ if (is_subclass_of($range_type, \FeedbackRange::class)) {
+ $range_object = $range_type::find($range_id);
+ if ($range_object) {
+ $course_id = $range_object->getRangeCourseId();
+ }
+ }
+ if ($course_id && Feedback::isActivated($course_id) && Feedback::hasRangeAccess($range_id, $range_type)) {
+ return '';
+ } else {
+ return null;
+ }
+ }
+ /**
+ * Returns activation status of the feedback module in currently active course
+ *
+ * @param string $course_id optional; use this course_id instead of the current context
+ *
+ * @return boolean
+ */
+ public static function isActivated(string $course_id = null): bool
+ {
+ $course_id = $course_id ?? Context::getId();
+ $plugin_manager = PluginManager::getInstance();
+ $feedback_module = $plugin_manager->getPluginInfo('FeedbackModule');
+
+ return $plugin_manager->isPluginActivated($feedback_module['id'], $course_id) ?? false;
+ }
+
+ /**
+ * Returns admin permission of current user within given course
+ *
+ * @param string $course_id the course
+ * @param string $user_id optional; use this ID instead of $GLOBALS['user']->id
+ *
+ * @return boolean
+ *
+ * @SuppressWarnings(PHPMD.Superglobals)
+ */
+ public static function hasAdminPerm($course_id, string $user_id = null): bool
+ {
+ $user_id = $user_id ?? $GLOBALS['user']->id;
+ $admin_perm_level = CourseConfig::get($course_id)->FEEDBACK_ADMIN_PERM;
+ $admin_perm = $GLOBALS['perm']->have_studip_perm($admin_perm_level, $course_id, $user_id);
+
+ return $admin_perm;
+ }
+
+ /**
+ * Returns create permission of current user within given course
+ *
+ * @param string $course_id the course
+ * @param string $user_id optional; use this ID instead of $GLOBALS['user']->id
+ *
+ * @return boolean
+ *
+ * @SuppressWarnings(PHPMD.Superglobals)
+ */
+ public static function hasCreatePerm($course_id, string $user_id = null): bool
+ {
+ $user_id = $user_id ?? $GLOBALS['user']->id;
+ $create_perm_level = CourseConfig::get($course_id)->FEEDBACK_CREATE_PERM;
+ $create_perm = $GLOBALS['perm']->have_studip_perm($create_perm_level, $course_id, $user_id);
+
+ return $create_perm;
+ }
+
+ /**
+ * Returns range access permission of current user for given range
+ *
+ * @param string $range_id
+ * @param string $range_type
+ * @param string $user_id optional; use this ID instead of $GLOBALS['user']->id
+ *
+ * @return boolean
+ */
+ public static function hasRangeAccess($range_id, $range_type, string $user_id = null): bool
+ {
+ $user_id = $user_id ?? $GLOBALS['user']->id;
+ $range = $range_type::find($range_id);
+ return $range->isRangeAccessible($user_id);
+ }
+
+}
diff --git a/lib/classes/FeedbackRange.interface.php b/lib/classes/FeedbackRange.interface.php
deleted file mode 100644
index 863c197..0000000
--- a/lib/classes/FeedbackRange.interface.php
+++ /dev/null
@@ -1,55 +0,0 @@
-
- */
-
-interface FeedbackRange
-{
- /**
- * Returns the ID of this range.
- *
- * @return string|integer The ID of the range.
- */
- public function getId();
-
- /**
- * Returns a human-friendly representation of the FeedbackRange object instance's name.
- *
- * @return string A human-friendly name for the FeedbackRange object instance.
- */
- public function getRangeName();
-
- /**
- * Returns the icon object that shall be used with the FeedbackRange object instance.
- *
- * @param string $role role of icon
- * @return Icon icon for the FeedbackRange object instance.
- */
- public function getRangeIcon($role);
-
- /**
- * Returns the URL of FeedbackRange view, where the object instance is visible
- * together with the related feedback element(s).
- * @return string Path that is usable with the url_for and link_for methods.
- */
- public function getRangeUrl();
-
- /**
- * Returns the course id of FeedbackRange object instance
- * @return string course_id
- */
- public function getRangeCourseId();
-
- /**
- * Returns the accessebility of FeedbackRange object instance for current active user
- * @param string $user_id optional; use this ID instead of $GLOBALS['user']->id
- * @return bool range object accessebility
- */
- public function isRangeAccessible(string $user_id = null): bool;
-}
diff --git a/lib/classes/FeedbackRange.php b/lib/classes/FeedbackRange.php
new file mode 100644
index 0000000..863c197
--- /dev/null
+++ b/lib/classes/FeedbackRange.php
@@ -0,0 +1,55 @@
+
+ */
+
+interface FeedbackRange
+{
+ /**
+ * Returns the ID of this range.
+ *
+ * @return string|integer The ID of the range.
+ */
+ public function getId();
+
+ /**
+ * Returns a human-friendly representation of the FeedbackRange object instance's name.
+ *
+ * @return string A human-friendly name for the FeedbackRange object instance.
+ */
+ public function getRangeName();
+
+ /**
+ * Returns the icon object that shall be used with the FeedbackRange object instance.
+ *
+ * @param string $role role of icon
+ * @return Icon icon for the FeedbackRange object instance.
+ */
+ public function getRangeIcon($role);
+
+ /**
+ * Returns the URL of FeedbackRange view, where the object instance is visible
+ * together with the related feedback element(s).
+ * @return string Path that is usable with the url_for and link_for methods.
+ */
+ public function getRangeUrl();
+
+ /**
+ * Returns the course id of FeedbackRange object instance
+ * @return string course_id
+ */
+ public function getRangeCourseId();
+
+ /**
+ * Returns the accessebility of FeedbackRange object instance for current active user
+ * @param string $user_id optional; use this ID instead of $GLOBALS['user']->id
+ * @return bool range object accessebility
+ */
+ public function isRangeAccessible(string $user_id = null): bool;
+}
diff --git a/lib/classes/FileLock.class.php b/lib/classes/FileLock.class.php
deleted file mode 100644
index 064d41e..0000000
--- a/lib/classes/FileLock.class.php
+++ /dev/null
@@ -1,74 +0,0 @@
-
- * @copyright 2013 Stud.IP Core-Group
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @since 2.4
- */
-
-class FileLock
-{
- protected $file;
-
- /**
- * Constructs a new lock object with the provided id.
- *
- * @param String $id Identifier of the lock
- */
- public function __construct($id)
- {
- $this->file = fopen("{$GLOBALS['TMP_PATH']}/$id.json", 'c+');
-
- if (!$this->file) {
- throw new RuntimeException('failed to create lock file.');
- }
- }
-
- /**
- * Try to aquire a file lock. The provided lock information will
- * be stored with the lock. If the lock cannot be aquired, the
- * lock information in $data is updated from the lock file.
- *
- * @param array $data additional data to be stored with the lock
- * @return boolean true on success or false on failure
- */
- public function tryLock(&$data = [])
- {
- rewind($this->file);
-
- if (flock($this->file, LOCK_EX | LOCK_NB)) {
- ftruncate($this->file, 0);
- fwrite($this->file, json_encode($data));
- fflush($this->file);
-
- return true;
- } else {
- $json = stream_get_contents($this->file);
- $data = json_decode($json, true);
-
- return false;
- }
- }
-
- /**
- * Releases a previously obtained lock
- *
- * @return boolean true on success or false on failure
- */
- public function release()
- {
- return flock($this->file, LOCK_UN);
- }
-}
diff --git a/lib/classes/FileLock.php b/lib/classes/FileLock.php
new file mode 100644
index 0000000..064d41e
--- /dev/null
+++ b/lib/classes/FileLock.php
@@ -0,0 +1,74 @@
+
+ * @copyright 2013 Stud.IP Core-Group
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 2.4
+ */
+
+class FileLock
+{
+ protected $file;
+
+ /**
+ * Constructs a new lock object with the provided id.
+ *
+ * @param String $id Identifier of the lock
+ */
+ public function __construct($id)
+ {
+ $this->file = fopen("{$GLOBALS['TMP_PATH']}/$id.json", 'c+');
+
+ if (!$this->file) {
+ throw new RuntimeException('failed to create lock file.');
+ }
+ }
+
+ /**
+ * Try to aquire a file lock. The provided lock information will
+ * be stored with the lock. If the lock cannot be aquired, the
+ * lock information in $data is updated from the lock file.
+ *
+ * @param array $data additional data to be stored with the lock
+ * @return boolean true on success or false on failure
+ */
+ public function tryLock(&$data = [])
+ {
+ rewind($this->file);
+
+ if (flock($this->file, LOCK_EX | LOCK_NB)) {
+ ftruncate($this->file, 0);
+ fwrite($this->file, json_encode($data));
+ fflush($this->file);
+
+ return true;
+ } else {
+ $json = stream_get_contents($this->file);
+ $data = json_decode($json, true);
+
+ return false;
+ }
+ }
+
+ /**
+ * Releases a previously obtained lock
+ *
+ * @return boolean true on success or false on failure
+ */
+ public function release()
+ {
+ return flock($this->file, LOCK_UN);
+ }
+}
diff --git a/lib/classes/Fullcalendar.class.php b/lib/classes/Fullcalendar.class.php
deleted file mode 100644
index db8364a..0000000
--- a/lib/classes/Fullcalendar.class.php
+++ /dev/null
@@ -1,109 +0,0 @@
-render();
- }
-
-
- public function __construct(
- $title = '',
- $config = [],
- $attributes = [],
- $data_name = 'fullcalendar'
- )
- {
- $this->title = $title;
- $this->config = $config;
- $this->attributes = $attributes;
- $this->data_name = $data_name;
- }
-
-
- public function render()
- {
- $factory = new \Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/templates');
- $template = $factory->open('studip-fullcalendar.php');
- $real_data_name = sprintf('data-%s', $this->data_name);
- return $template->render(
- [
- 'title' => $this->title,
- 'config' => $this->config,
- 'attributes' => array_merge(
- $this->attributes,
- [$real_data_name => '1']
- )
- ]
- );
- }
-
-
- /**
- * Creates an array with data for a Fullcalendar instance
- * from Stud.IP objects that implement the EventSource interface.
- */
- public static function createData($objects = [], $begin = null, $end = null)
- {
- if (!count($objects)) {
- //No data means there is nothing to do.
- return [];
- }
-
- $data = [];
-
- foreach ($objects as $object) {
- if ($object instanceof \Studip\Calendar\EventSource) {
- $events = $object->getFilteredEventData(
- $GLOBALS['user']->id, null, null, $begin, $end
- );
-
- foreach ($events as $event) {
- $data[] = $event->toFullcalendarEvent();
- }
- }
- }
- return $data;
- }
-}
diff --git a/lib/classes/Fullcalendar.php b/lib/classes/Fullcalendar.php
new file mode 100644
index 0000000..db8364a
--- /dev/null
+++ b/lib/classes/Fullcalendar.php
@@ -0,0 +1,109 @@
+render();
+ }
+
+
+ public function __construct(
+ $title = '',
+ $config = [],
+ $attributes = [],
+ $data_name = 'fullcalendar'
+ )
+ {
+ $this->title = $title;
+ $this->config = $config;
+ $this->attributes = $attributes;
+ $this->data_name = $data_name;
+ }
+
+
+ public function render()
+ {
+ $factory = new \Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/templates');
+ $template = $factory->open('studip-fullcalendar.php');
+ $real_data_name = sprintf('data-%s', $this->data_name);
+ return $template->render(
+ [
+ 'title' => $this->title,
+ 'config' => $this->config,
+ 'attributes' => array_merge(
+ $this->attributes,
+ [$real_data_name => '1']
+ )
+ ]
+ );
+ }
+
+
+ /**
+ * Creates an array with data for a Fullcalendar instance
+ * from Stud.IP objects that implement the EventSource interface.
+ */
+ public static function createData($objects = [], $begin = null, $end = null)
+ {
+ if (!count($objects)) {
+ //No data means there is nothing to do.
+ return [];
+ }
+
+ $data = [];
+
+ foreach ($objects as $object) {
+ if ($object instanceof \Studip\Calendar\EventSource) {
+ $events = $object->getFilteredEventData(
+ $GLOBALS['user']->id, null, null, $begin, $end
+ );
+
+ foreach ($events as $event) {
+ $data[] = $event->toFullcalendarEvent();
+ }
+ }
+ }
+ return $data;
+ }
+}
diff --git a/lib/classes/Icon.class.php b/lib/classes/Icon.class.php
deleted file mode 100644
index 6c586a0..0000000
--- a/lib/classes/Icon.class.php
+++ /dev/null
@@ -1,395 +0,0 @@
-
- * @copyright Stud.IP Core Group
- * @license GPL2 or any later version
- * @since 3.2
- */
-class Icon
-{
- const SVG = 1;
- const CSS_BACKGROUND = 4;
- const INPUT = 256;
-
- const DEFAULT_SIZE = 16;
- const DEFAULT_COLOR = 'blue';
- const DEFAULT_ROLE = 'clickable';
-
- const ROLE_INFO = 'info';
- const ROLE_CLICKABLE = 'clickable';
- const ROLE_ACCEPT = 'accept';
- const ROLE_STATUS_GREEN = 'status-green';
- const ROLE_INACTIVE = 'inactive';
- const ROLE_NAVIGATION = 'navigation';
- const ROLE_NEW = 'new';
- const ROLE_ATTENTION = 'attention';
- const ROLE_STATUS_RED = 'status-red';
- const ROLE_INFO_ALT = 'info_alt';
- const ROLE_SORT = 'sort';
- const ROLE_STATUS_YELLOW = 'status-yellow';
-
-
- protected $shape;
- protected $role;
- protected $attributes = [];
-
-
- /**
- * This is the magical Role to Color mapping.
- */
- private static $roles_to_colors = [
- self::ROLE_INFO => 'black',
- self::ROLE_CLICKABLE => 'blue',
- self::ROLE_ACCEPT => 'green',
- self::ROLE_STATUS_GREEN => 'green',
- self::ROLE_INACTIVE => 'grey',
- self::ROLE_NAVIGATION => 'blue',
- self::ROLE_NEW => 'red',
- self::ROLE_ATTENTION => 'red',
- self::ROLE_STATUS_RED => 'red',
- self::ROLE_INFO_ALT => 'white',
- self::ROLE_SORT => 'blue',
- self::ROLE_STATUS_YELLOW => 'yellow'
- ];
-
- // return the color associated to a role
- private static function roleToColor($role)
- {
- if (!isset(self::$roles_to_colors[$role])) {
- throw new \InvalidArgumentException('Unknown role: "' . $role . '"');
- }
- return self::$roles_to_colors[$role];
- }
-
- // return the roles! associated to a color
- public static function colorToRoles($color)
- {
- static $colors_to_roles;
-
- if (!$colors_to_roles) {
- foreach (self::$roles_to_colors as $r => $c) {
- $colors_to_roles[$c][] = $r;
- }
- }
-
- if (!isset($colors_to_roles[$color])) {
- throw new \InvalidArgumentException('Unknown color: "' . $color . '"');
- }
-
- return $colors_to_roles[$color];
- }
-
- /**
- * Create a new Icon object.
- *
- * This is just a factory method. You could easily just call the
- * constructor instead.
- *
- * @param String $shape Shape of the icon, may contain a mixed definition
- * like 'seminar'
- * @param String $role Role of the icon, defaults to Icon::DEFAULT_ROLE
- * @param Array $attributes Additional attributes like 'title';
- * only use semantic ones describing
- * this icon regardless of its later
- * rendering in a view
- * @return Icon object
- */
- public static function create($shape, $role = Icon::DEFAULT_ROLE, $attributes = [])
- {
- // $role may be omitted
- if (is_array($role)) {
- $attributes = $role;
- $role = Icon::DEFAULT_ROLE;
- }
-
- return new self($shape, $role, $attributes);
- }
-
- /**
- * Constructor of the object.
- *
- * @param String $shape Shape of the icon, may contain a mixed definition
- * like 'seminar'
- * @param String $role Role of the icon, defaults to Icon::DEFAULT_ROLE
- * @param Array $attributes Additional attributes like 'title';
- * only use semantic ones describing
- * this icon regardless of its later
- * rendering in a view
- */
- public function __construct($shape, $role = Icon::DEFAULT_ROLE, array $attributes = [])
- {
-
- // only defined roles
- if (!isset(self::$roles_to_colors[$role])) {
- throw new \InvalidArgumentException('Creating an Icon without proper role: "' . $role . '"');
- }
-
- // only semantic attributes
- if ($non_semantic = array_filter(array_keys($attributes), function ($attr) {
- return !in_array($attr, ['title']);
- })) {
- // DEPRECATED
- // TODO starting with the v3.6 the following line should
- // be enabled to prevent non-semantic attributes in this position
- # throw new \InvalidArgumentException('Creating an Icon with non-semantic attributes:' . json_encode($non_semantic));
- }
-
- $this->shape = $shape;
- $this->role = $role;
- $this->attributes = $attributes;
- }
-
- /**
- * Returns the `shape` -- the string describing the shape of this instance.
- * @return String the shape of this Icon
- */
- public function getShape()
- {
- return $this->shapeToPath($this->shape);
- }
-
- /**
- * Returns the `role` -- the string describing the role of this instance.
- * @return String the role of this Icon
- */
- public function getRole()
- {
- return $this->role;
- }
-
- /**
- * Returns the semantic `attributes` of this instance, e.g. the title of this Icon
- * @return Array the semantic attribiutes of the Icon
- */
- public function getAttributes()
- {
- return $this->attributes;
- }
-
- /**
- * Returns whether this icon intends to signal attention.
- *
- * @todo This is currently just a heuristic based on the associated icon
- * role. Although this is sufficient for the current requirements,
- * it could probably in a better, more suitable way.
- *
- * @return bool
- * @since Stud.IP 5.0
- */
- public function signalsAttention()
- {
- return $this->roleToColor($this->role) === 'red';
- }
-
- /**
- * Function to be called whenever the object is converted to
- * string. Internally the same as calling Icon::asImg
- *
- * @return String representation
- */
- public function __toString()
- {
- return $this->asImg();
- }
-
- /**
- * Renders the icon inside an img html tag.
- *
- * @param int $size Optional; Defines the dimension in px of the rendered icon; FALSE prevents any
- * width or height attributes
- * @param Array $view_attributes Optional; Additional attributes to pass
- * into the rendered output
- * @return String containing the html representation for the icon.
- */
- public function asImg($size = null, $view_attributes = [])
- {
- if (is_array($size)) {
- list($view_attributes, $size) = [$size, null];
- }
- return sprintf(
- '',
- arrayToHtmlAttributes(
- $this->prepareHTMLAttributes($size, $view_attributes)
- )
- );
- }
-
- /**
- * Renders the icon inside an input html tag.
- *
- * @param int $size Optional; Defines the dimension in px of the rendered icon; FALSE prevents any
- * width or height attributes
- * @param Array $view_attributes Optional; Additional attributes to pass
- * into the rendered output
- * @return String containing the html representation for the icon.
- */
- public function asInput($size = null, $view_attributes = [])
- {
- if (is_array($size)) {
- list($view_attributes, $size) = [$size, null];
- }
- return sprintf(
- '',
- arrayToHtmlAttributes(
- $this->prepareHTMLAttributes($size, $view_attributes)
- )
- );
- }
-
- /**
- * Renders the icon as a set of css background rules.
- *
- * @param int $size Optional; Defines the size in px of the rendered icon
- * @return String containing the html representation for css backgrounds
- */
- public function asCSS($size = null)
- {
- if (self::isStatic($this->shape)) {
- return sprintf(
- 'background-image:url(%1$s);background-size:%2$upx %2$upx;',
- $this->shapeToPath($this->shape),
- $this->get_size($size)
- );
- }
-
- return sprintf(
- 'background-image:url(%1$s);background-size:%2$upx %2$upx;',
- $this->get_asset_svg(),
- $this->get_size($size)
- );
- }
-
- /**
- * Returns a path to the SVG matching the icon.
- *
- * @return String containing the html representation for css backgrounds
- */
- public function asImagePath()
- {
- return $this->prepareHTMLAttributes(false, [])['src'];
- }
-
- /**
- * Returns a new Icon with a changed shape
- * @param mixed $shape New value of `shape`
- * @return Icon A new Icon with a new `shape`
- */
- public function copyWithShape($shape)
- {
- $clone = clone $this;
- $clone->shape = $shape;
- return $clone;
- }
-
- /**
- * Returns a new Icon with a changed role
- * @param mixed $role New value of `role`
- * @return Icon A new Icon with a new `role`
- */
- public function copyWithRole($role)
- {
- $clone = clone $this;
- $clone->role = $role;
- return $clone;
- }
-
- /**
- * Returns a new Icon with new attributes
- * @param mixed $attributes New value of `attributes`
- * @return Icon A new Icon with a new `attributes`
- */
- public function copyWithAttributes($attributes)
- {
- $clone = clone $this;
- $clone->attributes = $attributes;
- return $clone;
- }
-
- /**
- * Prepares the html attributes for use assembling HTML attributes
- * from given shape, role, size, semantic and view attributes
- *
- * @param int $size Size of the icon
- * @param array $attributes Additional attributes
- * @return Array containing the merged attributes
- */
- private function prepareHTMLAttributes($size, $attributes)
- {
- $dimensions = [];
- if ($size !== false) {
- $size = $this->get_size($size);
- $dimensions = ['width' => $size, 'height' => $size];
- }
-
- $result = array_merge($this->attributes, $attributes, $dimensions, [
- 'src' => self::isStatic($this->shape) ? $this->shape : $this->get_asset_svg(),
- ]);
-
- if (!isset($result['alt']) && !isset($result['title'])) {
- //Add an empty alt attribute to prevent screen readers from
- //reading the URL of the icon:
- $result['alt'] = '';
- }
-
- $classNames = 'icon-role-' . $this->role;
-
- if (!self::isStatic($this->shape)) {
- $classNames .= ' icon-shape-' . $this->shapeToPath($this->shape);
- }
-
- $result['class'] = isset($result['class']) ? $result['class'] . ' ' . $classNames : $classNames;
-
- return $result;
- }
-
- /**
- * Get the correct asset for an SVG icon.
- *
- * @return String containing the url of the corresponding asset
- */
- protected function get_asset_svg()
- {
- return Assets::url('images/icons/' . self::roleToColor($this->role) . '/' . $this->shapeToPath($this->shape) . '.svg');
- }
-
- /**
- * Get the size of the icon. If a size was passed as a parameter and
- * inside the attributes array during icon construction, the size from
- * the attributes will be used.
- *
- * @param int $size size of the icon
- * @return int Size of the icon in pixels
- */
- protected function get_size($size)
- {
- $size = $size ?: Icon::DEFAULT_SIZE;
- if (isset($this->attributes['size'])) {
- $parts = explode('@', $this->attributes['size'], 2);
- $size = $parts[0];
- $temp = $parts[1] ?? null;
- unset($this->attributes['size']);
- }
- return (int)$size;
- }
-
- // an icon is static if it starts with 'http'
- private static function isStatic($shape)
- {
- return mb_strpos($shape, 'http') === 0;
- }
-
- // transforms a shape w/ possible additions (`shape`) to a path `(addition/)?shape`
- private function shapeToPath()
- {
- if (self::isStatic($this->shape)) {
- return $this->shape;
- }
- $shape = array_reverse(explode('/', $this->shape))[0];
- $shape = explode('+', $shape)[0];
- return $shape;
- }
-}
diff --git a/lib/classes/Icon.php b/lib/classes/Icon.php
new file mode 100644
index 0000000..6c586a0
--- /dev/null
+++ b/lib/classes/Icon.php
@@ -0,0 +1,395 @@
+
+ * @copyright Stud.IP Core Group
+ * @license GPL2 or any later version
+ * @since 3.2
+ */
+class Icon
+{
+ const SVG = 1;
+ const CSS_BACKGROUND = 4;
+ const INPUT = 256;
+
+ const DEFAULT_SIZE = 16;
+ const DEFAULT_COLOR = 'blue';
+ const DEFAULT_ROLE = 'clickable';
+
+ const ROLE_INFO = 'info';
+ const ROLE_CLICKABLE = 'clickable';
+ const ROLE_ACCEPT = 'accept';
+ const ROLE_STATUS_GREEN = 'status-green';
+ const ROLE_INACTIVE = 'inactive';
+ const ROLE_NAVIGATION = 'navigation';
+ const ROLE_NEW = 'new';
+ const ROLE_ATTENTION = 'attention';
+ const ROLE_STATUS_RED = 'status-red';
+ const ROLE_INFO_ALT = 'info_alt';
+ const ROLE_SORT = 'sort';
+ const ROLE_STATUS_YELLOW = 'status-yellow';
+
+
+ protected $shape;
+ protected $role;
+ protected $attributes = [];
+
+
+ /**
+ * This is the magical Role to Color mapping.
+ */
+ private static $roles_to_colors = [
+ self::ROLE_INFO => 'black',
+ self::ROLE_CLICKABLE => 'blue',
+ self::ROLE_ACCEPT => 'green',
+ self::ROLE_STATUS_GREEN => 'green',
+ self::ROLE_INACTIVE => 'grey',
+ self::ROLE_NAVIGATION => 'blue',
+ self::ROLE_NEW => 'red',
+ self::ROLE_ATTENTION => 'red',
+ self::ROLE_STATUS_RED => 'red',
+ self::ROLE_INFO_ALT => 'white',
+ self::ROLE_SORT => 'blue',
+ self::ROLE_STATUS_YELLOW => 'yellow'
+ ];
+
+ // return the color associated to a role
+ private static function roleToColor($role)
+ {
+ if (!isset(self::$roles_to_colors[$role])) {
+ throw new \InvalidArgumentException('Unknown role: "' . $role . '"');
+ }
+ return self::$roles_to_colors[$role];
+ }
+
+ // return the roles! associated to a color
+ public static function colorToRoles($color)
+ {
+ static $colors_to_roles;
+
+ if (!$colors_to_roles) {
+ foreach (self::$roles_to_colors as $r => $c) {
+ $colors_to_roles[$c][] = $r;
+ }
+ }
+
+ if (!isset($colors_to_roles[$color])) {
+ throw new \InvalidArgumentException('Unknown color: "' . $color . '"');
+ }
+
+ return $colors_to_roles[$color];
+ }
+
+ /**
+ * Create a new Icon object.
+ *
+ * This is just a factory method. You could easily just call the
+ * constructor instead.
+ *
+ * @param String $shape Shape of the icon, may contain a mixed definition
+ * like 'seminar'
+ * @param String $role Role of the icon, defaults to Icon::DEFAULT_ROLE
+ * @param Array $attributes Additional attributes like 'title';
+ * only use semantic ones describing
+ * this icon regardless of its later
+ * rendering in a view
+ * @return Icon object
+ */
+ public static function create($shape, $role = Icon::DEFAULT_ROLE, $attributes = [])
+ {
+ // $role may be omitted
+ if (is_array($role)) {
+ $attributes = $role;
+ $role = Icon::DEFAULT_ROLE;
+ }
+
+ return new self($shape, $role, $attributes);
+ }
+
+ /**
+ * Constructor of the object.
+ *
+ * @param String $shape Shape of the icon, may contain a mixed definition
+ * like 'seminar'
+ * @param String $role Role of the icon, defaults to Icon::DEFAULT_ROLE
+ * @param Array $attributes Additional attributes like 'title';
+ * only use semantic ones describing
+ * this icon regardless of its later
+ * rendering in a view
+ */
+ public function __construct($shape, $role = Icon::DEFAULT_ROLE, array $attributes = [])
+ {
+
+ // only defined roles
+ if (!isset(self::$roles_to_colors[$role])) {
+ throw new \InvalidArgumentException('Creating an Icon without proper role: "' . $role . '"');
+ }
+
+ // only semantic attributes
+ if ($non_semantic = array_filter(array_keys($attributes), function ($attr) {
+ return !in_array($attr, ['title']);
+ })) {
+ // DEPRECATED
+ // TODO starting with the v3.6 the following line should
+ // be enabled to prevent non-semantic attributes in this position
+ # throw new \InvalidArgumentException('Creating an Icon with non-semantic attributes:' . json_encode($non_semantic));
+ }
+
+ $this->shape = $shape;
+ $this->role = $role;
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * Returns the `shape` -- the string describing the shape of this instance.
+ * @return String the shape of this Icon
+ */
+ public function getShape()
+ {
+ return $this->shapeToPath($this->shape);
+ }
+
+ /**
+ * Returns the `role` -- the string describing the role of this instance.
+ * @return String the role of this Icon
+ */
+ public function getRole()
+ {
+ return $this->role;
+ }
+
+ /**
+ * Returns the semantic `attributes` of this instance, e.g. the title of this Icon
+ * @return Array the semantic attribiutes of the Icon
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Returns whether this icon intends to signal attention.
+ *
+ * @todo This is currently just a heuristic based on the associated icon
+ * role. Although this is sufficient for the current requirements,
+ * it could probably in a better, more suitable way.
+ *
+ * @return bool
+ * @since Stud.IP 5.0
+ */
+ public function signalsAttention()
+ {
+ return $this->roleToColor($this->role) === 'red';
+ }
+
+ /**
+ * Function to be called whenever the object is converted to
+ * string. Internally the same as calling Icon::asImg
+ *
+ * @return String representation
+ */
+ public function __toString()
+ {
+ return $this->asImg();
+ }
+
+ /**
+ * Renders the icon inside an img html tag.
+ *
+ * @param int $size Optional; Defines the dimension in px of the rendered icon; FALSE prevents any
+ * width or height attributes
+ * @param Array $view_attributes Optional; Additional attributes to pass
+ * into the rendered output
+ * @return String containing the html representation for the icon.
+ */
+ public function asImg($size = null, $view_attributes = [])
+ {
+ if (is_array($size)) {
+ list($view_attributes, $size) = [$size, null];
+ }
+ return sprintf(
+ '',
+ arrayToHtmlAttributes(
+ $this->prepareHTMLAttributes($size, $view_attributes)
+ )
+ );
+ }
+
+ /**
+ * Renders the icon inside an input html tag.
+ *
+ * @param int $size Optional; Defines the dimension in px of the rendered icon; FALSE prevents any
+ * width or height attributes
+ * @param Array $view_attributes Optional; Additional attributes to pass
+ * into the rendered output
+ * @return String containing the html representation for the icon.
+ */
+ public function asInput($size = null, $view_attributes = [])
+ {
+ if (is_array($size)) {
+ list($view_attributes, $size) = [$size, null];
+ }
+ return sprintf(
+ '',
+ arrayToHtmlAttributes(
+ $this->prepareHTMLAttributes($size, $view_attributes)
+ )
+ );
+ }
+
+ /**
+ * Renders the icon as a set of css background rules.
+ *
+ * @param int $size Optional; Defines the size in px of the rendered icon
+ * @return String containing the html representation for css backgrounds
+ */
+ public function asCSS($size = null)
+ {
+ if (self::isStatic($this->shape)) {
+ return sprintf(
+ 'background-image:url(%1$s);background-size:%2$upx %2$upx;',
+ $this->shapeToPath($this->shape),
+ $this->get_size($size)
+ );
+ }
+
+ return sprintf(
+ 'background-image:url(%1$s);background-size:%2$upx %2$upx;',
+ $this->get_asset_svg(),
+ $this->get_size($size)
+ );
+ }
+
+ /**
+ * Returns a path to the SVG matching the icon.
+ *
+ * @return String containing the html representation for css backgrounds
+ */
+ public function asImagePath()
+ {
+ return $this->prepareHTMLAttributes(false, [])['src'];
+ }
+
+ /**
+ * Returns a new Icon with a changed shape
+ * @param mixed $shape New value of `shape`
+ * @return Icon A new Icon with a new `shape`
+ */
+ public function copyWithShape($shape)
+ {
+ $clone = clone $this;
+ $clone->shape = $shape;
+ return $clone;
+ }
+
+ /**
+ * Returns a new Icon with a changed role
+ * @param mixed $role New value of `role`
+ * @return Icon A new Icon with a new `role`
+ */
+ public function copyWithRole($role)
+ {
+ $clone = clone $this;
+ $clone->role = $role;
+ return $clone;
+ }
+
+ /**
+ * Returns a new Icon with new attributes
+ * @param mixed $attributes New value of `attributes`
+ * @return Icon A new Icon with a new `attributes`
+ */
+ public function copyWithAttributes($attributes)
+ {
+ $clone = clone $this;
+ $clone->attributes = $attributes;
+ return $clone;
+ }
+
+ /**
+ * Prepares the html attributes for use assembling HTML attributes
+ * from given shape, role, size, semantic and view attributes
+ *
+ * @param int $size Size of the icon
+ * @param array $attributes Additional attributes
+ * @return Array containing the merged attributes
+ */
+ private function prepareHTMLAttributes($size, $attributes)
+ {
+ $dimensions = [];
+ if ($size !== false) {
+ $size = $this->get_size($size);
+ $dimensions = ['width' => $size, 'height' => $size];
+ }
+
+ $result = array_merge($this->attributes, $attributes, $dimensions, [
+ 'src' => self::isStatic($this->shape) ? $this->shape : $this->get_asset_svg(),
+ ]);
+
+ if (!isset($result['alt']) && !isset($result['title'])) {
+ //Add an empty alt attribute to prevent screen readers from
+ //reading the URL of the icon:
+ $result['alt'] = '';
+ }
+
+ $classNames = 'icon-role-' . $this->role;
+
+ if (!self::isStatic($this->shape)) {
+ $classNames .= ' icon-shape-' . $this->shapeToPath($this->shape);
+ }
+
+ $result['class'] = isset($result['class']) ? $result['class'] . ' ' . $classNames : $classNames;
+
+ return $result;
+ }
+
+ /**
+ * Get the correct asset for an SVG icon.
+ *
+ * @return String containing the url of the corresponding asset
+ */
+ protected function get_asset_svg()
+ {
+ return Assets::url('images/icons/' . self::roleToColor($this->role) . '/' . $this->shapeToPath($this->shape) . '.svg');
+ }
+
+ /**
+ * Get the size of the icon. If a size was passed as a parameter and
+ * inside the attributes array during icon construction, the size from
+ * the attributes will be used.
+ *
+ * @param int $size size of the icon
+ * @return int Size of the icon in pixels
+ */
+ protected function get_size($size)
+ {
+ $size = $size ?: Icon::DEFAULT_SIZE;
+ if (isset($this->attributes['size'])) {
+ $parts = explode('@', $this->attributes['size'], 2);
+ $size = $parts[0];
+ $temp = $parts[1] ?? null;
+ unset($this->attributes['size']);
+ }
+ return (int)$size;
+ }
+
+ // an icon is static if it starts with 'http'
+ private static function isStatic($shape)
+ {
+ return mb_strpos($shape, 'http') === 0;
+ }
+
+ // transforms a shape w/ possible additions (`shape`) to a path `(addition/)?shape`
+ private function shapeToPath()
+ {
+ if (self::isStatic($this->shape)) {
+ return $this->shape;
+ }
+ $shape = array_reverse(explode('/', $this->shape))[0];
+ $shape = explode('+', $shape)[0];
+ return $shape;
+ }
+}
diff --git a/lib/classes/InstituteAvatar.class.php b/lib/classes/InstituteAvatar.class.php
deleted file mode 100644
index 8adbfba..0000000
--- a/lib/classes/InstituteAvatar.class.php
+++ /dev/null
@@ -1,45 +0,0 @@
-
- * @author Marcus Lunzenauer
- * @copyright (c) Authors
- * @license GPL2 or any later version
- * @since 1.10
- */
-class InstituteAvatar extends CourseAvatar
-{
- public const AVATAR_TYPE = 'institute';
-
- /**
- * Returns the CSS class to use for this avatar image.
- *
- * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- * @return string CSS class to use for the avatar
- */
- protected function getCssClass($size)
- {
- return "institute-avatar-{$size} institute-{$this->user_id}";
- }
-
- /**
- * Return the default title of the avatar.
- * @return string the default title
- */
- public function getDefaultTitle()
- {
- $institute = Institute::find($this->user_id);
- return $institute ? (string) $institute->name : self::NOBODY;
- }
-
- /**
- * Return if avatar is visible to the current user.
- * @return boolean: true if visible
- */
- protected function checkAvatarVisibility()
- {
- //no special conditions for visibility of course-avatars yet
- return true;
- }
-}
diff --git a/lib/classes/InstituteAvatar.php b/lib/classes/InstituteAvatar.php
new file mode 100644
index 0000000..8adbfba
--- /dev/null
+++ b/lib/classes/InstituteAvatar.php
@@ -0,0 +1,45 @@
+
+ * @author Marcus Lunzenauer
+ * @copyright (c) Authors
+ * @license GPL2 or any later version
+ * @since 1.10
+ */
+class InstituteAvatar extends CourseAvatar
+{
+ public const AVATAR_TYPE = 'institute';
+
+ /**
+ * Returns the CSS class to use for this avatar image.
+ *
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
+ * @return string CSS class to use for the avatar
+ */
+ protected function getCssClass($size)
+ {
+ return "institute-avatar-{$size} institute-{$this->user_id}";
+ }
+
+ /**
+ * Return the default title of the avatar.
+ * @return string the default title
+ */
+ public function getDefaultTitle()
+ {
+ $institute = Institute::find($this->user_id);
+ return $institute ? (string) $institute->name : self::NOBODY;
+ }
+
+ /**
+ * Return if avatar is visible to the current user.
+ * @return boolean: true if visible
+ */
+ protected function checkAvatarVisibility()
+ {
+ //no special conditions for visibility of course-avatars yet
+ return true;
+ }
+}
diff --git a/lib/classes/InstituteCalendarHelper.class.php b/lib/classes/InstituteCalendarHelper.class.php
deleted file mode 100644
index 4302cf1..0000000
--- a/lib/classes/InstituteCalendarHelper.class.php
+++ /dev/null
@@ -1,810 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- *
- */
-
-class InstituteCalendarHelper
-{
- const COLUMN_DATAFIELD_ID = '69f6485f3c937766866a03d9d642ecbb';
- const COLOR_DATAFIELD_ID = '41cda2be71fe9efd6e28b853fc0681f3';
- const INST_DEFAULT_COLOR_DATAFIELD_ID = '0c63321a8e93b3ccc927611709248e07';
- const DEFAULT_EVENT_COLOR = '#899ab9';
-
- /**
- * Returns the default calendar columns.
- *
- * @return array default column names
- */
- private static function getDefaultColumns()
- {
- return [
- 1 => ['Spalte 1', 1],
- 2 => ['Spalte 2', 1],
- 3 => ['Spalte 3', 1],
- 4 => ['Spalte 4', 1],
- 5 => ['Spalte 5', 1],
- 6 => ['Spalte 6', 1]
- ];
- }
-
- /**
- * Fetches the stores columns merged with defaults.
- *
- * @param string $institut_id
- * @param boolean $only_visible only return visible columns
- *
- * @return array column array in a fullcalender expected format
- */
- public static function getResourceColumns($institut_id, $only_visible = false)
- {
- $columns = [];
- $inst_columns = [
- 0 => ['Sammelspalte', 1]
- ];
-
- $db_inst_columns = InstitutePlanColumn::findByInstitute($institut_id);
-
- if ($db_inst_columns) {
- foreach ($db_inst_columns as $col_info) {
- $inst_columns[$col_info['column']] = [$col_info['name'], $col_info['visible']];
- }
- } else {
- $inst_columns = array_merge($inst_columns, self::getDefaultColumns());
- }
- foreach ($inst_columns as $id => $info) {
- if ($only_visible && !$info[1]) continue;
- $columns[] = ['id' => $id, 'title' => $info[0], 'visible' => $info[1]];
- }
- return $columns;
- }
-
- /**
- * Adds the default resource columns
- *
- * @param string $institut_id
- *
- * @return int last resource column number
- */
- public static function addDefaultResourceColumns($institut_id)
- {
- $max_col = 0;
- foreach (self::getDefaultColumns() as $col_id => $col_info) {
- $new_col = new InstitutePlanColumn([$institut_id, $col_id]);
- $new_col->name = $col_info[0];
- $new_col->visible = $col_info[1];
- if ($new_col->store()) {
- $max_col = $col_id;
- }
- }
- return $max_col;
- }
-
- /**
- * Adds a resource column
- *
- * @param string $institut_id
- * @param string $name
- * @param int $specific_column_number
- *
- * @return int number of affected rows
- */
- public static function addResourceColumn($institut_id, $name, $specific_column_number = 0)
- {
- $last_col = InstitutePlanColumn::getLastColumnOfInstitute($institut_id);
- if ($last_col !== null) {
- $max_col = $last_col->column;
- } else {
- $max_col = self::addDefaultResourceColumns($institut_id);
- }
- $column_number = $specific_column_number>0?$specific_column_number:intval($max_col)+1;
- $new_col = new InstitutePlanColumn([$institut_id, $column_number]);
- $new_col->name = $name;
- $new_col->visible = 1;
- return $new_col->store();
- }
-
- /**
- * Looks up column id for course events
- *
- * @param Course $course
- *
- * @return array course events with column id
- */
- public static function getCourseEventcolumns(Course $course)
- {
- $df = DatafieldEntryModel::findByModel($course, self::COLUMN_DATAFIELD_ID);
- if ($df[0] && $df[0]->content) {
- $event_columns = unserialize($df[0]->content);
- } else {
- $event_columns = [];
- }
- return $event_columns;
- }
-
- /**
- * Sets the column id for course events
- *
- * @param Course $course
- * @param string $event_id SeminarCycleDate id
- * @param string $institut_id
- * @param string $column number of the column
- *
- * @return bool stored
- */
- public static function setCourseEventcolumn($course, $event_id, $institut_id, $column)
- {
- $df = DatafieldEntryModel::findByModel($course, self::COLUMN_DATAFIELD_ID);
- if ($df[0]) {
- $event_columns = self::getCourseEventcolumns($course);
- if (!is_array($event_columns[$event_id])) {
- unset($event_columns[$event_id]);
- }
- $event_columns[$event_id][$institut_id] = $column;
- $df[0]->content = serialize($event_columns);
- return $df[0]->store();
- }
- return false;
- }
-
- /**
- * Looks up color value for course events
- *
- * @param Course $course
- *
- * @return array course events with color value
- */
- public static function getCourseEventcolors($course)
- {
- $df = DatafieldEntryModel::findByModel($course, self::COLOR_DATAFIELD_ID);
- if ($df[0] && $df[0]->content) {
- $event_colors = unserialize($df[0]->content);
- } else {
- $event_colors = [];
- }
- return $event_colors;
- }
-
- /**
- * Sets color value for course events
- *
- * @param Course $course
- * @param string $event_id SeminarCycleDate id
- * @param string $institut_id
- * @param string $color colorcode
- *
- * @return bool stored
- */
- public static function setCourseEventcolor($course, $event_id, $institut_id, $color)
- {
- $df = DatafieldEntryModel::findByModel($course, self::COLOR_DATAFIELD_ID);
- if ($df[0]) {
- $event_colors = self::getCourseEventcolors($course);
- if (!is_array($event_colors[$event_id])) {
- unset($event_colors[$event_id]);
- }
- $event_colors[$event_id][$institut_id] = $color;
- $df[0]->content = serialize($event_colors);
- return $df[0]->store();
- }
- return false;
- }
-
- /**
- * Looks up default color value for institute course events
- *
- * @param SimpleORMap $context
- *
- * @return array course events with color value
- */
- public static function getInstituteDefaultEventcolors($context)
- {
- $df = DatafieldEntryModel::findByModel($context, self::INST_DEFAULT_COLOR_DATAFIELD_ID);
- if ($df && $df[0]->content) {
- $event_colors = unserialize($df[0]->content);
- } else {
- $event_colors = [];
- }
- return $event_colors;
- }
-
- /**
- * Sets default institute course events color value for semtypes
- *
- * @param SimpleORMap $context
- * @param string $semtype
- * @param string $color colorcode
- *
- * @return bool stored
- */
- public static function setInstituteDefaultEventcolor($context, $semtype, $color)
- {
- $df = DatafieldEntryModel::findByModel($context, self::INST_DEFAULT_COLOR_DATAFIELD_ID);
- if ($df[0]) {
- $event_colors = self::getInstituteDefaultEventcolors($context);
- $event_colors[$semtype['name']] = $color;
- $df[0]->content = serialize($event_colors);
- return $df[0]->store();
- }
- return false;
- }
-
- /**
- * Sets color value for every course events of given courses semtype
- *
- * @param Course $course
- * @param string $institut_id
- * @param string $color colorcode
- *
- * @return bool stored
- */
- public static function setSemtypeEventcolor($course, $institut_id, $color)
- {
- $semtype = $course->getSemType();
- $institut = Institute::find($institut_id);
- if ($institut) {
- self::setInstituteDefaultEventcolor($institut, $semtype, $color);
- }
- $courses = Course::findBySQL('status =? AND Institut_id=?', [$semtype['id'], $course->institut_id]);
- if ($courses) {
- foreach ($courses as $semtype_course) {
- foreach (SeminarCycleDate::findBySeminar($semtype_course->seminar_id) as $cycle_date) {
- self::setCourseEventcolor($semtype_course, $cycle_date->id, $institut_id, $color);
- }
- }
- return true;
- }
- return false;
- }
-
- /**
- * Prepares an array of course id and names for creation of dropable calendar events
- *
- * @param array $courses Array of courses
- * @param array $semester Semester
- *
- * @return array prepared array
- */
- public static function getEventlessCourses($courses, $semester = null)
- {
- $eventless = [];
- foreach (array_keys($courses) as $cid) {
- $course = Course::find($cid);
- $cycle_dates = SeminarCycleDate::findBySeminar($course->seminar_id);
- if (count($cycle_dates) < 1) {
- $eventless[$cid] = $course->getFullName('number-name');
- } elseif ($semester) {
- $has_date_in_semester = false;
- foreach ($cycle_dates as $cycle_date) {
- foreach ($cycle_date->getAllDates() as $course_date) {
- if ($course_date->date >= $semester->beginn && $course_date->date <= $semester->ende) {
- $has_date_in_semester = true;
- break;
- }
- }
- if ($has_date_in_semester) break;
- }
- if (!$has_date_in_semester) {
- $eventless[$cid] = $course->getFullName('number-name');
- }
- }
- }
- return $eventless;
- }
-
- /**
- * Creates FullCalendar event date of course events
- *
- * @param array $courses Array of courses
- * @param string $institut_id
- * @param array $semester Semester
- * @param array $specific_weekday fetch only events for specific weekday
- *
- * @return array fullcalendar events
- */
- public static function getEvents($courses, $institut_id, $semester = null, $specific_weekday = null)
- {
- $today = date('w');
-
- $user_insts = array_map(function ($arr) {
- return $arr['Institut_id'];
- }, Institute::getMyInstitutes($GLOBALS['user']->id));
-
- $min_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_START_HOUR);
- $minbigtime = (int) $min_time[0];
- $max_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_END_HOUR);
- $maxbigtime = (int) $max_time[0];
-
- $institut = Institute::find($institut_id);
-
- if (!$institut) {
- return [];
- }
-
- $inst_default_colors = self::getInstituteDefaultEventcolors($institut);
-
- $events = [];
- Course::findEachMany(function ($course) use (
- $courses,
- &$events,
- $today,
- $minbigtime,
- $maxbigtime,
- $user_insts,
- $institut_id,
- $inst_default_colors,
- $semester,
- $specific_weekday
- ) {
- $semtype = $course->getSemType();
-
- $event_columns = self::getCourseEventcolumns($course) ?: [];
- $event_colors = self::getCourseEventcolors($course) ?: [];
-
- if (in_array($course->institut_id, $user_insts)) {
- $is_editable = true;
- $is_start_editable = !LockRules::Check($course->id, 'room_time');
- $is_duration_editable = false;
- } else {
- $is_editable = false;
- $is_start_editable = false;
- $is_duration_editable = false;
- }
-
- foreach (SeminarCycleDate::findBySeminar($course->seminar_id) as $cycle_date) {
- if ($semester) {
- $has_date_in_semester = false;
- foreach ($cycle_date->getAllDates() as $course_date) {
- if ($course_date->date >= $semester->beginn && $course_date->date <= $semester->ende) {
- $has_date_in_semester = true;
- break;
- }
- }
- if (!$has_date_in_semester) {
- continue;
- }
- }
-
- if (is_numeric($specific_weekday) && $specific_weekday != $cycle_date['weekday']) {
- continue;
- }
-
- $conform = true;
-
- if ($cycle_date['weekday'] == 0) {
- $day_offset = 7 - $today;
- } else {
- $day_offset = $cycle_date['weekday'] - $today;
- }
-
- $start_time = explode(':', $cycle_date['start_time']);
- $bigtime = (int) $start_time[0];
- if ($bigtime > $maxbigtime || $bigtime < $minbigtime) {
- $conform = false;
- } elseif ($bigtime % 2) {
- $bigtime--;
- }
- $start_time = $bigtime . ':00:00';
-
- $end_time = explode(':', $start_time);
- $end_time[0] += 2;
- $end_time = implode(':', $end_time);
-
- $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event');
- $name = $course->getFullName('number-name');
-
- if ($start_time != $cycle_date['start_time'] && $end_time != $cycle_date['end_time']) {
- $start = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['start_time']);
- $end = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['end_time']);
- $backgroundcolor = '#6c737a';
- $textcolor = '#ffffff';
- } else {
- $start = self::iso8601date(strtotime($day_offset . ' days'), $start_time);
- $end = self::iso8601date(strtotime($day_offset . ' days'), $end_time);
-
- $backgroundcolor = null;
- if (array_key_exists($cycle_date->id, $event_colors)) {
- if (is_array($event_colors[$cycle_date->id]) && array_key_exists($institut_id, $event_colors[$cycle_date->id])) {
- $backgroundcolor = $event_colors[$cycle_date->id][$institut_id];
- }
- }
- if (!$backgroundcolor) {
- $backgroundcolor = array_key_exists($semtype['name'], $inst_default_colors)
- ? $inst_default_colors[$semtype['name']]
- : self::DEFAULT_EVENT_COLOR;
- }
-
- $textcolor = '#ffffff';
- if (self::calculateLuminosityRatio($backgroundcolor, $textcolor) < 3) {
- $textcolor = '#000000';
- }
- }
- if (!$is_editable) {
- $backgroundcolor = '#c4c7c9';
- $textcolor = '#000000';
- }
-
- $resource_column = '0';
- if (array_key_exists($cycle_date->id, $event_columns)) {
- if (is_array($event_columns[$cycle_date->id]) && array_key_exists($institut_id, $event_columns[$cycle_date->id])) {
- $resource_column = $event_columns[$cycle_date->id][$institut_id];
- }
- }
-
- $events[] = [
- 'resourceId' => $resource_column,
- 'id' => $cycle_date->id,
- 'title' => $name,
- 'start' => $start,
- 'end' => $end,
- 'textColor' => $textcolor,
- 'backgroundColor' => $backgroundcolor,
- 'borderColor' => '#000',
- 'editable' => $is_editable,
- 'startEditable' => $is_start_editable,
- 'durationEditable' => $is_duration_editable,
- 'resourceEditable' => true,
- 'studip_api_urls' => ['move' => $move_url],
- 'studip_view_urls' => ['edit' => URLHelper::getURL('dispatch.php/course/details/index/' . $cycle_date->seminar_id)],
- 'metadate_id' => $cycle_date->metadate_id,
- 'course_id' => $cycle_date->seminar_id,
- 'tooltip' => self::getCycleInfos($course, $cycle_date),
- 'icon' => $is_start_editable ? '' : 'lock-locked',
- 'conform' => $conform,
- ];
- }
- }, array_keys($courses));
-
- return $events;
- }
-
- /**
- * Creates a fullcalendar event of given SeminarCycleDate
- *
- * @param SeminarCycleDate $cycle_date
- * @param string $institut_id
- *
- * @return array enriched course info string for tooltip
- */
- public static function getCycleEvent($cycle_date, $institut_id)
- {
- $course = Course::find($cycle_date->seminar_id);
- $semtype = $course->getSemType();
- $institut = Institute::find($institut_id);
- $inst_default_colors = self::getInstituteDefaultEventcolors($institut);
-
- $today = date('w');
- $user_insts = array_map(function ($arr) {
- return $arr['Institut_id'];
- }, Institute::getMyInstitutes($GLOBALS['user']->id));
-
- $min_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_START_HOUR);
- $minbigtime = (int) $min_time[0];
- $max_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_END_HOUR);
- $maxbigtime = (int) $max_time[0];
-
- $start_time = explode(':', $cycle_date['start_time']);
- $bigtime = (int) $start_time[0];
-
- if ($bigtime > $maxbigtime || $bigtime < $minbigtime) {
- return null;
- } elseif ($bigtime % 2) {
- $bigtime--;
- }
- $start_time = $bigtime . ':00:00';
-
- $end_time = explode(':', $start_time);
- $end_time[0] += 2;
- $end_time = implode(':', $end_time);
-
- if (in_array($course->institut_id, $user_insts)) {
- $is_editable = true;
- $is_start_editable = !LockRules::Check($cycle_date->seminar_id, 'room_time');
- $is_duration_editable = false;
- } else {
- $is_editable = false;
- $is_start_editable = false;
- $is_duration_editable = false;
- }
-
- if ($cycle_date['weekday'] == 0) {
- $day_offset = (7 - $today);
- } else {
- $day_offset = ($cycle_date['weekday'] - $today);
- }
-
- $event_columns = self::getCourseEventcolumns($course);
- $event_colors = self::getCourseEventcolors($course);
-
- $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event');
- $name = $course->getFullName('number-name');
-
- if ($start_time != $cycle_date['start_time'] && $end_time != $cycle_date['end_time']) {
- $start = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['start_time']);
- $end = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['end_time']);
- $backgroundcolor = '#6c737a';
- $textcolor = '#ffffff';
- } else {
- $start = self::iso8601date(strtotime($day_offset . ' days'), $start_time);
- $end = self::iso8601date(strtotime($day_offset . ' days'), $end_time);
-
- $backgroundcolor = null;
- if (array_key_exists($cycle_date->id, $event_colors)) {
- if (is_array($event_colors[$cycle_date->id]) && array_key_exists($institut_id, $event_colors[$cycle_date->id])) {
- $backgroundcolor = $event_colors[$cycle_date->id][$institut_id];
- }
- }
- if (!$backgroundcolor) {
- $backgroundcolor = array_key_exists($semtype['name'], $inst_default_colors)?$inst_default_colors[$semtype['name']]:self::DEFAULT_EVENT_COLOR;
- }
-
- $textcolor = '#ffffff';
- if (self::calculateLuminosityRatio($backgroundcolor, $textcolor) < 3) {
- $textcolor = '#000000';
- }
- }
- if (!$is_editable) {
- $backgroundcolor = '#c4c7c9';
- $textcolor = '#000000';
- }
-
- $resource_column = '0';
- if (array_key_exists($cycle_date->id, $event_columns)) {
- if (is_array($event_columns[$cycle_date->id]) && array_key_exists($institut_id, $event_columns[$cycle_date->id])) {
- $resource_column = $event_columns[$cycle_date->id][$institut_id];
- }
- }
-
- return [
- 'resourceId' => $resource_column,
- 'id' => $cycle_date->id,
- 'title' => $name,
- 'start' => $start,
- 'end' => $end,
- 'textColor' => $textcolor,
- 'backgroundColor' => $backgroundcolor,
- 'borderColor' => '#000',
- 'editable' => $is_editable,
- 'startEditable' => $is_start_editable,
- 'durationEditable' => $is_duration_editable,
- 'resourceEditable' => true,
- 'studip_api_urls' => ['move' => $move_url],
- 'studip_view_urls' => ['edit' => URLHelper::getURL('dispatch.php/course/details/index/' . $cycle_date->seminar_id)],
- 'metadate_id' => $cycle_date->metadate_id,
- 'course_id' => $cycle_date->seminar_id,
- 'tooltip' => self::getCycleInfos($course, $cycle_date),
- 'icon' => $is_start_editable ? '' : 'lock-locked'
- ];
- }
-
- /**
- * Creates a string with course infos to be displayed as a tooltip in calendar events
- *
- * @param SeminarCycleDate $cycle_date
- *
- * @return string enriched course info string for tooltip
- */
- private static function getCycleInfos($course, $cycle_date)
- {
-
- $info_string = $course->getFullName('number-name') . "\n";
-
- $dozenten = [];
- foreach (CourseMember::findByCourseAndStatus($course->id, 'dozent') as $cmember) {
- $dozenten[$cmember->user->user_id] = $cmember->user->getFullName();
- }
- if ($dozenten) {
- $info_string .= implode(', ', $dozenten) . "\n";
- }
-
- $rooms = [];
- foreach ($cycle_date->getAllDates() as $course_date) {
- $room = $course_date->getRoom();
- if ($room) {
- $rooms[$room->id] = $room->name;
- }
- }
- if ($rooms) {
- $info_string .= implode(', ', $rooms) . "\n";
- }
-
- if ($course->getSemClass()->offsetGet('module')) {
- $mvv_pathes = [];
- $course_start = $course->start_time;
- $course_end = ($course->end_time < 0 || is_null($course->end_time))
- ? PHP_INT_MAX
- : $course->end_time;
- // set filter to show only pathes with valid semester data
- ModuleManagementModelTreeItem::setObjectFilter('Modul',
- function ($modul) use ($course_start, $course_end) {
- // check for public status
- if (!$GLOBALS['MVV_MODUL']['STATUS']['values'][$modul->stat]['public']) {
- return false;
- }
- $modul_start = Semester::find($modul->start)->beginn ?: 0;
- $modul_end = Semester::find($modul->end)->ende ?: PHP_INT_MAX;
- return ($modul_start <= $course_end && $modul_end >= $course_start);
- }
- );
-
- ModuleManagementModelTreeItem::setObjectFilter('StgteilVersion',
- function ($version) {
- return (bool) $GLOBALS['MVV_STGTEILVERSION']['STATUS']['values'][$version->stat]['public'];
- }
- );
-
- $trail_classes = ['Modulteil', 'StgteilabschnittModul', 'StgteilAbschnitt', 'StgteilVersion'];
- $mvv_object_pathes = MvvCourse::get($course->getId())->getTrails($trail_classes);
- if ($mvv_object_pathes) {
- if (Config::get()->COURSE_SEM_TREE_DISPLAY) {
- $mvv_tree = [];
- foreach ($mvv_object_pathes as $mvv_object_path) {
- // show only complete pathes
- if (count($mvv_object_path) == 4) {
- // flatten the pathes to a linked list
- $stg = reset($mvv_object_path);
- $parent_id = 'root';
- foreach ($mvv_object_path as $mvv_object) {
- $mvv_object_id = $mvv_object instanceof StgteilabschnittModul
- ? $mvv_object->modul_id
- : $mvv_object->id;
- $mvv_tree[$parent_id][$mvv_object_id] = [
- 'id' => $mvv_object_id,
- 'name' => $mvv_object->getDisplayName(),
- 'class' => get_class($mvv_object),
- ];
- $parent_id = $mvv_object_id;
- }
- }
- }
- if (count($mvv_tree)) {
- // add the root node
- $mvv_tree['start'][] = [
- 'id' => 'root',
- 'name' => Config::get()->UNI_NAME_CLEAN,
- 'class' => ''
- ];
- }
- } else {
- foreach ($mvv_object_pathes as $mvv_object_path) {
- // show only complete pathes
- if (count($mvv_object_path) == 4) {
- $mvv_object_names = [];
- $modul_id = '';
- foreach ($mvv_object_path as $mvv_object) {
- if ($mvv_object instanceof StgteilabschnittModul) {
- $modul_id = $mvv_object->modul_id;
- }
- $mvv_object_names[] = $mvv_object->getDisplayName();
- }
- $mvv_pathes[] = [$modul_id => $mvv_object_names];
- }
- }
- }
- // to prevent collisions of object ids in the tree
- // in the case of same objects listed in more than one part
- // of the tree
- $id_sfx = new stdClass();
- $id_sfx->c = 1;
- }
- foreach ($mvv_pathes as $mvv_path) {
- foreach ($mvv_path as $mvv_path_content) {
- $info_string .= implode(' > ', $mvv_path_content) . "\n";
- }
- }
- }
-
- return $info_string;
- }
-
- public static function getBackgroundEvents($start = null)
- {
- $datetime = new DateTime();
- $slot_duration = 1;
- $start_time = 8;
- $end_time = 16;
- $events = [];
- $day_interval = 1;
-
- if ($start == null) {
- $datetime->modify('monday this week');
- $datetime->add(new DateInterval('PT' . $start_time . 'H'));
- $day_interval = 7;
- }
-
- for ($i = 1; $i <= $day_interval; $i++) {
- for ($slot = $start_time; $slot < $end_time; $slot += $slot_duration) {
- if ($slot % 2) {
- $datetime->setTime($slot, 0);
- $events[] = [
- 'start' => self::iso8601date($datetime, $slot),
- 'end' => self::iso8601date($datetime, $slot + $slot_duration),
- 'rendering' => 'background',
- ];
-
- }
- }
- $datetime->setTime($start_time, 0);
- $datetime->add(new DateInterval('P1D'));
- };
- return $events;
- }
-
- private static function iso8601date($date, $time = '00:00:00', $timezone = '+00:00')
- {
- // If only date parameter is passed and is a DateTimeInterface object or
- // unix timestamp, assume time from date
- if (func_num_args() === 1 && ($date instanceof DateTimeInterface || ctype_digit($date))) {
- $time = $date;
- }
-
- // Get time
- if ($time instanceof DateTimeInterface) {
- $time = $time->format('H:i:s');
- } elseif (ctype_digit($time)) {
- $time = date('H:i:s', $time);
- } elseif (sscanf($time, '%u:%u:%u', $hours, $minutes, $seconds)) {
- $time = sprintf('%02u:%02u:%02u', $hours, $minutes, $seconds);
- }
-
- // Get date
- if ($date instanceof DateTimeInterface) {
- $date = $date->format('Y-m-d');
- } elseif (ctype_digit($date)) {
- $date = date('Y-m-d', $date);
- }
-
- return "{$date}T{$time}{$timezone}";
- }
-
-
- // calculates the luminosity of an given RGB color
- // the color code must be in the format of RRGGBB
- // the luminosity equations are from the WCAG 2 requirements
- // http://www.w3.org/TR/WCAG20/#relativeluminancedef
- private static function calculateLuminosity($color)
- {
- $r = hexdec(substr($color, 0, 2)) / 255; // red value
- $g = hexdec(substr($color, 2, 2)) / 255; // green value
- $b = hexdec(substr($color, 4, 2)) / 255; // blue value
- if ($r <= 0.03928) {
- $r = $r / 12.92;
- } else {
- $r = pow(($r + 0.055) / 1.055, 2.4);
- }
- if ($g <= 0.03928) {
- $g = $g / 12.92;
- } else {
- $g = pow(($g + 0.055) / 1.055, 2.4);
- }
- if ($b <= 0.03928) {
- $b = $b / 12.92;
- } else {
- $b = pow(($b + 0.055) / 1.055, 2.4);
- }
- $luminosity = 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
- return $luminosity;
- }
-
- // calculates the luminosity ratio of two colors
- // the luminosity ratio equations are from the WCAG 2 requirements
- // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
- private static function calculateLuminosityRatio($color1, $color2)
- {
- $c1 = ltrim($color1, '#');
- $c2 = ltrim($color2, '#');
- $l1 = self::calculateLuminosity($c1);
- $l2 = self::calculateLuminosity($c2);
- if ($l1 > $l2) {
- $ratio = ($l1 + 0.05) / ($l2 + 0.05);
- } else {
- $ratio = ($l2 + 0.05) / ($l1 + 0.05);
- }
- return $ratio;
- }
-}
diff --git a/lib/classes/InstituteCalendarHelper.php b/lib/classes/InstituteCalendarHelper.php
new file mode 100644
index 0000000..4302cf1
--- /dev/null
+++ b/lib/classes/InstituteCalendarHelper.php
@@ -0,0 +1,810 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ *
+ */
+
+class InstituteCalendarHelper
+{
+ const COLUMN_DATAFIELD_ID = '69f6485f3c937766866a03d9d642ecbb';
+ const COLOR_DATAFIELD_ID = '41cda2be71fe9efd6e28b853fc0681f3';
+ const INST_DEFAULT_COLOR_DATAFIELD_ID = '0c63321a8e93b3ccc927611709248e07';
+ const DEFAULT_EVENT_COLOR = '#899ab9';
+
+ /**
+ * Returns the default calendar columns.
+ *
+ * @return array default column names
+ */
+ private static function getDefaultColumns()
+ {
+ return [
+ 1 => ['Spalte 1', 1],
+ 2 => ['Spalte 2', 1],
+ 3 => ['Spalte 3', 1],
+ 4 => ['Spalte 4', 1],
+ 5 => ['Spalte 5', 1],
+ 6 => ['Spalte 6', 1]
+ ];
+ }
+
+ /**
+ * Fetches the stores columns merged with defaults.
+ *
+ * @param string $institut_id
+ * @param boolean $only_visible only return visible columns
+ *
+ * @return array column array in a fullcalender expected format
+ */
+ public static function getResourceColumns($institut_id, $only_visible = false)
+ {
+ $columns = [];
+ $inst_columns = [
+ 0 => ['Sammelspalte', 1]
+ ];
+
+ $db_inst_columns = InstitutePlanColumn::findByInstitute($institut_id);
+
+ if ($db_inst_columns) {
+ foreach ($db_inst_columns as $col_info) {
+ $inst_columns[$col_info['column']] = [$col_info['name'], $col_info['visible']];
+ }
+ } else {
+ $inst_columns = array_merge($inst_columns, self::getDefaultColumns());
+ }
+ foreach ($inst_columns as $id => $info) {
+ if ($only_visible && !$info[1]) continue;
+ $columns[] = ['id' => $id, 'title' => $info[0], 'visible' => $info[1]];
+ }
+ return $columns;
+ }
+
+ /**
+ * Adds the default resource columns
+ *
+ * @param string $institut_id
+ *
+ * @return int last resource column number
+ */
+ public static function addDefaultResourceColumns($institut_id)
+ {
+ $max_col = 0;
+ foreach (self::getDefaultColumns() as $col_id => $col_info) {
+ $new_col = new InstitutePlanColumn([$institut_id, $col_id]);
+ $new_col->name = $col_info[0];
+ $new_col->visible = $col_info[1];
+ if ($new_col->store()) {
+ $max_col = $col_id;
+ }
+ }
+ return $max_col;
+ }
+
+ /**
+ * Adds a resource column
+ *
+ * @param string $institut_id
+ * @param string $name
+ * @param int $specific_column_number
+ *
+ * @return int number of affected rows
+ */
+ public static function addResourceColumn($institut_id, $name, $specific_column_number = 0)
+ {
+ $last_col = InstitutePlanColumn::getLastColumnOfInstitute($institut_id);
+ if ($last_col !== null) {
+ $max_col = $last_col->column;
+ } else {
+ $max_col = self::addDefaultResourceColumns($institut_id);
+ }
+ $column_number = $specific_column_number>0?$specific_column_number:intval($max_col)+1;
+ $new_col = new InstitutePlanColumn([$institut_id, $column_number]);
+ $new_col->name = $name;
+ $new_col->visible = 1;
+ return $new_col->store();
+ }
+
+ /**
+ * Looks up column id for course events
+ *
+ * @param Course $course
+ *
+ * @return array course events with column id
+ */
+ public static function getCourseEventcolumns(Course $course)
+ {
+ $df = DatafieldEntryModel::findByModel($course, self::COLUMN_DATAFIELD_ID);
+ if ($df[0] && $df[0]->content) {
+ $event_columns = unserialize($df[0]->content);
+ } else {
+ $event_columns = [];
+ }
+ return $event_columns;
+ }
+
+ /**
+ * Sets the column id for course events
+ *
+ * @param Course $course
+ * @param string $event_id SeminarCycleDate id
+ * @param string $institut_id
+ * @param string $column number of the column
+ *
+ * @return bool stored
+ */
+ public static function setCourseEventcolumn($course, $event_id, $institut_id, $column)
+ {
+ $df = DatafieldEntryModel::findByModel($course, self::COLUMN_DATAFIELD_ID);
+ if ($df[0]) {
+ $event_columns = self::getCourseEventcolumns($course);
+ if (!is_array($event_columns[$event_id])) {
+ unset($event_columns[$event_id]);
+ }
+ $event_columns[$event_id][$institut_id] = $column;
+ $df[0]->content = serialize($event_columns);
+ return $df[0]->store();
+ }
+ return false;
+ }
+
+ /**
+ * Looks up color value for course events
+ *
+ * @param Course $course
+ *
+ * @return array course events with color value
+ */
+ public static function getCourseEventcolors($course)
+ {
+ $df = DatafieldEntryModel::findByModel($course, self::COLOR_DATAFIELD_ID);
+ if ($df[0] && $df[0]->content) {
+ $event_colors = unserialize($df[0]->content);
+ } else {
+ $event_colors = [];
+ }
+ return $event_colors;
+ }
+
+ /**
+ * Sets color value for course events
+ *
+ * @param Course $course
+ * @param string $event_id SeminarCycleDate id
+ * @param string $institut_id
+ * @param string $color colorcode
+ *
+ * @return bool stored
+ */
+ public static function setCourseEventcolor($course, $event_id, $institut_id, $color)
+ {
+ $df = DatafieldEntryModel::findByModel($course, self::COLOR_DATAFIELD_ID);
+ if ($df[0]) {
+ $event_colors = self::getCourseEventcolors($course);
+ if (!is_array($event_colors[$event_id])) {
+ unset($event_colors[$event_id]);
+ }
+ $event_colors[$event_id][$institut_id] = $color;
+ $df[0]->content = serialize($event_colors);
+ return $df[0]->store();
+ }
+ return false;
+ }
+
+ /**
+ * Looks up default color value for institute course events
+ *
+ * @param SimpleORMap $context
+ *
+ * @return array course events with color value
+ */
+ public static function getInstituteDefaultEventcolors($context)
+ {
+ $df = DatafieldEntryModel::findByModel($context, self::INST_DEFAULT_COLOR_DATAFIELD_ID);
+ if ($df && $df[0]->content) {
+ $event_colors = unserialize($df[0]->content);
+ } else {
+ $event_colors = [];
+ }
+ return $event_colors;
+ }
+
+ /**
+ * Sets default institute course events color value for semtypes
+ *
+ * @param SimpleORMap $context
+ * @param string $semtype
+ * @param string $color colorcode
+ *
+ * @return bool stored
+ */
+ public static function setInstituteDefaultEventcolor($context, $semtype, $color)
+ {
+ $df = DatafieldEntryModel::findByModel($context, self::INST_DEFAULT_COLOR_DATAFIELD_ID);
+ if ($df[0]) {
+ $event_colors = self::getInstituteDefaultEventcolors($context);
+ $event_colors[$semtype['name']] = $color;
+ $df[0]->content = serialize($event_colors);
+ return $df[0]->store();
+ }
+ return false;
+ }
+
+ /**
+ * Sets color value for every course events of given courses semtype
+ *
+ * @param Course $course
+ * @param string $institut_id
+ * @param string $color colorcode
+ *
+ * @return bool stored
+ */
+ public static function setSemtypeEventcolor($course, $institut_id, $color)
+ {
+ $semtype = $course->getSemType();
+ $institut = Institute::find($institut_id);
+ if ($institut) {
+ self::setInstituteDefaultEventcolor($institut, $semtype, $color);
+ }
+ $courses = Course::findBySQL('status =? AND Institut_id=?', [$semtype['id'], $course->institut_id]);
+ if ($courses) {
+ foreach ($courses as $semtype_course) {
+ foreach (SeminarCycleDate::findBySeminar($semtype_course->seminar_id) as $cycle_date) {
+ self::setCourseEventcolor($semtype_course, $cycle_date->id, $institut_id, $color);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Prepares an array of course id and names for creation of dropable calendar events
+ *
+ * @param array $courses Array of courses
+ * @param array $semester Semester
+ *
+ * @return array prepared array
+ */
+ public static function getEventlessCourses($courses, $semester = null)
+ {
+ $eventless = [];
+ foreach (array_keys($courses) as $cid) {
+ $course = Course::find($cid);
+ $cycle_dates = SeminarCycleDate::findBySeminar($course->seminar_id);
+ if (count($cycle_dates) < 1) {
+ $eventless[$cid] = $course->getFullName('number-name');
+ } elseif ($semester) {
+ $has_date_in_semester = false;
+ foreach ($cycle_dates as $cycle_date) {
+ foreach ($cycle_date->getAllDates() as $course_date) {
+ if ($course_date->date >= $semester->beginn && $course_date->date <= $semester->ende) {
+ $has_date_in_semester = true;
+ break;
+ }
+ }
+ if ($has_date_in_semester) break;
+ }
+ if (!$has_date_in_semester) {
+ $eventless[$cid] = $course->getFullName('number-name');
+ }
+ }
+ }
+ return $eventless;
+ }
+
+ /**
+ * Creates FullCalendar event date of course events
+ *
+ * @param array $courses Array of courses
+ * @param string $institut_id
+ * @param array $semester Semester
+ * @param array $specific_weekday fetch only events for specific weekday
+ *
+ * @return array fullcalendar events
+ */
+ public static function getEvents($courses, $institut_id, $semester = null, $specific_weekday = null)
+ {
+ $today = date('w');
+
+ $user_insts = array_map(function ($arr) {
+ return $arr['Institut_id'];
+ }, Institute::getMyInstitutes($GLOBALS['user']->id));
+
+ $min_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_START_HOUR);
+ $minbigtime = (int) $min_time[0];
+ $max_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_END_HOUR);
+ $maxbigtime = (int) $max_time[0];
+
+ $institut = Institute::find($institut_id);
+
+ if (!$institut) {
+ return [];
+ }
+
+ $inst_default_colors = self::getInstituteDefaultEventcolors($institut);
+
+ $events = [];
+ Course::findEachMany(function ($course) use (
+ $courses,
+ &$events,
+ $today,
+ $minbigtime,
+ $maxbigtime,
+ $user_insts,
+ $institut_id,
+ $inst_default_colors,
+ $semester,
+ $specific_weekday
+ ) {
+ $semtype = $course->getSemType();
+
+ $event_columns = self::getCourseEventcolumns($course) ?: [];
+ $event_colors = self::getCourseEventcolors($course) ?: [];
+
+ if (in_array($course->institut_id, $user_insts)) {
+ $is_editable = true;
+ $is_start_editable = !LockRules::Check($course->id, 'room_time');
+ $is_duration_editable = false;
+ } else {
+ $is_editable = false;
+ $is_start_editable = false;
+ $is_duration_editable = false;
+ }
+
+ foreach (SeminarCycleDate::findBySeminar($course->seminar_id) as $cycle_date) {
+ if ($semester) {
+ $has_date_in_semester = false;
+ foreach ($cycle_date->getAllDates() as $course_date) {
+ if ($course_date->date >= $semester->beginn && $course_date->date <= $semester->ende) {
+ $has_date_in_semester = true;
+ break;
+ }
+ }
+ if (!$has_date_in_semester) {
+ continue;
+ }
+ }
+
+ if (is_numeric($specific_weekday) && $specific_weekday != $cycle_date['weekday']) {
+ continue;
+ }
+
+ $conform = true;
+
+ if ($cycle_date['weekday'] == 0) {
+ $day_offset = 7 - $today;
+ } else {
+ $day_offset = $cycle_date['weekday'] - $today;
+ }
+
+ $start_time = explode(':', $cycle_date['start_time']);
+ $bigtime = (int) $start_time[0];
+ if ($bigtime > $maxbigtime || $bigtime < $minbigtime) {
+ $conform = false;
+ } elseif ($bigtime % 2) {
+ $bigtime--;
+ }
+ $start_time = $bigtime . ':00:00';
+
+ $end_time = explode(':', $start_time);
+ $end_time[0] += 2;
+ $end_time = implode(':', $end_time);
+
+ $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event');
+ $name = $course->getFullName('number-name');
+
+ if ($start_time != $cycle_date['start_time'] && $end_time != $cycle_date['end_time']) {
+ $start = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['start_time']);
+ $end = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['end_time']);
+ $backgroundcolor = '#6c737a';
+ $textcolor = '#ffffff';
+ } else {
+ $start = self::iso8601date(strtotime($day_offset . ' days'), $start_time);
+ $end = self::iso8601date(strtotime($day_offset . ' days'), $end_time);
+
+ $backgroundcolor = null;
+ if (array_key_exists($cycle_date->id, $event_colors)) {
+ if (is_array($event_colors[$cycle_date->id]) && array_key_exists($institut_id, $event_colors[$cycle_date->id])) {
+ $backgroundcolor = $event_colors[$cycle_date->id][$institut_id];
+ }
+ }
+ if (!$backgroundcolor) {
+ $backgroundcolor = array_key_exists($semtype['name'], $inst_default_colors)
+ ? $inst_default_colors[$semtype['name']]
+ : self::DEFAULT_EVENT_COLOR;
+ }
+
+ $textcolor = '#ffffff';
+ if (self::calculateLuminosityRatio($backgroundcolor, $textcolor) < 3) {
+ $textcolor = '#000000';
+ }
+ }
+ if (!$is_editable) {
+ $backgroundcolor = '#c4c7c9';
+ $textcolor = '#000000';
+ }
+
+ $resource_column = '0';
+ if (array_key_exists($cycle_date->id, $event_columns)) {
+ if (is_array($event_columns[$cycle_date->id]) && array_key_exists($institut_id, $event_columns[$cycle_date->id])) {
+ $resource_column = $event_columns[$cycle_date->id][$institut_id];
+ }
+ }
+
+ $events[] = [
+ 'resourceId' => $resource_column,
+ 'id' => $cycle_date->id,
+ 'title' => $name,
+ 'start' => $start,
+ 'end' => $end,
+ 'textColor' => $textcolor,
+ 'backgroundColor' => $backgroundcolor,
+ 'borderColor' => '#000',
+ 'editable' => $is_editable,
+ 'startEditable' => $is_start_editable,
+ 'durationEditable' => $is_duration_editable,
+ 'resourceEditable' => true,
+ 'studip_api_urls' => ['move' => $move_url],
+ 'studip_view_urls' => ['edit' => URLHelper::getURL('dispatch.php/course/details/index/' . $cycle_date->seminar_id)],
+ 'metadate_id' => $cycle_date->metadate_id,
+ 'course_id' => $cycle_date->seminar_id,
+ 'tooltip' => self::getCycleInfos($course, $cycle_date),
+ 'icon' => $is_start_editable ? '' : 'lock-locked',
+ 'conform' => $conform,
+ ];
+ }
+ }, array_keys($courses));
+
+ return $events;
+ }
+
+ /**
+ * Creates a fullcalendar event of given SeminarCycleDate
+ *
+ * @param SeminarCycleDate $cycle_date
+ * @param string $institut_id
+ *
+ * @return array enriched course info string for tooltip
+ */
+ public static function getCycleEvent($cycle_date, $institut_id)
+ {
+ $course = Course::find($cycle_date->seminar_id);
+ $semtype = $course->getSemType();
+ $institut = Institute::find($institut_id);
+ $inst_default_colors = self::getInstituteDefaultEventcolors($institut);
+
+ $today = date('w');
+ $user_insts = array_map(function ($arr) {
+ return $arr['Institut_id'];
+ }, Institute::getMyInstitutes($GLOBALS['user']->id));
+
+ $min_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_START_HOUR);
+ $minbigtime = (int) $min_time[0];
+ $max_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_END_HOUR);
+ $maxbigtime = (int) $max_time[0];
+
+ $start_time = explode(':', $cycle_date['start_time']);
+ $bigtime = (int) $start_time[0];
+
+ if ($bigtime > $maxbigtime || $bigtime < $minbigtime) {
+ return null;
+ } elseif ($bigtime % 2) {
+ $bigtime--;
+ }
+ $start_time = $bigtime . ':00:00';
+
+ $end_time = explode(':', $start_time);
+ $end_time[0] += 2;
+ $end_time = implode(':', $end_time);
+
+ if (in_array($course->institut_id, $user_insts)) {
+ $is_editable = true;
+ $is_start_editable = !LockRules::Check($cycle_date->seminar_id, 'room_time');
+ $is_duration_editable = false;
+ } else {
+ $is_editable = false;
+ $is_start_editable = false;
+ $is_duration_editable = false;
+ }
+
+ if ($cycle_date['weekday'] == 0) {
+ $day_offset = (7 - $today);
+ } else {
+ $day_offset = ($cycle_date['weekday'] - $today);
+ }
+
+ $event_columns = self::getCourseEventcolumns($course);
+ $event_colors = self::getCourseEventcolors($course);
+
+ $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event');
+ $name = $course->getFullName('number-name');
+
+ if ($start_time != $cycle_date['start_time'] && $end_time != $cycle_date['end_time']) {
+ $start = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['start_time']);
+ $end = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['end_time']);
+ $backgroundcolor = '#6c737a';
+ $textcolor = '#ffffff';
+ } else {
+ $start = self::iso8601date(strtotime($day_offset . ' days'), $start_time);
+ $end = self::iso8601date(strtotime($day_offset . ' days'), $end_time);
+
+ $backgroundcolor = null;
+ if (array_key_exists($cycle_date->id, $event_colors)) {
+ if (is_array($event_colors[$cycle_date->id]) && array_key_exists($institut_id, $event_colors[$cycle_date->id])) {
+ $backgroundcolor = $event_colors[$cycle_date->id][$institut_id];
+ }
+ }
+ if (!$backgroundcolor) {
+ $backgroundcolor = array_key_exists($semtype['name'], $inst_default_colors)?$inst_default_colors[$semtype['name']]:self::DEFAULT_EVENT_COLOR;
+ }
+
+ $textcolor = '#ffffff';
+ if (self::calculateLuminosityRatio($backgroundcolor, $textcolor) < 3) {
+ $textcolor = '#000000';
+ }
+ }
+ if (!$is_editable) {
+ $backgroundcolor = '#c4c7c9';
+ $textcolor = '#000000';
+ }
+
+ $resource_column = '0';
+ if (array_key_exists($cycle_date->id, $event_columns)) {
+ if (is_array($event_columns[$cycle_date->id]) && array_key_exists($institut_id, $event_columns[$cycle_date->id])) {
+ $resource_column = $event_columns[$cycle_date->id][$institut_id];
+ }
+ }
+
+ return [
+ 'resourceId' => $resource_column,
+ 'id' => $cycle_date->id,
+ 'title' => $name,
+ 'start' => $start,
+ 'end' => $end,
+ 'textColor' => $textcolor,
+ 'backgroundColor' => $backgroundcolor,
+ 'borderColor' => '#000',
+ 'editable' => $is_editable,
+ 'startEditable' => $is_start_editable,
+ 'durationEditable' => $is_duration_editable,
+ 'resourceEditable' => true,
+ 'studip_api_urls' => ['move' => $move_url],
+ 'studip_view_urls' => ['edit' => URLHelper::getURL('dispatch.php/course/details/index/' . $cycle_date->seminar_id)],
+ 'metadate_id' => $cycle_date->metadate_id,
+ 'course_id' => $cycle_date->seminar_id,
+ 'tooltip' => self::getCycleInfos($course, $cycle_date),
+ 'icon' => $is_start_editable ? '' : 'lock-locked'
+ ];
+ }
+
+ /**
+ * Creates a string with course infos to be displayed as a tooltip in calendar events
+ *
+ * @param SeminarCycleDate $cycle_date
+ *
+ * @return string enriched course info string for tooltip
+ */
+ private static function getCycleInfos($course, $cycle_date)
+ {
+
+ $info_string = $course->getFullName('number-name') . "\n";
+
+ $dozenten = [];
+ foreach (CourseMember::findByCourseAndStatus($course->id, 'dozent') as $cmember) {
+ $dozenten[$cmember->user->user_id] = $cmember->user->getFullName();
+ }
+ if ($dozenten) {
+ $info_string .= implode(', ', $dozenten) . "\n";
+ }
+
+ $rooms = [];
+ foreach ($cycle_date->getAllDates() as $course_date) {
+ $room = $course_date->getRoom();
+ if ($room) {
+ $rooms[$room->id] = $room->name;
+ }
+ }
+ if ($rooms) {
+ $info_string .= implode(', ', $rooms) . "\n";
+ }
+
+ if ($course->getSemClass()->offsetGet('module')) {
+ $mvv_pathes = [];
+ $course_start = $course->start_time;
+ $course_end = ($course->end_time < 0 || is_null($course->end_time))
+ ? PHP_INT_MAX
+ : $course->end_time;
+ // set filter to show only pathes with valid semester data
+ ModuleManagementModelTreeItem::setObjectFilter('Modul',
+ function ($modul) use ($course_start, $course_end) {
+ // check for public status
+ if (!$GLOBALS['MVV_MODUL']['STATUS']['values'][$modul->stat]['public']) {
+ return false;
+ }
+ $modul_start = Semester::find($modul->start)->beginn ?: 0;
+ $modul_end = Semester::find($modul->end)->ende ?: PHP_INT_MAX;
+ return ($modul_start <= $course_end && $modul_end >= $course_start);
+ }
+ );
+
+ ModuleManagementModelTreeItem::setObjectFilter('StgteilVersion',
+ function ($version) {
+ return (bool) $GLOBALS['MVV_STGTEILVERSION']['STATUS']['values'][$version->stat]['public'];
+ }
+ );
+
+ $trail_classes = ['Modulteil', 'StgteilabschnittModul', 'StgteilAbschnitt', 'StgteilVersion'];
+ $mvv_object_pathes = MvvCourse::get($course->getId())->getTrails($trail_classes);
+ if ($mvv_object_pathes) {
+ if (Config::get()->COURSE_SEM_TREE_DISPLAY) {
+ $mvv_tree = [];
+ foreach ($mvv_object_pathes as $mvv_object_path) {
+ // show only complete pathes
+ if (count($mvv_object_path) == 4) {
+ // flatten the pathes to a linked list
+ $stg = reset($mvv_object_path);
+ $parent_id = 'root';
+ foreach ($mvv_object_path as $mvv_object) {
+ $mvv_object_id = $mvv_object instanceof StgteilabschnittModul
+ ? $mvv_object->modul_id
+ : $mvv_object->id;
+ $mvv_tree[$parent_id][$mvv_object_id] = [
+ 'id' => $mvv_object_id,
+ 'name' => $mvv_object->getDisplayName(),
+ 'class' => get_class($mvv_object),
+ ];
+ $parent_id = $mvv_object_id;
+ }
+ }
+ }
+ if (count($mvv_tree)) {
+ // add the root node
+ $mvv_tree['start'][] = [
+ 'id' => 'root',
+ 'name' => Config::get()->UNI_NAME_CLEAN,
+ 'class' => ''
+ ];
+ }
+ } else {
+ foreach ($mvv_object_pathes as $mvv_object_path) {
+ // show only complete pathes
+ if (count($mvv_object_path) == 4) {
+ $mvv_object_names = [];
+ $modul_id = '';
+ foreach ($mvv_object_path as $mvv_object) {
+ if ($mvv_object instanceof StgteilabschnittModul) {
+ $modul_id = $mvv_object->modul_id;
+ }
+ $mvv_object_names[] = $mvv_object->getDisplayName();
+ }
+ $mvv_pathes[] = [$modul_id => $mvv_object_names];
+ }
+ }
+ }
+ // to prevent collisions of object ids in the tree
+ // in the case of same objects listed in more than one part
+ // of the tree
+ $id_sfx = new stdClass();
+ $id_sfx->c = 1;
+ }
+ foreach ($mvv_pathes as $mvv_path) {
+ foreach ($mvv_path as $mvv_path_content) {
+ $info_string .= implode(' > ', $mvv_path_content) . "\n";
+ }
+ }
+ }
+
+ return $info_string;
+ }
+
+ public static function getBackgroundEvents($start = null)
+ {
+ $datetime = new DateTime();
+ $slot_duration = 1;
+ $start_time = 8;
+ $end_time = 16;
+ $events = [];
+ $day_interval = 1;
+
+ if ($start == null) {
+ $datetime->modify('monday this week');
+ $datetime->add(new DateInterval('PT' . $start_time . 'H'));
+ $day_interval = 7;
+ }
+
+ for ($i = 1; $i <= $day_interval; $i++) {
+ for ($slot = $start_time; $slot < $end_time; $slot += $slot_duration) {
+ if ($slot % 2) {
+ $datetime->setTime($slot, 0);
+ $events[] = [
+ 'start' => self::iso8601date($datetime, $slot),
+ 'end' => self::iso8601date($datetime, $slot + $slot_duration),
+ 'rendering' => 'background',
+ ];
+
+ }
+ }
+ $datetime->setTime($start_time, 0);
+ $datetime->add(new DateInterval('P1D'));
+ };
+ return $events;
+ }
+
+ private static function iso8601date($date, $time = '00:00:00', $timezone = '+00:00')
+ {
+ // If only date parameter is passed and is a DateTimeInterface object or
+ // unix timestamp, assume time from date
+ if (func_num_args() === 1 && ($date instanceof DateTimeInterface || ctype_digit($date))) {
+ $time = $date;
+ }
+
+ // Get time
+ if ($time instanceof DateTimeInterface) {
+ $time = $time->format('H:i:s');
+ } elseif (ctype_digit($time)) {
+ $time = date('H:i:s', $time);
+ } elseif (sscanf($time, '%u:%u:%u', $hours, $minutes, $seconds)) {
+ $time = sprintf('%02u:%02u:%02u', $hours, $minutes, $seconds);
+ }
+
+ // Get date
+ if ($date instanceof DateTimeInterface) {
+ $date = $date->format('Y-m-d');
+ } elseif (ctype_digit($date)) {
+ $date = date('Y-m-d', $date);
+ }
+
+ return "{$date}T{$time}{$timezone}";
+ }
+
+
+ // calculates the luminosity of an given RGB color
+ // the color code must be in the format of RRGGBB
+ // the luminosity equations are from the WCAG 2 requirements
+ // http://www.w3.org/TR/WCAG20/#relativeluminancedef
+ private static function calculateLuminosity($color)
+ {
+ $r = hexdec(substr($color, 0, 2)) / 255; // red value
+ $g = hexdec(substr($color, 2, 2)) / 255; // green value
+ $b = hexdec(substr($color, 4, 2)) / 255; // blue value
+ if ($r <= 0.03928) {
+ $r = $r / 12.92;
+ } else {
+ $r = pow(($r + 0.055) / 1.055, 2.4);
+ }
+ if ($g <= 0.03928) {
+ $g = $g / 12.92;
+ } else {
+ $g = pow(($g + 0.055) / 1.055, 2.4);
+ }
+ if ($b <= 0.03928) {
+ $b = $b / 12.92;
+ } else {
+ $b = pow(($b + 0.055) / 1.055, 2.4);
+ }
+ $luminosity = 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
+ return $luminosity;
+ }
+
+ // calculates the luminosity ratio of two colors
+ // the luminosity ratio equations are from the WCAG 2 requirements
+ // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
+ private static function calculateLuminosityRatio($color1, $color2)
+ {
+ $c1 = ltrim($color1, '#');
+ $c2 = ltrim($color2, '#');
+ $l1 = self::calculateLuminosity($c1);
+ $l2 = self::calculateLuminosity($c2);
+ if ($l1 > $l2) {
+ $ratio = ($l1 + 0.05) / ($l2 + 0.05);
+ } else {
+ $ratio = ($l2 + 0.05) / ($l1 + 0.05);
+ }
+ return $ratio;
+ }
+}
diff --git a/lib/classes/InstituteConfig.class.php b/lib/classes/InstituteConfig.class.php
deleted file mode 100644
index d1a51cb..0000000
--- a/lib/classes/InstituteConfig.class.php
+++ /dev/null
@@ -1,15 +0,0 @@
-
- * @license GPL2 or any later version
- */
-class InstituteConfig extends RangeConfig
-{
- /**
- * range type
- */
- const RANGE_TYPE = 'institute';
-}
diff --git a/lib/classes/InstituteConfig.php b/lib/classes/InstituteConfig.php
new file mode 100644
index 0000000..d1a51cb
--- /dev/null
+++ b/lib/classes/InstituteConfig.php
@@ -0,0 +1,15 @@
+
+ * @license GPL2 or any later version
+ */
+class InstituteConfig extends RangeConfig
+{
+ /**
+ * range type
+ */
+ const RANGE_TYPE = 'institute';
+}
diff --git a/lib/classes/Interactable.class.php b/lib/classes/Interactable.class.php
deleted file mode 100644
index 9796f91..0000000
--- a/lib/classes/Interactable.class.php
+++ /dev/null
@@ -1,204 +0,0 @@
-label = $label;
- $this->attributes = $attributes;
- }
-
- /**
- * Magic method (triggered when invoking inaccessible methods in a static
- * context) used to dynamically create an interactable element with an
- * additional CSSclass. This works for every static method call matching:
- * /^create(.+)/ The matched group is used as CSS class for the
- * interactable element.
- *
- * @code
- * echo Button::createSubmit();
- *
- * # =>
', '
', $html);
- $html = str_replace('', '
', $html);
-
- $purifier = new \HTMLPurifier($config);
- $html = $purifier->purify($html);
-
- // Replace new lines with simple line break; twice because we don't
- // want to create unneccessary white space if a is followed
- // by a new line
- $html = str_replace(' ' . PHP_EOL, PHP_EOL, $html);
- $html = str_replace(' ', PHP_EOL, $html);
-
- $html = \decodeHTML(trim($html));
- }
-
- return $html;
- }
-}
-
-/**
- * Members of Studip\MarkupPrivate must not be used outside of this file!!
- */
-
-namespace Studip\MarkupPrivate\Purifier;
-
-use Studip\MarkupPrivate\MediaProxy;
-
-/**
- * Remove invalid attributes.
- */
-class AttrTransform_Image_Source extends \HTMLPurifier_AttrTransform
-{
- /**
- * Implements abstract method of base class.
- */
- function transform($attr, $config, $context)
- {
- try {
- $attr['src'] = MediaProxy\getMediaUrl($attr['src']);
- } catch (MediaProxy\InvalidInternalLinkException $e) {
- // invalid internal link ==> remove attribute
- $GLOBALS['msg'][] = _('Ungültige interne Medienverknüpfung entfernt: ')
- . \htmlentities($e->getUrl());
- $attr['src'] = NULL; // remove attribute
- } catch (MediaProxy\ExternalMediaDeniedException $e) {
- $GLOBALS['msg'][] = _('Verbotene externe Medienverknüpfung entfernt: ')
- . \htmlentities($e->getUrl());
- $attr['src'] = NULL; // remove attribute
- }
- return $attr;
- }
-}
-
-//// media proxy //////////////////////////////////////////////////////////////
-
-namespace Studip\MarkupPrivate\MediaProxy;
-
-use Studip\MarkupPrivate\Text;
-
-/**
- * Check if media proxy should be used and if so return the respective URL.
- *
- * @param string $url URL to media file.
- * @return mixed URL string to media file (possibly 'proxied')
- * or NULL if URL is invalid.
- */
-function getMediaUrl($url) {
- // even though proxied URLs shouldn't be stored in the database, the
- // next line will handle those cases where they're accidentally there
- $url = decodeMediaProxyUrl($url);
-
- // handle internal media links
- if (isStudipMediaUrl($url)) {
- return transformInternalIdnaLink($url);
- }
- if (isInternalLink($url)) {
- // link is studip-internal, but not to a valid media location
- throw new InvalidInternalLinkException($url);
- }
-
- // handle external media links
- $external_media = \Config::get()->LOAD_EXTERNAL_MEDIA;
- if ($external_media === 'proxy' &&
- \Seminar_Session::is_current_session_authenticated()
- ) {
- // media proxy must be accessed by an internal link
- return encodeMediaProxyUrl($url);
- }
- if ($external_media === 'allow') {
- return $url;
- }
- throw new ExternalMediaDeniedException($url);
-}
-
-/**
- * Return media proxy URL for an unproxied URL.
- *
- * @params string $url Unproxied media URL.
- * @return string Media proxy URL for accessing the same resource.
- */
-function encodeMediaProxyUrl($url) {
- return transformInternalIdnaLink(
- getMediaProxyUrl() .'?url=' . \urlencode(\idna_link($url)));
-}
-
-/**
- * Extract the original URL from a media proxy URL.
- *
- * @param string $url The media proxy URL.
- * return string The original URL. If $url does not point to the media
- * proxy then this is the exact same value given by $url.
- */
-function decodeMediaProxyUrl($url) {
- # TODO make it work for 'url=' at any position in query
- $urlpath = removeStudipDomain($url);
- $proxypath = removeStudipDomain(getMediaProxyUrl()) . '?url=';
- if (Text\startsWith($urlpath, $proxypath)) {
- return \urldecode(Text\removePrefix($urlpath, $proxypath));
- }
- return $url;
-}
-
-/**
- * Return Stud.IP's absolute media proxy URL.
- */
-function getMediaProxyUrl() {
- return $GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/media_proxy';
-}
-
-/**
- * Test if an URL points to a valid internal Stud.IP media path.
- *
- * @param string $url Internal Stud.IP URL.
- * @returns boolean TRUE for internal media link URLs, FALSE otherwise.
- */
-function isStudipMediaUrl($url) {
- return isInternalLink($url) &&
- isStudipMediaUrlPath(getStudipRelativePath($url));
-}
-
-function isInternalLink($url) {
- return is_internal_url(transformInternalIdnaLink($url));
-}
-
-//// url utilities ////////////////////////////////////////////////////////////
-
-/**
- * Remove domain name from internal URLs.
- *
- * Remove scheme, domain and authentication information from internal
- * Stud.IP URLs. Leave external URLs untouched.
- *
- * @param string $url URL from which to remove internal domain.
- * @returns string URL without internal domain or the exact same
- * value as $url for external URLs.
- */
-function removeStudipDomain($url) {
- if (!isInternalLink($url)) {
- return $url;
- }
- $parsed_url = \parse_url(transformInternalIdnaLink($url));
- $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
- $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
- $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
- return $path . $query . $fragment;
-}
-
-/**
- * Return a URL's path component with the absolute Stud.IP path removed.
- *
- * NOTE: If the URL is not an internal Stud.IP URL, the path component will
- * nevertheless be returned without issuing an error message.
- *
- * Example:
- * >>> getStudipRelativePath('http://localhost:8080'
- * . '/studip/sendfile.php?type=0&file_id=ABC123&file_name=nice.jpg')
- * 'sendfile.php'
- *
- * @param string $url The URL from which to return the Stud.IP-relative
- * path component.
- * returns string Stud.IP-relative path component of $url.
- */
-function getStudipRelativePath($url) {
- $parsed_url = \parse_url(transformInternalIdnaLink($url));
- $parsed_studip_url = getParsedStudipUrl();
- return Text\removePrefix($parsed_url['path'], $parsed_studip_url['path']);
-}
-
-/**
- * Return an associative array containing the Stud.IP URL elements.
- *
- * see also: http://php.net/manual/en/function.parse-url.php
- *
- * @returns mixed Same values that PHP's parse_url() returns.
- */
-function getParsedStudipUrl() {
- return \parse_url($GLOBALS['ABSOLUTE_URI_STUDIP']);
-}
-
-/**
- * Test if path is valid for internal Stud.IP media URLs.
- *
- * @params string $path The path component of an URL.
- * return boolean TRUE for valid media paths, FALSE otherwise.
- */
-function isStudipMediaUrlPath($path) {
- list($path_head) = \explode('/', $path);
- $valid_paths = ['sendfile.php', 'download', 'assets', 'pictures'];
- return \mb_strpos(\urldecode($path), '../') === false && \in_array($path_head, $valid_paths);
-}
-
-/**
- * Return a normalized, internal URL.
- *
- * @params string $url An internal URL.
- * @returns string Normalized internal URL.
- */
-function transformInternalIdnaLink($url) {
- return \idna_link(\TransformInternalLinks($url));
-}
-
-//// url exceptions ///////////////////////////////////////////////////////////
-
-class UrlException extends \Exception
-{
- private $url;
-
- public function __construct($url) {
- parent::__construct();
- $this->url = $url;
- }
-
- public function getUrl()
- {
- return $this->url;
- }
-}
-
-class InvalidInternalLinkException extends UrlException
-{
-}
-
-class ExternalMediaDeniedException extends UrlException
-{
-}
-
-//// string utilities /////////////////////////////////////////////////////////
-
-namespace Studip\MarkupPrivate\Text;
-
-/**
- * Test if string starts with prefix.
- *
- * @param string $string Tested string.
- * @param string $prefix Prefix of tested string.
- *
- * @return boolean TRUE if string starts with prefix.
- */
-function startsWith($string, $prefix) {
- return \mb_substr($string, 0, \mb_strlen($prefix)) === $prefix;
-}
-
-/**
- * Test if string ends with suffix.
- *
- * @param string $string Tested string.
- * @param string $suffix Suffix of tested string.
- *
- * @return boolean TRUE if string ends with suffix.
- */
-function endsWith($string, $suffix) {
- return \mb_substr($string, - \mb_strlen($suffix)) === $suffix;
-}
-
-/**
- * Remove prefix from string.
- *
- * Does not change the string if it has a different prefix.
- *
- * @param string $string The string that must start with the prefix.
- * @param string $prefix The prefix of the string.
- *
- * @return string String without prefix.
- */
-function removePrefix($string, $prefix) {
- return startsWith($string, $prefix) ? \mb_substr($string, \mb_strlen($prefix)) : $string;
-}
diff --git a/lib/classes/Markup.php b/lib/classes/Markup.php
new file mode 100644
index 0000000..fbde67b
--- /dev/null
+++ b/lib/classes/Markup.php
@@ -0,0 +1,788 @@
+
+ */
+namespace Studip;
+
+require_once __DIR__ . '/htmlpurifier/HTMLPurifier_Injector_ClassifyLinks.php';
+require_once __DIR__ . '/htmlpurifier/HTMLPurifier_Injector_ClassifyTables.php';
+require_once __DIR__ . '/htmlpurifier/HTMLPurifier_Injector_LinkifyEmail.php';
+require_once __DIR__ . '/htmlpurifier/HTMLPurifier_Injector_TransformLinks.php';
+require_once __DIR__ . '/htmlpurifier/HTMLPurifier_Injector_Unlinkify.php';
+
+class Markup
+{
+ /**
+ * Apply markup rules and clean the text up.
+ *
+ * @param TextFormat $markup Markup rules applied on marked-up text.
+ * @param string $text Marked-up text on which rules are applied.
+ * @param boolean $trim Trim text before applying markup rules, if TRUE.
+ *
+ * @return string HTML code computed from marked-up text.
+ */
+ public static function apply($markup, $text, $trim)
+ {
+ return $markup->format(self::markupToHtml($text, $trim, false));
+ }
+
+ // signature for HTML entries
+ const HTML_MARKER = '';
+
+ // signature for HTML fallback entries
+ const HTML_MARKER_FALLBACK = '';
+
+ // regular expression for detecting HTML signature
+ const HTML_MARKER_REGEXP = '/^\s*/i';
+
+ /**
+ * Return `true` if the WYSIWYG editor is enabled for this user.
+ * @deprecated since Stud.IP 5.5
+ *
+ * @return boolean always returns `true`.
+ */
+ public static function editorEnabled()
+ {
+ return true;
+ }
+
+ /**
+ * Return `true` for HTML code and `false` for plain text.
+ *
+ * HTML code must either match `HTML_MARKER_REGEXP` or begin
+ * with '<' and end with '>' (leading and trailing whitespace
+ * is ignored). Everything else is considered to be plain
+ * text.
+ *
+ * @param string $text HTML code or plain text.
+ *
+ * @return boolean `true` for HTML code, `false` for plain text.
+ */
+ public static function isHtml($text)
+ {
+ return self::hasHtmlMarker($text);
+ }
+
+ /**
+ * Return `true` for Stud.IP-HTML and `false` otherwise.
+ *
+ * Stud.IP-HTML is HTML that can contain Stud.IP Markup.
+ *
+ * Stud.IP-HTML must match Stud.IP 3.2's HTML marker.
+ * Leading and trailing whitespace is ignored.
+ *
+ * Everything else is considered not Stud.IP-HTML. In other
+ * words, if it's not Stud.IP-HTML it might be everything
+ * from plain text to binary code. But usually it's either
+ * Stud.IP markup or plain HTML code, then.
+ *
+ * @param string $text Text that is or isn't Stud.IP-HTML.
+ *
+ * @return boolean `true` for Stud.IP-HTML
+ */
+ public static function isHtmlFallback($text)
+ {
+ $text = trim($text);
+
+ // it's not fallback if the new HTML marker is detected
+ if (MarkupPrivate\Text\startsWith($text, self::HTML_MARKER)) {
+ return false;
+ }
+
+ // it's Stud.IP-HTML if Stud.IP 3.2's HTML marker is detected
+ if (MarkupPrivate\Text\startsWith($text, self::HTML_MARKER_FALLBACK)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return `true` for HTML code and `false` for plain text.
+ *
+ * HTML code must start with a match for `HTML_MARKER_REGEXP`.
+ *
+ * @param string $text HTML code or plain text.
+ *
+ * @return boolean `true` for HTML code, `false` for plain text.
+ */
+ public static function hasHtmlMarker($text)
+ {
+ return preg_match(self::HTML_MARKER_REGEXP, $text);
+ }
+
+ /**
+ * Mark a given text as HTML code.
+ *
+ * No sanity-checking is done on the given text. It is simply
+ * marked up so to be identified by Markup::isHtml as HTML
+ * code.
+ *
+ * @param string $text The text to be marked up as HTML code.
+ *
+ * @return string The text marked up as HTML code.
+ */
+ public static function markAsHtml($text)
+ {
+ // NOTE keep this function in sync with the JavaScript
+ // function markAsHtml in WyswygHtmlHead.php
+ if (self::hasHtmlMarker($text) || trim($text) === '') {
+ return $text; // marker already set, don't set twice
+ }
+ return self::HTML_MARKER . $text;
+ }
+
+ /**
+ * Apply markup rules after running text through HTML ready.
+ *
+ * @param TextFormat $markup Markup rules applied on marked-up text.
+ * @param string $text Marked-up text on which rules are applied.
+ * @param boolean $trim Trim text before applying markup rules, if TRUE.
+ *
+ * @return string HTML code computed from marked-up text.
+ */
+ private static function markupHtmlReady($markup, $text, $trim)
+ {
+ return str_replace("\n", ' ', self::markupText(
+ $markup, self::htmlReady(self::unixEOL($text), $trim)));
+ }
+
+ /**
+ * Convert line break to Unix format.
+ *
+ * @param string $text Text with possibly mixed line breaks (Win, Mac, Unix).
+ *
+ * @return string Text with Unix line breaks only.
+ */
+ private static function unixEOL($text)
+ {
+ return preg_replace("/\r\n?/", "\n", $text);
+ }
+
+ /**
+ * Apply markup rules on plain text.
+ *
+ * @param TextFormat $markup Markup rules applied on marked-up text.
+ * @param string $text Marked-up text on which rules are applied.
+ *
+ * @return string HTML code computed from marked-up text.
+ */
+ private static function markupText($markup, $text)
+ {
+ return symbol($markup->format($text));
+ }
+
+ /**
+ * Call HTMLPurifier to create safe HTML.
+ *
+ * @param string $dirty_html Unsafe or 'uncleaned' HTML code.
+ * @param boolean $autoformat Apply the AutoFormat rules
+ * @return string Clean and safe HTML code.
+ */
+ private static function purify($dirty_html, $autoformat = true)
+ {
+ $purifier = self::createPurifier($autoformat);
+
+ return $purifier->purify($dirty_html);
+ }
+
+ /**
+ * Call HTMLPurifier to filter the HTML code (if the source is detected
+ * to contain HTML, returns the argument unchanged otherwise). The HTML
+ * marker is restored afterwards, if it was present.
+ *
+ * @param string $dirty_html Unsafe or 'uncleaned' HTML code.
+ * @return string Clean and safe HTML code.
+ */
+ public static function purifyHtml($html)
+ {
+ if ($html instanceof \I18NString) {
+ $base = self::purifyHtml($html->original());
+ $lang = $html->toArray();
+
+ foreach ($lang as &$value) {
+ $value = self::purifyHtml($value);
+ }
+
+ return new \I18NString($base, $lang);
+ }
+
+ if (self::isHtml($html)) {
+ $html = self::markAsHtml(self::purify($html));
+ }
+
+ return $html;
+ }
+
+ /**
+ * Create HTML purifier instance with Stud.IP-specific configuration.
+ *
+ * @param boolean $autoformat Apply the AutoFormat rules
+ * @return \HTMLPurifier A new instance of the HTML purifier.
+ */
+ private static function createPurifier($autoformat)
+ {
+ $config = \HTMLPurifier_Config::createDefault();
+ $config->set('Cache.SerializerPath', $GLOBALS['TMP_PATH']);
+ $config->set('Core.RemoveInvalidImg', true);
+
+ // restrict allowed HTML tags and attributes
+ //
+ // note that changes here should also be reflected in CKEditor's
+ // settings!!
+ //
+ // NOTE The list could be restricted even further by allowing only
+ // specific values for some attributes and CSS styles, but that is not
+ // directly supported by HTMLPurifier and would need to be implemented
+ // with a filter similar to ClassifyLinks.
+ //
+ // This is a list of further restrictions that can/should be introduced
+ // at a later time point maybe, if possible:
+ //
+ // - always open external links in a new tab or window
+ // a[class="link-extern" href="..." target="_blank"]
+ // - only allow left margin and horizontal text alignment to be set in
+ // divs (NOTE maybe remove these two features completely?):
+ // div[style="margin-left:(40|80|...)px; text-align:(center|right|justify)"]
+ // - img[style] should only allow float:left or float:right
+ // - only allow text color and background color to be set in a span's
+ // style attribute (NOTE 'wiki-links' are currently set here due to
+ // implementation difficulties, but probably this should be
+ // changed...):
+ // span[style="color:(#000000|#800000|...);
+ // background-color:(#000000|#800000|...)"
+ // class="wiki-link"]
+ // - tables should always have the class "content" (it should not be
+ // optional and no other class should be set):
+ // table[class="content"]
+ // - table headings should have a column and/or a row scope or no scope
+ // at all, but nothing else:
+ // th[scope="(col | row)"]
+ // - fonts: only Stud.IP-specific fonts should be allowed
+ //
+ $config->set('HTML.Allowed', '
+ a[class|href|target|rel|name|id]
+ audio[controls|src|height|width|style]
+ big
+ blockquote
+ br
+ caption
+ code[class]
+ div[class|style]
+ em
+ figure[class|style]
+ figcaption
+ h1
+ h2
+ h3
+ h4
+ h5
+ h6
+ hr
+ i
+ img[alt|src|height|width|class|style]
+ li
+ ol[reversed|start|style]
+ p[style]
+ pre[class]
+ span[style|class]
+ strong
+ u
+ ul[style]
+ s
+ small
+ sub
+ sup
+ table[class|style]
+ tbody
+ td[colspan|rowspan|style]
+ thead
+ th[colspan|rowspan|style|scope]
+ tr
+ tt
+ video[controls|src|height|width|style]
+ ');
+
+ $config->set('Attr.AllowedFrameTargets', ['_blank']);
+ $config->set('Attr.AllowedRel', ['nofollow']);
+ $config->set('Attr.EnableID', true);
+ $config->set('Attr.AllowedClasses', [
+ 'author',
+ 'content',
+ 'image',
+ 'image-style-side',
+ 'image_resized',
+ 'language-cpp',
+ 'language-css',
+ 'language-diff',
+ 'language-java',
+ 'language-javascript',
+ 'language-json',
+ 'language-php',
+ 'language-python',
+ 'language-ruby',
+ 'language-scss',
+ 'language-sql',
+ 'language-xml',
+ 'link-extern',
+ 'link-intern',
+ 'math-tex',
+ 'table',
+ 'usercode',
+ 'wiki-link'
+ ]);
+ $config->set('CSS.AllowedFonts', [
+ 'serif',
+ 'sans-serif',
+ 'monospace',
+ 'cursive'
+ ]);
+ $config->set('CSS.AllowedProperties', [
+ 'margin-left',
+ 'text-align',
+ 'width',
+ 'height',
+ 'color',
+ 'background-color', // needed by span, td
+ 'border-color',
+ 'border-style',
+ 'float',
+ 'border',
+ 'vertical-align'
+ ]);
+ $config->set('CSS.MaxImgLength', null);
+
+ if ($autoformat) {
+ $config->set('AutoFormat.Linkify', true);
+ $config->set('AutoFormat.Custom', [
+ 'ClassifyLinks',
+ 'ClassifyTables',
+ 'LinkifyEmail'
+ ]);
+ $config->set('AutoFormat.RemoveSpansWithoutAttributes', true);
+ } else {
+ $config->set('AutoFormat.Custom', ['TransformLinks']);
+ }
+
+ // avoid
+ $def = $config->getHTMLDefinition(true);
+ $img = $def->addBlankElement('img');
+ $img->attr_transform_post[]
+ = new MarkupPrivate\Purifier\AttrTransform_Image_Source();
+
+ $def->addElement('audio', 'Inline', 'Flow', 'Common', [
+ 'src*' => 'URI',
+ 'width' => 'Length',
+ 'height' => 'Length',
+ 'controls' => 'Text', // Bool triggers bug in HTMLPurifier
+ ]);
+
+ $def->addElement('video', 'Inline', 'Flow', 'Common', [
+ 'src*' => 'URI',
+ 'width' => 'Length',
+ 'height' => 'Length',
+ 'controls' => 'Text', // Bool triggers bug in HTMLPurifier
+ ]);
+
+ $def->addElement('figcaption', 'Inline', 'Flow', 'Common');
+ $def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common');
+
+ $def->addAttribute('ol', 'reversed', 'Bool');
+ $def->addAttribute('ol', 'style', 'Text');
+ $def->addAttribute('ul', 'style', 'Text');
+
+ return new \HTMLPurifier($config);
+ }
+
+ /**
+ * Convert special characters to HTML entities, and clean up.
+ *
+ * @param string $text This text's special chars will be converted.
+ * @param boolean $trim Trim text before applying markup rules, if TRUE.
+ * @param boolean $br Replace newlines by , if TRUE.
+ * @param boolean $double_encode Encode existing HTML entities, if TRUE.
+ * @return string The converted string.
+ */
+ public static function htmlReady(
+ $text, $trim = true, $br = false, $double_encode = true
+ ) {
+ $text = htmlspecialchars($text, ENT_QUOTES, 'utf-8', $double_encode);
+
+ if ($trim) {
+ $text = trim($text);
+ }
+ if ($br) { // fix newlines
+ $text = nl2br($text, false);
+ }
+ return $text;
+ }
+
+ /**
+ * Prepare text for wysiwyg (if enabled), otherwise convert special
+ * characters using htmlReady.
+ *
+ * @param string $text The text.
+ * @param boolean $trim Trim text before applying markup rules, if TRUE.
+ * @param boolean $br Replace newlines by , if TRUE and wysiwyg editor disabled.
+ * @param boolean $double_encode Encode existing HTML entities, if TRUE and wysiwyg editor disabled.
+ * @return string The converted string.
+ */
+ public static function wysiwygReady(
+ $text, $trim = true, $br = false, $double_encode = true
+ ) {
+ if (self::editorEnabled()) {
+ $text = self::markupToHtml($text, $trim);
+ }
+ return self::htmlReady($text, $trim, $br, $double_encode);
+ }
+
+ /**
+ * Convert Stud.IP markup (possibly mixed with HTML if fallback mode is
+ * enabled) to editable HTML. Pure HTML will only run through the purifier.
+ *
+ * @param string $text The text.
+ * @param boolean $trim Trim text before applying markup rules, if TRUE.
+ * @param boolean $mark Mark result text as HTML, if TRUE.
+ * @return string The converted string.
+ */
+ public static function markupToHtml($text, $trim = true, $mark = true)
+ {
+ if (!trim($text)) {
+ return $text;
+ }
+ if (self::isHtml($text)) {
+ $is_fallback = self::isHtmlFallback($text);
+ $text = self::purify($text, false);
+
+ if ($is_fallback) {
+ $text = self::markupText(new \StudipCoreFormat(), $text);
+ }
+ } else {
+ $text = self::markupHtmlReady(new \StudipCoreFormat(), $text, $trim);
+ }
+
+ return $mark ? self::markAsHtml($text) : $text;
+ }
+
+ /**
+ * Call HTMLPurifier to remove all HTML tags from the string (if the source
+ * is detected to contain HTML, returns the argument unchanged otherwise).
+ *
+ * @param string $html HTML code to filter
+ * @return string The converted string.
+ */
+ public static function removeHtml($html)
+ {
+ if (self::isHtml($html)) {
+ $config = \HTMLPurifier_Config::createDefault();
+ $config->set('Cache.SerializerPath', $GLOBALS['TMP_PATH']);
+ $config->set('HTML.Allowed', 'a[href],img[alt|src],br');
+ $config->set('AutoFormat.Custom', ['Unlinkify']);
+
+ $html = str_replace('', ' ', $html);
+ $html = str_replace('', ' ', $html);
+ $html = str_replace('', ' ', $html);
+ $html = str_replace('', ' ', $html);
+ $html = str_replace('', '
', $html);
+ $html = str_replace('', '
', $html);
+
+ $purifier = new \HTMLPurifier($config);
+ $html = $purifier->purify($html);
+
+ // Replace new lines with simple line break; twice because we don't
+ // want to create unneccessary white space if a is followed
+ // by a new line
+ $html = str_replace(' ' . PHP_EOL, PHP_EOL, $html);
+ $html = str_replace(' ', PHP_EOL, $html);
+
+ $html = \decodeHTML(trim($html));
+ }
+
+ return $html;
+ }
+}
+
+/**
+ * Members of Studip\MarkupPrivate must not be used outside of this file!!
+ */
+
+namespace Studip\MarkupPrivate\Purifier;
+
+use Studip\MarkupPrivate\MediaProxy;
+
+/**
+ * Remove invalid attributes.
+ */
+class AttrTransform_Image_Source extends \HTMLPurifier_AttrTransform
+{
+ /**
+ * Implements abstract method of base class.
+ */
+ function transform($attr, $config, $context)
+ {
+ try {
+ $attr['src'] = MediaProxy\getMediaUrl($attr['src']);
+ } catch (MediaProxy\InvalidInternalLinkException $e) {
+ // invalid internal link ==> remove attribute
+ $GLOBALS['msg'][] = _('Ungültige interne Medienverknüpfung entfernt: ')
+ . \htmlentities($e->getUrl());
+ $attr['src'] = NULL; // remove attribute
+ } catch (MediaProxy\ExternalMediaDeniedException $e) {
+ $GLOBALS['msg'][] = _('Verbotene externe Medienverknüpfung entfernt: ')
+ . \htmlentities($e->getUrl());
+ $attr['src'] = NULL; // remove attribute
+ }
+ return $attr;
+ }
+}
+
+//// media proxy //////////////////////////////////////////////////////////////
+
+namespace Studip\MarkupPrivate\MediaProxy;
+
+use Studip\MarkupPrivate\Text;
+
+/**
+ * Check if media proxy should be used and if so return the respective URL.
+ *
+ * @param string $url URL to media file.
+ * @return mixed URL string to media file (possibly 'proxied')
+ * or NULL if URL is invalid.
+ */
+function getMediaUrl($url) {
+ // even though proxied URLs shouldn't be stored in the database, the
+ // next line will handle those cases where they're accidentally there
+ $url = decodeMediaProxyUrl($url);
+
+ // handle internal media links
+ if (isStudipMediaUrl($url)) {
+ return transformInternalIdnaLink($url);
+ }
+ if (isInternalLink($url)) {
+ // link is studip-internal, but not to a valid media location
+ throw new InvalidInternalLinkException($url);
+ }
+
+ // handle external media links
+ $external_media = \Config::get()->LOAD_EXTERNAL_MEDIA;
+ if ($external_media === 'proxy' &&
+ \Seminar_Session::is_current_session_authenticated()
+ ) {
+ // media proxy must be accessed by an internal link
+ return encodeMediaProxyUrl($url);
+ }
+ if ($external_media === 'allow') {
+ return $url;
+ }
+ throw new ExternalMediaDeniedException($url);
+}
+
+/**
+ * Return media proxy URL for an unproxied URL.
+ *
+ * @params string $url Unproxied media URL.
+ * @return string Media proxy URL for accessing the same resource.
+ */
+function encodeMediaProxyUrl($url) {
+ return transformInternalIdnaLink(
+ getMediaProxyUrl() .'?url=' . \urlencode(\idna_link($url)));
+}
+
+/**
+ * Extract the original URL from a media proxy URL.
+ *
+ * @param string $url The media proxy URL.
+ * return string The original URL. If $url does not point to the media
+ * proxy then this is the exact same value given by $url.
+ */
+function decodeMediaProxyUrl($url) {
+ # TODO make it work for 'url=' at any position in query
+ $urlpath = removeStudipDomain($url);
+ $proxypath = removeStudipDomain(getMediaProxyUrl()) . '?url=';
+ if (Text\startsWith($urlpath, $proxypath)) {
+ return \urldecode(Text\removePrefix($urlpath, $proxypath));
+ }
+ return $url;
+}
+
+/**
+ * Return Stud.IP's absolute media proxy URL.
+ */
+function getMediaProxyUrl() {
+ return $GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/media_proxy';
+}
+
+/**
+ * Test if an URL points to a valid internal Stud.IP media path.
+ *
+ * @param string $url Internal Stud.IP URL.
+ * @returns boolean TRUE for internal media link URLs, FALSE otherwise.
+ */
+function isStudipMediaUrl($url) {
+ return isInternalLink($url) &&
+ isStudipMediaUrlPath(getStudipRelativePath($url));
+}
+
+function isInternalLink($url) {
+ return is_internal_url(transformInternalIdnaLink($url));
+}
+
+//// url utilities ////////////////////////////////////////////////////////////
+
+/**
+ * Remove domain name from internal URLs.
+ *
+ * Remove scheme, domain and authentication information from internal
+ * Stud.IP URLs. Leave external URLs untouched.
+ *
+ * @param string $url URL from which to remove internal domain.
+ * @returns string URL without internal domain or the exact same
+ * value as $url for external URLs.
+ */
+function removeStudipDomain($url) {
+ if (!isInternalLink($url)) {
+ return $url;
+ }
+ $parsed_url = \parse_url(transformInternalIdnaLink($url));
+ $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
+ $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
+ $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
+ return $path . $query . $fragment;
+}
+
+/**
+ * Return a URL's path component with the absolute Stud.IP path removed.
+ *
+ * NOTE: If the URL is not an internal Stud.IP URL, the path component will
+ * nevertheless be returned without issuing an error message.
+ *
+ * Example:
+ * >>> getStudipRelativePath('http://localhost:8080'
+ * . '/studip/sendfile.php?type=0&file_id=ABC123&file_name=nice.jpg')
+ * 'sendfile.php'
+ *
+ * @param string $url The URL from which to return the Stud.IP-relative
+ * path component.
+ * returns string Stud.IP-relative path component of $url.
+ */
+function getStudipRelativePath($url) {
+ $parsed_url = \parse_url(transformInternalIdnaLink($url));
+ $parsed_studip_url = getParsedStudipUrl();
+ return Text\removePrefix($parsed_url['path'], $parsed_studip_url['path']);
+}
+
+/**
+ * Return an associative array containing the Stud.IP URL elements.
+ *
+ * see also: http://php.net/manual/en/function.parse-url.php
+ *
+ * @returns mixed Same values that PHP's parse_url() returns.
+ */
+function getParsedStudipUrl() {
+ return \parse_url($GLOBALS['ABSOLUTE_URI_STUDIP']);
+}
+
+/**
+ * Test if path is valid for internal Stud.IP media URLs.
+ *
+ * @params string $path The path component of an URL.
+ * return boolean TRUE for valid media paths, FALSE otherwise.
+ */
+function isStudipMediaUrlPath($path) {
+ list($path_head) = \explode('/', $path);
+ $valid_paths = ['sendfile.php', 'download', 'assets', 'pictures'];
+ return \mb_strpos(\urldecode($path), '../') === false && \in_array($path_head, $valid_paths);
+}
+
+/**
+ * Return a normalized, internal URL.
+ *
+ * @params string $url An internal URL.
+ * @returns string Normalized internal URL.
+ */
+function transformInternalIdnaLink($url) {
+ return \idna_link(\TransformInternalLinks($url));
+}
+
+//// url exceptions ///////////////////////////////////////////////////////////
+
+class UrlException extends \Exception
+{
+ private $url;
+
+ public function __construct($url) {
+ parent::__construct();
+ $this->url = $url;
+ }
+
+ public function getUrl()
+ {
+ return $this->url;
+ }
+}
+
+class InvalidInternalLinkException extends UrlException
+{
+}
+
+class ExternalMediaDeniedException extends UrlException
+{
+}
+
+//// string utilities /////////////////////////////////////////////////////////
+
+namespace Studip\MarkupPrivate\Text;
+
+/**
+ * Test if string starts with prefix.
+ *
+ * @param string $string Tested string.
+ * @param string $prefix Prefix of tested string.
+ *
+ * @return boolean TRUE if string starts with prefix.
+ */
+function startsWith($string, $prefix) {
+ return \mb_substr($string, 0, \mb_strlen($prefix)) === $prefix;
+}
+
+/**
+ * Test if string ends with suffix.
+ *
+ * @param string $string Tested string.
+ * @param string $suffix Suffix of tested string.
+ *
+ * @return boolean TRUE if string ends with suffix.
+ */
+function endsWith($string, $suffix) {
+ return \mb_substr($string, - \mb_strlen($suffix)) === $suffix;
+}
+
+/**
+ * Remove prefix from string.
+ *
+ * Does not change the string if it has a different prefix.
+ *
+ * @param string $string The string that must start with the prefix.
+ * @param string $prefix The prefix of the string.
+ *
+ * @return string String without prefix.
+ */
+function removePrefix($string, $prefix) {
+ return startsWith($string, $prefix) ? \mb_substr($string, \mb_strlen($prefix)) : $string;
+}
diff --git a/lib/classes/MessageBox.class.php b/lib/classes/MessageBox.class.php
deleted file mode 100644
index 8936ebe..0000000
--- a/lib/classes/MessageBox.class.php
+++ /dev/null
@@ -1,177 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL Licence 2
- * @category Stud.IP
- * @package layout
- * @since 1.10
- *
- */
-
-/**
- * class MessageBox
- *
- * usage:
- *
- * echo MessageBox::error('Nachricht', ['optional details']);
- *
- * use the optional parameter $close_details for displaying the message box with
- * closed details
- *
- * echo MessageBox::success('Nachricht', ['optional details'], true);
- *
- */
-class MessageBox implements LayoutMessage
-{
- /**
- * type and contents of the message box
- */
- public $class;
- public $message;
- public $details;
- public $close_details;
- protected $hide_close = false;
- public static $counter = 0;
-
- /**
- * This function returns an exception message box. Use it only for system errors
- * or security related problems.
- *
- * @param string $message
- * @param array $details
- * @param boolean $close_details
- * @return object MessageBox object
- */
- public static function exception($message, $details = [], $close_details = false)
- {
- return new MessageBox('exception', $message, $details, $close_details);
- }
-
- /**
- * This function returns an error message box. Use it for validation errors,
- * problems and other wrong user input.
- *
- * @param string $message
- * @param array $details (optional)
- * @param boolean $close_details (optional)
- * @return object MessageBox object
- */
- public static function error($message, $details = [], $close_details = false)
- {
- return new MessageBox('error', $message, $details, $close_details);
- }
-
- /**
- * This function returns a success message box. Use it for confirmation of user
- * interaction.
- *
- * @param string $message
- * @param array $details (optional)
- * @param boolean $close_details (optional)
- * @return object MessageBox object
- */
- public static function success($message, $details = [], $close_details = false)
- {
- return new MessageBox('success', $message, $details, $close_details);
- }
-
- /**
- * This function returns an info message box. Use it to report neutral
- * informations.
- *
- * @param string $message
- * @param array $details (optional)
- * @param boolean $close_details (optional)
- * @return object MessageBox object
- */
- public static function info($message, $details = [], $close_details = false)
- {
- return new MessageBox('info', $message, $details, $close_details);
- }
-
- /**
- * This function returns a warning message box. Use it to report potentially
- * wrong behaviour.
- *
- * @param string $message
- * @param array $details (optional)
- * @param boolean $close_details (optional)
- * @return object MessageBox object
- */
- public static function warning($message, $details = [], $close_details = false)
- {
- return new MessageBox('warning', $message, $details, $close_details);
- }
-
- /**
- * Initializes a new MessageBox object of the given class.
- *
- * @param string $class the type of this message
- * @param string $message
- * @param array $details (optional)
- * @param boolean $close_details (optional)
- */
- protected function __construct($class, $message, $details = [], $close_details = false)
- {
- $this->class = $class;
- $this->message = $message;
- $this->details = $details;
- $this->close_details = $close_details;
- }
-
- /**
- * Sets the state whether the close button should be hidden or not.
- *
- * @param boolean $state Whether the close button should be hidden or not
- * @return MessageBox instance to allow chaining
- */
- public function hideClose($state = true)
- {
- $this->hide_close = (bool) $state;
- return $this;
- }
-
- /**
- * Return whether this messagebox can be closed or not.
- * @return bool
- */
- public function isCloseable(): bool
- {
- return $this->hide_close;
- }
-
- /**
- * This method renders a MessageBox object to a string.
- *
- * @return string html output of the message box
- */
- public function __toString()
- {
- $label = [
- 'exception' => _('Systemfehler'),
- 'error' => _('Fehler'),
- 'warning' => _('Warnung'),
- 'info' => _('Hinweis'),
- 'success' => _('Erfolg'),
- ];
- return $GLOBALS['template_factory']->render('shared/message_box', [
- 'class' => $this->class,
- 'message' => $this->message,
- 'details' => is_array($this->details) ? $this->details : [],
- 'close_details' => $this->close_details,
- 'hide_close' => $this->hide_close,
- 'label' => $label[$this->class],
- 'counter' => self::$counter++,
- ]);
- }
-}
diff --git a/lib/classes/MessageBox.php b/lib/classes/MessageBox.php
new file mode 100644
index 0000000..8936ebe
--- /dev/null
+++ b/lib/classes/MessageBox.php
@@ -0,0 +1,177 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL Licence 2
+ * @category Stud.IP
+ * @package layout
+ * @since 1.10
+ *
+ */
+
+/**
+ * class MessageBox
+ *
+ * usage:
+ *
+ * echo MessageBox::error('Nachricht', ['optional details']);
+ *
+ * use the optional parameter $close_details for displaying the message box with
+ * closed details
+ *
+ * echo MessageBox::success('Nachricht', ['optional details'], true);
+ *
+ */
+class MessageBox implements LayoutMessage
+{
+ /**
+ * type and contents of the message box
+ */
+ public $class;
+ public $message;
+ public $details;
+ public $close_details;
+ protected $hide_close = false;
+ public static $counter = 0;
+
+ /**
+ * This function returns an exception message box. Use it only for system errors
+ * or security related problems.
+ *
+ * @param string $message
+ * @param array $details
+ * @param boolean $close_details
+ * @return object MessageBox object
+ */
+ public static function exception($message, $details = [], $close_details = false)
+ {
+ return new MessageBox('exception', $message, $details, $close_details);
+ }
+
+ /**
+ * This function returns an error message box. Use it for validation errors,
+ * problems and other wrong user input.
+ *
+ * @param string $message
+ * @param array $details (optional)
+ * @param boolean $close_details (optional)
+ * @return object MessageBox object
+ */
+ public static function error($message, $details = [], $close_details = false)
+ {
+ return new MessageBox('error', $message, $details, $close_details);
+ }
+
+ /**
+ * This function returns a success message box. Use it for confirmation of user
+ * interaction.
+ *
+ * @param string $message
+ * @param array $details (optional)
+ * @param boolean $close_details (optional)
+ * @return object MessageBox object
+ */
+ public static function success($message, $details = [], $close_details = false)
+ {
+ return new MessageBox('success', $message, $details, $close_details);
+ }
+
+ /**
+ * This function returns an info message box. Use it to report neutral
+ * informations.
+ *
+ * @param string $message
+ * @param array $details (optional)
+ * @param boolean $close_details (optional)
+ * @return object MessageBox object
+ */
+ public static function info($message, $details = [], $close_details = false)
+ {
+ return new MessageBox('info', $message, $details, $close_details);
+ }
+
+ /**
+ * This function returns a warning message box. Use it to report potentially
+ * wrong behaviour.
+ *
+ * @param string $message
+ * @param array $details (optional)
+ * @param boolean $close_details (optional)
+ * @return object MessageBox object
+ */
+ public static function warning($message, $details = [], $close_details = false)
+ {
+ return new MessageBox('warning', $message, $details, $close_details);
+ }
+
+ /**
+ * Initializes a new MessageBox object of the given class.
+ *
+ * @param string $class the type of this message
+ * @param string $message
+ * @param array $details (optional)
+ * @param boolean $close_details (optional)
+ */
+ protected function __construct($class, $message, $details = [], $close_details = false)
+ {
+ $this->class = $class;
+ $this->message = $message;
+ $this->details = $details;
+ $this->close_details = $close_details;
+ }
+
+ /**
+ * Sets the state whether the close button should be hidden or not.
+ *
+ * @param boolean $state Whether the close button should be hidden or not
+ * @return MessageBox instance to allow chaining
+ */
+ public function hideClose($state = true)
+ {
+ $this->hide_close = (bool) $state;
+ return $this;
+ }
+
+ /**
+ * Return whether this messagebox can be closed or not.
+ * @return bool
+ */
+ public function isCloseable(): bool
+ {
+ return $this->hide_close;
+ }
+
+ /**
+ * This method renders a MessageBox object to a string.
+ *
+ * @return string html output of the message box
+ */
+ public function __toString()
+ {
+ $label = [
+ 'exception' => _('Systemfehler'),
+ 'error' => _('Fehler'),
+ 'warning' => _('Warnung'),
+ 'info' => _('Hinweis'),
+ 'success' => _('Erfolg'),
+ ];
+ return $GLOBALS['template_factory']->render('shared/message_box', [
+ 'class' => $this->class,
+ 'message' => $this->message,
+ 'details' => is_array($this->details) ? $this->details : [],
+ 'close_details' => $this->close_details,
+ 'hide_close' => $this->hide_close,
+ 'label' => $label[$this->class],
+ 'counter' => self::$counter++,
+ ]);
+ }
+}
diff --git a/lib/classes/ModulesNotification.class.php b/lib/classes/ModulesNotification.class.php
deleted file mode 100644
index f414071..0000000
--- a/lib/classes/ModulesNotification.class.php
+++ /dev/null
@@ -1,182 +0,0 @@
-, Suchi & Berg GmbH
-* @access public
-* @modulegroup core
-* @package studip_core
-*/
-
-// +---------------------------------------------------------------------------+
-// This file is part of Stud.IP
-// Modules.class.php
-// Checks fuer Module (global und lokal fuer Veranstaltungen und Einrichtungen), Schreib-/Lesezugriff
-// Copyright (C) 2003 Cornelis Kater , Suchi & Berg GmbH
-// +---------------------------------------------------------------------------+
-// 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 any later version.
-// +---------------------------------------------------------------------------+
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-// +---------------------------------------------------------------------------+
-
-class ModulesNotification
-{
-
- public $registered_notification_modules = [];
- public $subject;
-
- public function __construct ()
- {
- foreach (MyRealmModel::getDefaultModules() as $id => $module) {
- if (!is_object($module)) {
- continue;
- }
-
- $metadata = $module->getMetadata();
-
- $this->registered_notification_modules[$id] = [
- 'icon' => !empty($metadata['icon']) ? $metadata['icon'] : null,
- 'name' => !empty($metadata['displayname']) ? $metadata['displayname'] : $module->getPluginName(),
- ];
- if ($module instanceof CoreOverview) {
- $this->registered_notification_modules[$id]['name'] = _("Ankündigungen");
- $this->registered_notification_modules[$id]['icon'] = Icon::create('news');
- }
- if (!is_object($this->registered_notification_modules[$id]['icon'])) {
- $icon = $module->getPluginURL() . '/' . $this->registered_notification_modules[$id]['icon'];
- $this->registered_notification_modules[$id]['icon'] = Icon::create($icon);
- }
- }
- $this->registered_notification_modules[-1] =
- [
- 'name' => _("Umfragen und Tests"),
- 'icon' => Icon::create('vote')
- ];
- $this->registered_notification_modules[0] =
- [
- 'name' => _("Grunddaten der Veranstaltung"),
- 'icon' => Icon::create('seminar')
- ];
-
- $this->subject = _("Stud.IP Benachrichtigung");
- }
-
-
-
- public function getAllNotifications ($user_id = null)
- {
- if ($user_id === null) {
- $user_id = $GLOBALS['user']->id;
- }
-
- $my_sem = [];
- $query = "SELECT s.Seminar_id, s.Name, s.chdate, s.start_time, IFNULL(visitdate, :threshold) AS visitdate
- FROM seminar_user_notifications su
- JOIN seminar_user USING (user_id, seminar_id)
- JOIN seminare s USING (Seminar_id)
- LEFT JOIN object_user_visits ouv
- ON (
- ouv.object_id = su.Seminar_id
- AND ouv.user_id = :user_id
- AND ouv.plugin_id = 0
- )
- WHERE su.user_id = :user_id";
-
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':user_id', $user_id);
- $statement->bindValue(':threshold', object_get_visit_threshold());
- $statement->execute();
- while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
- $seminar_id = $row['Seminar_id'];
- $tools = ToolActivation::findbyRange_id($seminar_id);
- $notification = CourseMemberNotification::find([$user_id, $seminar_id]);
-
- if (!$notification || count($notification->notification_data) === 0) {
- continue;
- }
-
- $my_sem[$seminar_id] = [
- 'name' => $row['Name'],
- 'chdate' => $row['chdate'],
- 'start_time' => $row['start_time'],
- 'tools' => new SimpleCollection($tools),
- 'visitdate' => $row['visitdate'],
- 'notification' => $notification->notification_data->getArrayCopy(),
- ];
- }
- $visit_data = get_objects_visits(array_keys($my_sem), 'sem', null, $user_id, array_keys($this->registered_notification_modules));
- $news = [];
- foreach ($my_sem as $seminar_id => $s_data) {
- $navigation = MyRealmModel::getAdditionalNavigations($seminar_id, $s_data, null, $user_id, $visit_data[$seminar_id]);
- $n_data = [];
- foreach ($this->registered_notification_modules as $id => $m_data) {
- if (
- in_array($id, $s_data['notification'])
- && isset($navigation[$id])
- && $navigation[$id]->getImage()
- && $navigation[$id]->getImage()->getRole() === Icon::ROLE_ATTENTION
- ) {
- $data = $this->getPluginText($navigation[$id], $seminar_id, $id);
- if ($data) {
- $n_data[] = $data;
- }
- }
- }
- if (count($n_data)) {
- $news[$s_data['name']] = $n_data;
- }
- }
- if (count($news)) {
- $user = User::find($user_id);
- $auth_plugin = $user->auth_plugin;
- if (!is_a('StudipAuth' . ucfirst($auth_plugin), 'StudipAuthSSO', true)) {
- $auth_plugin = null;
- }
- $template = $GLOBALS['template_factory']->open('mail/notification_html');
- $template->set_attribute('lang', getUserLanguagePath($user_id));
- $template->set_attribute('rec_fullname', $user->getFullname('full'));
- $template->set_attribute('rec_username', $user->username);
- $template->set_attribute('news', $news);
- $template->set_attribute('sso', $auth_plugin);
-
- $template_text = $GLOBALS['template_factory']->open('mail/notification_text');
- $template_text->set_attribute('news', $news);
- $template_text->set_attribute('sso', $auth_plugin);
- return ['text' => $template_text->render(), 'html' => $template->render()];
- }
-
- return null;
- }
-
- function getPluginText($nav, $seminar_id, $id)
- {
- $base_url = URLHelper::setBaseURL('');
- URLHelper::setBaseURl($base_url);
- if ($nav instanceof Navigation && $nav->isVisible(true)) {
- $url = 'seminar_main.php?again=yes&auswahl=' . $seminar_id . '&redirect_to=' . strtr($nav->getURL(), '?', '&');
- $icon = $nav->getImage();
- $text = $nav->getTitle();
- if (!$text) {
- $text = $this->registered_notification_modules[$id]['name'];
- }
- $text .= ' - ' . $nav->getLinkAttributes()['title'];
- return compact('text', 'url', 'icon', 'seminar_id');
- }
- }
-}
diff --git a/lib/classes/ModulesNotification.php b/lib/classes/ModulesNotification.php
new file mode 100644
index 0000000..f414071
--- /dev/null
+++ b/lib/classes/ModulesNotification.php
@@ -0,0 +1,182 @@
+, Suchi & Berg GmbH
+* @access public
+* @modulegroup core
+* @package studip_core
+*/
+
+// +---------------------------------------------------------------------------+
+// This file is part of Stud.IP
+// Modules.class.php
+// Checks fuer Module (global und lokal fuer Veranstaltungen und Einrichtungen), Schreib-/Lesezugriff
+// Copyright (C) 2003 Cornelis Kater , Suchi & Berg GmbH
+// +---------------------------------------------------------------------------+
+// 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 any later version.
+// +---------------------------------------------------------------------------+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// +---------------------------------------------------------------------------+
+
+class ModulesNotification
+{
+
+ public $registered_notification_modules = [];
+ public $subject;
+
+ public function __construct ()
+ {
+ foreach (MyRealmModel::getDefaultModules() as $id => $module) {
+ if (!is_object($module)) {
+ continue;
+ }
+
+ $metadata = $module->getMetadata();
+
+ $this->registered_notification_modules[$id] = [
+ 'icon' => !empty($metadata['icon']) ? $metadata['icon'] : null,
+ 'name' => !empty($metadata['displayname']) ? $metadata['displayname'] : $module->getPluginName(),
+ ];
+ if ($module instanceof CoreOverview) {
+ $this->registered_notification_modules[$id]['name'] = _("Ankündigungen");
+ $this->registered_notification_modules[$id]['icon'] = Icon::create('news');
+ }
+ if (!is_object($this->registered_notification_modules[$id]['icon'])) {
+ $icon = $module->getPluginURL() . '/' . $this->registered_notification_modules[$id]['icon'];
+ $this->registered_notification_modules[$id]['icon'] = Icon::create($icon);
+ }
+ }
+ $this->registered_notification_modules[-1] =
+ [
+ 'name' => _("Umfragen und Tests"),
+ 'icon' => Icon::create('vote')
+ ];
+ $this->registered_notification_modules[0] =
+ [
+ 'name' => _("Grunddaten der Veranstaltung"),
+ 'icon' => Icon::create('seminar')
+ ];
+
+ $this->subject = _("Stud.IP Benachrichtigung");
+ }
+
+
+
+ public function getAllNotifications ($user_id = null)
+ {
+ if ($user_id === null) {
+ $user_id = $GLOBALS['user']->id;
+ }
+
+ $my_sem = [];
+ $query = "SELECT s.Seminar_id, s.Name, s.chdate, s.start_time, IFNULL(visitdate, :threshold) AS visitdate
+ FROM seminar_user_notifications su
+ JOIN seminar_user USING (user_id, seminar_id)
+ JOIN seminare s USING (Seminar_id)
+ LEFT JOIN object_user_visits ouv
+ ON (
+ ouv.object_id = su.Seminar_id
+ AND ouv.user_id = :user_id
+ AND ouv.plugin_id = 0
+ )
+ WHERE su.user_id = :user_id";
+
+ $statement = DBManager::get()->prepare($query);
+ $statement->bindValue(':user_id', $user_id);
+ $statement->bindValue(':threshold', object_get_visit_threshold());
+ $statement->execute();
+ while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
+ $seminar_id = $row['Seminar_id'];
+ $tools = ToolActivation::findbyRange_id($seminar_id);
+ $notification = CourseMemberNotification::find([$user_id, $seminar_id]);
+
+ if (!$notification || count($notification->notification_data) === 0) {
+ continue;
+ }
+
+ $my_sem[$seminar_id] = [
+ 'name' => $row['Name'],
+ 'chdate' => $row['chdate'],
+ 'start_time' => $row['start_time'],
+ 'tools' => new SimpleCollection($tools),
+ 'visitdate' => $row['visitdate'],
+ 'notification' => $notification->notification_data->getArrayCopy(),
+ ];
+ }
+ $visit_data = get_objects_visits(array_keys($my_sem), 'sem', null, $user_id, array_keys($this->registered_notification_modules));
+ $news = [];
+ foreach ($my_sem as $seminar_id => $s_data) {
+ $navigation = MyRealmModel::getAdditionalNavigations($seminar_id, $s_data, null, $user_id, $visit_data[$seminar_id]);
+ $n_data = [];
+ foreach ($this->registered_notification_modules as $id => $m_data) {
+ if (
+ in_array($id, $s_data['notification'])
+ && isset($navigation[$id])
+ && $navigation[$id]->getImage()
+ && $navigation[$id]->getImage()->getRole() === Icon::ROLE_ATTENTION
+ ) {
+ $data = $this->getPluginText($navigation[$id], $seminar_id, $id);
+ if ($data) {
+ $n_data[] = $data;
+ }
+ }
+ }
+ if (count($n_data)) {
+ $news[$s_data['name']] = $n_data;
+ }
+ }
+ if (count($news)) {
+ $user = User::find($user_id);
+ $auth_plugin = $user->auth_plugin;
+ if (!is_a('StudipAuth' . ucfirst($auth_plugin), 'StudipAuthSSO', true)) {
+ $auth_plugin = null;
+ }
+ $template = $GLOBALS['template_factory']->open('mail/notification_html');
+ $template->set_attribute('lang', getUserLanguagePath($user_id));
+ $template->set_attribute('rec_fullname', $user->getFullname('full'));
+ $template->set_attribute('rec_username', $user->username);
+ $template->set_attribute('news', $news);
+ $template->set_attribute('sso', $auth_plugin);
+
+ $template_text = $GLOBALS['template_factory']->open('mail/notification_text');
+ $template_text->set_attribute('news', $news);
+ $template_text->set_attribute('sso', $auth_plugin);
+ return ['text' => $template_text->render(), 'html' => $template->render()];
+ }
+
+ return null;
+ }
+
+ function getPluginText($nav, $seminar_id, $id)
+ {
+ $base_url = URLHelper::setBaseURL('');
+ URLHelper::setBaseURl($base_url);
+ if ($nav instanceof Navigation && $nav->isVisible(true)) {
+ $url = 'seminar_main.php?again=yes&auswahl=' . $seminar_id . '&redirect_to=' . strtr($nav->getURL(), '?', '&');
+ $icon = $nav->getImage();
+ $text = $nav->getTitle();
+ if (!$text) {
+ $text = $this->registered_notification_modules[$id]['name'];
+ }
+ $text .= ' - ' . $nav->getLinkAttributes()['title'];
+ return compact('text', 'url', 'icon', 'seminar_id');
+ }
+ }
+}
diff --git a/lib/classes/MultiDimArrayObject.class.php b/lib/classes/MultiDimArrayObject.class.php
deleted file mode 100644
index b578018..0000000
--- a/lib/classes/MultiDimArrayObject.class.php
+++ /dev/null
@@ -1,129 +0,0 @@
-
- *
- */
-class MultiDimArrayObject extends StudipArrayObject
-{
- /**
- * Constructor
- *
- * @param array $input
- * @param int $flags
- * @param string $iteratorClass
- */
- public function __construct($input = [], $flags = self::STD_PROP_LIST, $iteratorClass = 'ArrayIterator')
- {
- parent::__construct([], $flags, $iteratorClass);
- $this->exchangeArray($input);
- }
-
- /**
- * Appends the value
- *
- * @param mixed $value
- * @return void
- */
- public function append($value)
- {
- $this->offsetSet(null, $value);
- }
-
- /**
- * Exchange the array for another one.
- *
- * @param array|ArrayObject $data
- * @return array
- */
- public function exchangeArray($data)
- {
- if (!is_array($data) && !is_object($data)) {
- throw new InvalidArgumentException('Passed variable is not an array or object');
- }
-
- if ($data instanceof \StudipArrayObject) {
- $data = $data->getArrayCopy();
- }
- if (!is_array($data)) {
- $data = (array) $data;
- }
-
- $storage = $this->storage;
-
- $this->storage = $this->recursiveArrayToArrayObjects($data);
-
- return $storage;
- }
-
- /**
- * Creates a copy of the ArrayObject.
- *
- * @return array
- */
- public function getArrayCopy()
- {
- $ret = [];
- foreach($this->storage as $key => $value) {
- if ($value instanceOf StudipArrayObject) {
- $ret[$key] = $value->getArrayCopy();
- } else {
- $ret[$key] = $value;
- }
- }
- return $ret;
- }
-
- /**
- * Create a new iterator from an ArrayObject instance
- */
- public function getIterator(): Traversable
- {
- $class = $this->iteratorClass;
-
- return new $class($this->getArrayCopy());
- }
-
- /**
- * Sets the value at the specified key to value
- *
- * @param mixed $key
- * @param mixed $value
- */
- public function offsetSet($key, $value): void
- {
- $new_value = $this->recursiveArrayToArrayObjects($value);
- if (is_array($new_value)) {
- $class = get_called_class();
- $new_value = new $class($new_value, $this->getFlags(), $this->getIteratorClass());
- }
- if (is_null($key)) {
- $this->storage[] = $new_value;
- } else {
- $this->storage[$key] = $new_value;
- }
- }
-
- protected function recursiveArrayToArrayObjects($data)
- {
- if ($data instanceOf StudipArrayObject) {
- $data = $data->getArrayCopy();
- }
- if (is_array($data)) {
- $new_data = [];
- foreach ($data as $key => $value) {
- $new_value = $this->recursiveArrayToArrayObjects($value);
- if (is_array($new_value)) {
- $class = get_called_class();
- $new_data[$key] = new $class($new_value, $this->getFlags(), $this->getIteratorClass());
- } else {
- $new_data[$key] = $value;
- }
- }
- return $new_data;
- }
- return $data;
- }
-}
diff --git a/lib/classes/MultiDimArrayObject.php b/lib/classes/MultiDimArrayObject.php
new file mode 100644
index 0000000..b578018
--- /dev/null
+++ b/lib/classes/MultiDimArrayObject.php
@@ -0,0 +1,129 @@
+
+ *
+ */
+class MultiDimArrayObject extends StudipArrayObject
+{
+ /**
+ * Constructor
+ *
+ * @param array $input
+ * @param int $flags
+ * @param string $iteratorClass
+ */
+ public function __construct($input = [], $flags = self::STD_PROP_LIST, $iteratorClass = 'ArrayIterator')
+ {
+ parent::__construct([], $flags, $iteratorClass);
+ $this->exchangeArray($input);
+ }
+
+ /**
+ * Appends the value
+ *
+ * @param mixed $value
+ * @return void
+ */
+ public function append($value)
+ {
+ $this->offsetSet(null, $value);
+ }
+
+ /**
+ * Exchange the array for another one.
+ *
+ * @param array|ArrayObject $data
+ * @return array
+ */
+ public function exchangeArray($data)
+ {
+ if (!is_array($data) && !is_object($data)) {
+ throw new InvalidArgumentException('Passed variable is not an array or object');
+ }
+
+ if ($data instanceof \StudipArrayObject) {
+ $data = $data->getArrayCopy();
+ }
+ if (!is_array($data)) {
+ $data = (array) $data;
+ }
+
+ $storage = $this->storage;
+
+ $this->storage = $this->recursiveArrayToArrayObjects($data);
+
+ return $storage;
+ }
+
+ /**
+ * Creates a copy of the ArrayObject.
+ *
+ * @return array
+ */
+ public function getArrayCopy()
+ {
+ $ret = [];
+ foreach($this->storage as $key => $value) {
+ if ($value instanceOf StudipArrayObject) {
+ $ret[$key] = $value->getArrayCopy();
+ } else {
+ $ret[$key] = $value;
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Create a new iterator from an ArrayObject instance
+ */
+ public function getIterator(): Traversable
+ {
+ $class = $this->iteratorClass;
+
+ return new $class($this->getArrayCopy());
+ }
+
+ /**
+ * Sets the value at the specified key to value
+ *
+ * @param mixed $key
+ * @param mixed $value
+ */
+ public function offsetSet($key, $value): void
+ {
+ $new_value = $this->recursiveArrayToArrayObjects($value);
+ if (is_array($new_value)) {
+ $class = get_called_class();
+ $new_value = new $class($new_value, $this->getFlags(), $this->getIteratorClass());
+ }
+ if (is_null($key)) {
+ $this->storage[] = $new_value;
+ } else {
+ $this->storage[$key] = $new_value;
+ }
+ }
+
+ protected function recursiveArrayToArrayObjects($data)
+ {
+ if ($data instanceOf StudipArrayObject) {
+ $data = $data->getArrayCopy();
+ }
+ if (is_array($data)) {
+ $new_data = [];
+ foreach ($data as $key => $value) {
+ $new_value = $this->recursiveArrayToArrayObjects($value);
+ if (is_array($new_value)) {
+ $class = get_called_class();
+ $new_data[$key] = new $class($new_value, $this->getFlags(), $this->getIteratorClass());
+ } else {
+ $new_data[$key] = $value;
+ }
+ }
+ return $new_data;
+ }
+ return $data;
+ }
+}
diff --git a/lib/classes/MultiPersonSearch.class.php b/lib/classes/MultiPersonSearch.class.php
deleted file mode 100644
index af10e6b..0000000
--- a/lib/classes/MultiPersonSearch.class.php
+++ /dev/null
@@ -1,548 +0,0 @@
-
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- * @link http://docs.studip.de/develop/Entwickler/MultiPersonSearch
- */
-class MultiPersonSearch {
-
- private $name;
- private $linkIconPath = "";
- private $linkText = "";
- private $title = "";
- private $description = "";
- private $executeURL;
- private $jsFunction = null;
- private $pageURL = null;
- private $quickfilterIds = [];
- private $defaultSelectableUsersIDs = [];
- private $defaultSelectedUsersIDs = [];
- private $searchObject = null;
- private $additionalHMTL = "";
- private $navigationItem = "";
- private $dataDialogStatus = false;
-
- /**
- * restores a MultiPersonSearch object.
- *
- * @param string name of the object
- *
- * @return MultiPersonSearch
- */
- public static function load($name)
- {
- $mp = new MultiPersonSearch($name);
- $mp->restoreFromSession();
- return $mp;
- }
-
- /**
- * returns a MultiPersonSearch object.
- *
- * @param string name of the object
- *
- * @return MultiPersonSearch
- */
- public static function get($name)
- {
- $mp = new MultiPersonSearch($name);
- return $mp;
- }
-
- /**
- * contsructs a new MultiPersonSearch object.
- *
- * @param string name of the object and html ids
- */
- public function __construct($name)
- {
- $this->name = $name;
- $_SESSION['multipersonsearch'][$this->name]['lastUse'] = time();
- $this->collectGarbage();
- $this->setDefaultValues();
-
- }
-
- /**
- * returns the newly added persons. The array will contain all
- * persons which are selected (on the right side of the dialog) but
- * without the defaultSelectedUsers.
- *
- * @return array containing all new persons
- */
- public function getAddedUsers() {
- return $_SESSION['multipersonsearch'][$this->name]['added'] ?? [];
- }
-
- /**
- * saves the added persons to $_SESSION.
- */
- public function saveAddedUsersToSession() {
- $addedUsers = [];
- foreach (Request::optionArray($this->name . '_selectbox') as $selected) {
- if (!in_array($selected, $_SESSION['multipersonsearch'][$this->name]['defaultSelectedUsersIDs'])) {
- $addedUsers[] = $selected;
- }
- }
- $_SESSION['multipersonsearch'][$this->name]['added'] = $addedUsers;
- $_SESSION['multipersonsearch'][$this->name]['additional'] = Request::optionArray('additional');
- }
-
- /**
- * returns the removed persons. The array will contain all
- * persons which were selected by default (on the right side of the
- * dialog) and then removed by the user.
- *
- * @return array containing all removed persons
- */
- public function getRemovedUsers() {
- return $_SESSION['multipersonsearch'][$this->name]['removed'];
- }
-
- /**
- * saves the removed persons to $_SESSION.
- */
- public function saveRemovedUsersToSession() {
- $removedUsers = [];
- foreach ($this->defaultSelectedUsersIDs as $default) {
- if (!in_array($default, Request::optionArray($this->name . '_selectbox'))) {
- $removedUsers[] = $default;
- }
- }
- $_SESSION['multipersonsearch'][$this->name]['removed'] = $removedUsers;
- }
-
- /**
- * renders a link to open the multipersonsearch dialog.
- *
- * @param string $with_link_text include link text in output
- */
- public function render($with_link_text = true) {
- $template = $GLOBALS['template_factory']->open('multipersonsearch/link.php');
-
- $template->set_attribute('linkIconPath', $this->linkIconPath);
- $template->set_attribute('linkText', $with_link_text ? $this->linkText : '');
- $template->set_attribute('title', $this->title);
- $template->set_attribute('name', $this->name);
- $template->set_attribute('description', $this->description);
- $template->set_attribute('executeURL', $this->executeURL);
- $template->set_attribute('jsFunction', $this->jsFunction);
- $this->storeToSession();
- return $template->render();
- }
-
- /**
- * sets the icon of the link to open the dialog. To hide the icon an
- * empty string can be set.
- *
- * @param string path ot the icon
- *
- * @return MultiPersonSearch
- */
- public function setLinkIconPath($path) {
- $this->linkIconPath = $path;
-
- return $this;
- }
-
- /**
- * returns the icon of the link to open the dialog.
- *
- * @return string path ot the icon.
- */
- public function getLinkIconPath() {
- return $this->linkIconPath;
- }
-
- /**
- * sets the link text of the link to open the dialog. To hide the
- * text an empty string can be set.
- *
- * @param string text of the link
- *
- * @return MultiPersonSearch
- *
- */
- public function setLinkText($text = "") {
- $this->linkText = $text;
-
- return $this;
- }
-
- /**
- * returns the link text of the link.
- *
- * @return string text of the link.
- */
- public function getLinkText() {
- return $this->linkText;
- }
-
- /**
- * sets the action which will handle the added and removed persons after saving the dialog.
- *
- * @param string action
- *
- * @return MultiPersonSearch
- */
- public function setExecuteURL($action) {
- $this->executeURL = $action;
-
- return $this;
- }
-
- /**
- * returns the action which will handle the added and removed persons after saving the dialog.
- *
- * @return string action which will handle the form data.
- */
- public function getExecuteURL() {
- return $this->executeURL;
- }
-
- /**
- * sets a JavaScript-function to be fired when the user has pressed the submit-button.
- * Arguments are:
- * function fireme(id_of_item, text_of_item)
- * example setting: MPS->setJSFunctionOnSubmit('fireme');
- *
- * @param string $function_name the name of the javascript function
- *
- * @return MultiPersonSearch
- */
- public function setJSFunctionOnSubmit($function_name)
- {
- $this->jsFunction = $function_name;
- return $this;
- }
-
- /**
- * returns a JavaScript-function which should be fired when the user has pressed the submit button.
- *
- * @return string function name
- */
- public function getJSFunctionOnSubmit()
- {
- return $this->jsFunction;
- }
-
- /**
- * sets the search object.
- *
- * @param SearchType object of type SearchType (e.g. SQLSearch.class.php)
- *
- * @return MultiPersonSearch
- */
- public function setSearchObject($searchType) {
- $this->searchObject = $searchType;
-
- return $this;
- }
-
- /**
- * returns the search object.
- *
- * @return SearchType
- */
- public function getSearchObject() {
- return $this->searchObject;
- }
-
- /**
- * sets html code which will be shown inside the form element.
- *
- * @param string html code
- *
- * @return MultiPersonSearch
- */
- public function setAdditionalHTML($html) {
- $this->additionalHMTL = $html;
-
- return $this;
- }
-
-
- /**
- * enables or disabled data-dialog
- * @param boolean $status
- * @return $this
- */
- public function setDataDialogStatus($status) {
- $this->dataDialogStatus = $status;
-
- return $this;
- }
-
- /**
- * returns if data-dialog is enabled or disabled
- * @return bool
- */
- public function getDataDialogStatus() {
- return $this->dataDialogStatus;
- }
- /**
- * returns html code which will be shown inside the form element.
- *
- * @return string html code
- */
- public function getAdditionHTML() {
- return $this->additionalHMTL;
- }
-
- /**
- * returns an additional option array.
- *
- * @return string html code
- */
- public function getAdditionalOptionArray() {
- return $_SESSION['multipersonsearch'][$this->name]['additional'];
- }
-
- /**
- * sets the persons which will be shown as selectable by default on
- * the left side of the dialoag.
- *
- * @param array array containing user-ids
- */
- public function setDefaultSelectableUser($userArray) {
- $userArray = array_unique($userArray);
- $this->defaultSelectableUsersIDs = [];
- if (is_array($userArray)) {
- foreach ($userArray as $userId) {
- $this->defaultSelectableUsersIDs[] = $userId;
- }
- }
- return $this;
- }
- /**
- * returns the ids of defaultselectable users.
- *
- * @return array
- */
- public function getDefaultSelectableUsersIDs() {
- return $this->defaultSelectableUsersIDs;
- }
-
- /**
- * sets the persons which will be shown as selected by default on
- * the right side of the dialoag.
- *
- * @param array array containing user-ids
- */
- public function setDefaultSelectedUser($userArray) {
- $userArray = array_unique($userArray);
- $this->defaultSelectedUsersIDs = [];
- if (is_array($userArray)) {
- foreach ($userArray as $userId) {
- $this->defaultSelectedUsersIDs[] = $userId;
- }
- }
- return $this;
- }
-
- /**
- * returns the ids of defaultselected users.
- *
- * @return array
- */
- public function getDefaultSelectedUsersIDs() {
- return $this->defaultSelectedUsersIDs;
- }
-
-
- /**
- * sets the title of the dialog.
- *
- * @param string $title title of the dialog
- *
- * @return MultiPersonSearch
- */
- public function setTitle($title) {
- $this->title = $title;
- return $this;
- }
-
- /**
- * returns the title.
- *
- * @return string
- */
- public function getTitle() {
- return $this->title;
- }
-
-
- /**
- * sets the description of the dialog.
- *
- * @param string $desc description of the dialog
- *
- * @return MultiPersonSearch
- */
- public function setDescription($desc) {
- $this->description = $desc;
- return $this;
- }
-
- /**
- * returns the description.
- *
- * @return string
- */
- public function getDescription() {
- return $this->description;
- }
-
- /**
- * returns the url of the page where the GUI element is added.
- *
- * @return string
- */
- public function getPageUrl() {
- return $this->pageURL;
- }
-
- /**
- * adds a new quickfilter.
- *
- * @param string $title title of the new quickfilter
- * @param array $userArray containing all user-ids belonging to the quickfilter
- *
- * @return MultiPersonSearch
- */
- public function addQuickfilter($title, $userArray) {
- $users = [];
- $usersIds = [];
- if (is_array($userArray)) {
- foreach ($userArray as $userId) {
- $usersIds[] = $userId;
- }
- }
- $this->quickfilterIds[(string) $title] = $usersIds;
-
- return $this;
- }
-
- /**
- * returns the ids of quickfilters.
- *
- * @return array
- */
- public function getQuickfilterIds()
- {
- return $this->quickfilterIds ?: [];
- }
-
- /**
- * clears all quickfilters.
- *
- * @return MultiPersonSearch
- */
- public function clearQuickfilters() {
- $this->quickfilterIds = [];
-
- return $this;
- }
-
- /**
- * sets the navigation item.
- *
- * @param string $navigationItem navigation item
- *
- * @return MultiPersonSearch
- */
- public function setNavigationItem($navigationItem) {
- $this->navigationItem = $navigationItem;
-
- return $this;
- }
-
- /**
- * returns the navigation item.
- *
- * @return string
- */
- public function getNavigationItem() {
- return $this->navigationItem;
- }
-
- /**
- * stores the internal data to a session.
- */
- public function storeToSession() {
- $_SESSION['multipersonsearch'][$this->name]['title'] = $this->title;
- $_SESSION['multipersonsearch'][$this->name]['description'] = $this->description;
- $_SESSION['multipersonsearch'][$this->name]['additionalHMTL'] = $this->additionalHMTL;
- $_SESSION['multipersonsearch'][$this->name]['executeURL'] = $this->executeURL;
- $_SESSION['multipersonsearch'][$this->name]['jsFunction'] = $this->jsFunction;
- $_SESSION['multipersonsearch'][$this->name]['pageURL'] = Request::url();
- $_SESSION['multipersonsearch'][$this->name]['defaultSelectableUsersIDs'] = $this->defaultSelectableUsersIDs;
- $_SESSION['multipersonsearch'][$this->name]['defaultSelectedUsersIDs'] = $this->defaultSelectedUsersIDs;
- $_SESSION['multipersonsearch'][$this->name]['quickfilterIds'] = $this->quickfilterIds;
- $_SESSION['multipersonsearch'][$this->name]['searchObject'] = serialize($this->searchObject);
- $_SESSION['multipersonsearch'][$this->name]['navigationItem'] = $this->navigationItem;
- $_SESSION['multipersonsearch'][$this->name]['dataDialogStatus'] = $this->dataDialogStatus;
- }
-
- /**
- * restores the internal data from a session.
- */
- public function restoreFromSession() {
- if (isset($_SESSION['multipersonsearch'][$this->name])) {
- $this->title = $_SESSION['multipersonsearch'][$this->name]['title'] ?? '';
- $this->description = $_SESSION['multipersonsearch'][$this->name]['description'] ?? '';
- $this->quickfilterIds = $_SESSION['multipersonsearch'][$this->name]['quickfilterIds'] ?? [];
- $this->additionalHMTL = $_SESSION['multipersonsearch'][$this->name]['additionalHMTL'] ?? '';
- $this->executeURL = html_entity_decode($_SESSION['multipersonsearch'][$this->name]['executeURL'] ?? '');
- $this->jsFunction = $_SESSION['multipersonsearch'][$this->name]['jsFunction'] ?? '';
- $this->pageURL = $_SESSION['multipersonsearch'][$this->name]['pageURL'] ?? '';
- $this->defaultSelectableUsersIDs = $_SESSION['multipersonsearch'][$this->name]['defaultSelectableUsersIDs'] ?? [];
- $this->defaultSelectedUsersIDs = $_SESSION['multipersonsearch'][$this->name]['defaultSelectedUsersIDs'] ?? [];
- $this->searchObject = unserialize($_SESSION['multipersonsearch'][$this->name]['searchObject'] ?? null);
- $this->navigationItem = $_SESSION['multipersonsearch'][$this->name]['navigationItem'] ?? null;
- $this->dataDialogStatus = $_SESSION['multipersonsearch'][$this->name]['dataDialogStatus'] ?? '';
- }
- }
-
- /**
- * clears the session data.
- */
- public function clearSession() {
- unset($_SESSION['multipersonsearch'][$this->name]);
- }
-
- /**
- * sets default values of the internal variables.
- */
- private function setDefaultValues() {
- $this->title = '';
- $this->description = _('Bitte wählen Sie aus, wen Sie hinzufügen möchten.');
- $this->linkIconPath = Icon::create('add');
- }
-
- /**
- * clear unused sessions.
- */
- private function collectGarbage() {
- $maxLifeTime = 30; // minutes
- foreach ($_SESSION['multipersonsearch'] as $key=>$value) {
- if (time() - $value['lastUse'] > $maxLifeTime * 60) {
- unset($_SESSION['multipersonsearch'][$key]);
- }
- }
- }
-
-}
diff --git a/lib/classes/MultiPersonSearch.php b/lib/classes/MultiPersonSearch.php
new file mode 100644
index 0000000..af10e6b
--- /dev/null
+++ b/lib/classes/MultiPersonSearch.php
@@ -0,0 +1,548 @@
+
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @link http://docs.studip.de/develop/Entwickler/MultiPersonSearch
+ */
+class MultiPersonSearch {
+
+ private $name;
+ private $linkIconPath = "";
+ private $linkText = "";
+ private $title = "";
+ private $description = "";
+ private $executeURL;
+ private $jsFunction = null;
+ private $pageURL = null;
+ private $quickfilterIds = [];
+ private $defaultSelectableUsersIDs = [];
+ private $defaultSelectedUsersIDs = [];
+ private $searchObject = null;
+ private $additionalHMTL = "";
+ private $navigationItem = "";
+ private $dataDialogStatus = false;
+
+ /**
+ * restores a MultiPersonSearch object.
+ *
+ * @param string name of the object
+ *
+ * @return MultiPersonSearch
+ */
+ public static function load($name)
+ {
+ $mp = new MultiPersonSearch($name);
+ $mp->restoreFromSession();
+ return $mp;
+ }
+
+ /**
+ * returns a MultiPersonSearch object.
+ *
+ * @param string name of the object
+ *
+ * @return MultiPersonSearch
+ */
+ public static function get($name)
+ {
+ $mp = new MultiPersonSearch($name);
+ return $mp;
+ }
+
+ /**
+ * contsructs a new MultiPersonSearch object.
+ *
+ * @param string name of the object and html ids
+ */
+ public function __construct($name)
+ {
+ $this->name = $name;
+ $_SESSION['multipersonsearch'][$this->name]['lastUse'] = time();
+ $this->collectGarbage();
+ $this->setDefaultValues();
+
+ }
+
+ /**
+ * returns the newly added persons. The array will contain all
+ * persons which are selected (on the right side of the dialog) but
+ * without the defaultSelectedUsers.
+ *
+ * @return array containing all new persons
+ */
+ public function getAddedUsers() {
+ return $_SESSION['multipersonsearch'][$this->name]['added'] ?? [];
+ }
+
+ /**
+ * saves the added persons to $_SESSION.
+ */
+ public function saveAddedUsersToSession() {
+ $addedUsers = [];
+ foreach (Request::optionArray($this->name . '_selectbox') as $selected) {
+ if (!in_array($selected, $_SESSION['multipersonsearch'][$this->name]['defaultSelectedUsersIDs'])) {
+ $addedUsers[] = $selected;
+ }
+ }
+ $_SESSION['multipersonsearch'][$this->name]['added'] = $addedUsers;
+ $_SESSION['multipersonsearch'][$this->name]['additional'] = Request::optionArray('additional');
+ }
+
+ /**
+ * returns the removed persons. The array will contain all
+ * persons which were selected by default (on the right side of the
+ * dialog) and then removed by the user.
+ *
+ * @return array containing all removed persons
+ */
+ public function getRemovedUsers() {
+ return $_SESSION['multipersonsearch'][$this->name]['removed'];
+ }
+
+ /**
+ * saves the removed persons to $_SESSION.
+ */
+ public function saveRemovedUsersToSession() {
+ $removedUsers = [];
+ foreach ($this->defaultSelectedUsersIDs as $default) {
+ if (!in_array($default, Request::optionArray($this->name . '_selectbox'))) {
+ $removedUsers[] = $default;
+ }
+ }
+ $_SESSION['multipersonsearch'][$this->name]['removed'] = $removedUsers;
+ }
+
+ /**
+ * renders a link to open the multipersonsearch dialog.
+ *
+ * @param string $with_link_text include link text in output
+ */
+ public function render($with_link_text = true) {
+ $template = $GLOBALS['template_factory']->open('multipersonsearch/link.php');
+
+ $template->set_attribute('linkIconPath', $this->linkIconPath);
+ $template->set_attribute('linkText', $with_link_text ? $this->linkText : '');
+ $template->set_attribute('title', $this->title);
+ $template->set_attribute('name', $this->name);
+ $template->set_attribute('description', $this->description);
+ $template->set_attribute('executeURL', $this->executeURL);
+ $template->set_attribute('jsFunction', $this->jsFunction);
+ $this->storeToSession();
+ return $template->render();
+ }
+
+ /**
+ * sets the icon of the link to open the dialog. To hide the icon an
+ * empty string can be set.
+ *
+ * @param string path ot the icon
+ *
+ * @return MultiPersonSearch
+ */
+ public function setLinkIconPath($path) {
+ $this->linkIconPath = $path;
+
+ return $this;
+ }
+
+ /**
+ * returns the icon of the link to open the dialog.
+ *
+ * @return string path ot the icon.
+ */
+ public function getLinkIconPath() {
+ return $this->linkIconPath;
+ }
+
+ /**
+ * sets the link text of the link to open the dialog. To hide the
+ * text an empty string can be set.
+ *
+ * @param string text of the link
+ *
+ * @return MultiPersonSearch
+ *
+ */
+ public function setLinkText($text = "") {
+ $this->linkText = $text;
+
+ return $this;
+ }
+
+ /**
+ * returns the link text of the link.
+ *
+ * @return string text of the link.
+ */
+ public function getLinkText() {
+ return $this->linkText;
+ }
+
+ /**
+ * sets the action which will handle the added and removed persons after saving the dialog.
+ *
+ * @param string action
+ *
+ * @return MultiPersonSearch
+ */
+ public function setExecuteURL($action) {
+ $this->executeURL = $action;
+
+ return $this;
+ }
+
+ /**
+ * returns the action which will handle the added and removed persons after saving the dialog.
+ *
+ * @return string action which will handle the form data.
+ */
+ public function getExecuteURL() {
+ return $this->executeURL;
+ }
+
+ /**
+ * sets a JavaScript-function to be fired when the user has pressed the submit-button.
+ * Arguments are:
+ * function fireme(id_of_item, text_of_item)
+ * example setting: MPS->setJSFunctionOnSubmit('fireme');
+ *
+ * @param string $function_name the name of the javascript function
+ *
+ * @return MultiPersonSearch
+ */
+ public function setJSFunctionOnSubmit($function_name)
+ {
+ $this->jsFunction = $function_name;
+ return $this;
+ }
+
+ /**
+ * returns a JavaScript-function which should be fired when the user has pressed the submit button.
+ *
+ * @return string function name
+ */
+ public function getJSFunctionOnSubmit()
+ {
+ return $this->jsFunction;
+ }
+
+ /**
+ * sets the search object.
+ *
+ * @param SearchType object of type SearchType (e.g. SQLSearch.class.php)
+ *
+ * @return MultiPersonSearch
+ */
+ public function setSearchObject($searchType) {
+ $this->searchObject = $searchType;
+
+ return $this;
+ }
+
+ /**
+ * returns the search object.
+ *
+ * @return SearchType
+ */
+ public function getSearchObject() {
+ return $this->searchObject;
+ }
+
+ /**
+ * sets html code which will be shown inside the form element.
+ *
+ * @param string html code
+ *
+ * @return MultiPersonSearch
+ */
+ public function setAdditionalHTML($html) {
+ $this->additionalHMTL = $html;
+
+ return $this;
+ }
+
+
+ /**
+ * enables or disabled data-dialog
+ * @param boolean $status
+ * @return $this
+ */
+ public function setDataDialogStatus($status) {
+ $this->dataDialogStatus = $status;
+
+ return $this;
+ }
+
+ /**
+ * returns if data-dialog is enabled or disabled
+ * @return bool
+ */
+ public function getDataDialogStatus() {
+ return $this->dataDialogStatus;
+ }
+ /**
+ * returns html code which will be shown inside the form element.
+ *
+ * @return string html code
+ */
+ public function getAdditionHTML() {
+ return $this->additionalHMTL;
+ }
+
+ /**
+ * returns an additional option array.
+ *
+ * @return string html code
+ */
+ public function getAdditionalOptionArray() {
+ return $_SESSION['multipersonsearch'][$this->name]['additional'];
+ }
+
+ /**
+ * sets the persons which will be shown as selectable by default on
+ * the left side of the dialoag.
+ *
+ * @param array array containing user-ids
+ */
+ public function setDefaultSelectableUser($userArray) {
+ $userArray = array_unique($userArray);
+ $this->defaultSelectableUsersIDs = [];
+ if (is_array($userArray)) {
+ foreach ($userArray as $userId) {
+ $this->defaultSelectableUsersIDs[] = $userId;
+ }
+ }
+ return $this;
+ }
+ /**
+ * returns the ids of defaultselectable users.
+ *
+ * @return array
+ */
+ public function getDefaultSelectableUsersIDs() {
+ return $this->defaultSelectableUsersIDs;
+ }
+
+ /**
+ * sets the persons which will be shown as selected by default on
+ * the right side of the dialoag.
+ *
+ * @param array array containing user-ids
+ */
+ public function setDefaultSelectedUser($userArray) {
+ $userArray = array_unique($userArray);
+ $this->defaultSelectedUsersIDs = [];
+ if (is_array($userArray)) {
+ foreach ($userArray as $userId) {
+ $this->defaultSelectedUsersIDs[] = $userId;
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * returns the ids of defaultselected users.
+ *
+ * @return array
+ */
+ public function getDefaultSelectedUsersIDs() {
+ return $this->defaultSelectedUsersIDs;
+ }
+
+
+ /**
+ * sets the title of the dialog.
+ *
+ * @param string $title title of the dialog
+ *
+ * @return MultiPersonSearch
+ */
+ public function setTitle($title) {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * returns the title.
+ *
+ * @return string
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+
+ /**
+ * sets the description of the dialog.
+ *
+ * @param string $desc description of the dialog
+ *
+ * @return MultiPersonSearch
+ */
+ public function setDescription($desc) {
+ $this->description = $desc;
+ return $this;
+ }
+
+ /**
+ * returns the description.
+ *
+ * @return string
+ */
+ public function getDescription() {
+ return $this->description;
+ }
+
+ /**
+ * returns the url of the page where the GUI element is added.
+ *
+ * @return string
+ */
+ public function getPageUrl() {
+ return $this->pageURL;
+ }
+
+ /**
+ * adds a new quickfilter.
+ *
+ * @param string $title title of the new quickfilter
+ * @param array $userArray containing all user-ids belonging to the quickfilter
+ *
+ * @return MultiPersonSearch
+ */
+ public function addQuickfilter($title, $userArray) {
+ $users = [];
+ $usersIds = [];
+ if (is_array($userArray)) {
+ foreach ($userArray as $userId) {
+ $usersIds[] = $userId;
+ }
+ }
+ $this->quickfilterIds[(string) $title] = $usersIds;
+
+ return $this;
+ }
+
+ /**
+ * returns the ids of quickfilters.
+ *
+ * @return array
+ */
+ public function getQuickfilterIds()
+ {
+ return $this->quickfilterIds ?: [];
+ }
+
+ /**
+ * clears all quickfilters.
+ *
+ * @return MultiPersonSearch
+ */
+ public function clearQuickfilters() {
+ $this->quickfilterIds = [];
+
+ return $this;
+ }
+
+ /**
+ * sets the navigation item.
+ *
+ * @param string $navigationItem navigation item
+ *
+ * @return MultiPersonSearch
+ */
+ public function setNavigationItem($navigationItem) {
+ $this->navigationItem = $navigationItem;
+
+ return $this;
+ }
+
+ /**
+ * returns the navigation item.
+ *
+ * @return string
+ */
+ public function getNavigationItem() {
+ return $this->navigationItem;
+ }
+
+ /**
+ * stores the internal data to a session.
+ */
+ public function storeToSession() {
+ $_SESSION['multipersonsearch'][$this->name]['title'] = $this->title;
+ $_SESSION['multipersonsearch'][$this->name]['description'] = $this->description;
+ $_SESSION['multipersonsearch'][$this->name]['additionalHMTL'] = $this->additionalHMTL;
+ $_SESSION['multipersonsearch'][$this->name]['executeURL'] = $this->executeURL;
+ $_SESSION['multipersonsearch'][$this->name]['jsFunction'] = $this->jsFunction;
+ $_SESSION['multipersonsearch'][$this->name]['pageURL'] = Request::url();
+ $_SESSION['multipersonsearch'][$this->name]['defaultSelectableUsersIDs'] = $this->defaultSelectableUsersIDs;
+ $_SESSION['multipersonsearch'][$this->name]['defaultSelectedUsersIDs'] = $this->defaultSelectedUsersIDs;
+ $_SESSION['multipersonsearch'][$this->name]['quickfilterIds'] = $this->quickfilterIds;
+ $_SESSION['multipersonsearch'][$this->name]['searchObject'] = serialize($this->searchObject);
+ $_SESSION['multipersonsearch'][$this->name]['navigationItem'] = $this->navigationItem;
+ $_SESSION['multipersonsearch'][$this->name]['dataDialogStatus'] = $this->dataDialogStatus;
+ }
+
+ /**
+ * restores the internal data from a session.
+ */
+ public function restoreFromSession() {
+ if (isset($_SESSION['multipersonsearch'][$this->name])) {
+ $this->title = $_SESSION['multipersonsearch'][$this->name]['title'] ?? '';
+ $this->description = $_SESSION['multipersonsearch'][$this->name]['description'] ?? '';
+ $this->quickfilterIds = $_SESSION['multipersonsearch'][$this->name]['quickfilterIds'] ?? [];
+ $this->additionalHMTL = $_SESSION['multipersonsearch'][$this->name]['additionalHMTL'] ?? '';
+ $this->executeURL = html_entity_decode($_SESSION['multipersonsearch'][$this->name]['executeURL'] ?? '');
+ $this->jsFunction = $_SESSION['multipersonsearch'][$this->name]['jsFunction'] ?? '';
+ $this->pageURL = $_SESSION['multipersonsearch'][$this->name]['pageURL'] ?? '';
+ $this->defaultSelectableUsersIDs = $_SESSION['multipersonsearch'][$this->name]['defaultSelectableUsersIDs'] ?? [];
+ $this->defaultSelectedUsersIDs = $_SESSION['multipersonsearch'][$this->name]['defaultSelectedUsersIDs'] ?? [];
+ $this->searchObject = unserialize($_SESSION['multipersonsearch'][$this->name]['searchObject'] ?? null);
+ $this->navigationItem = $_SESSION['multipersonsearch'][$this->name]['navigationItem'] ?? null;
+ $this->dataDialogStatus = $_SESSION['multipersonsearch'][$this->name]['dataDialogStatus'] ?? '';
+ }
+ }
+
+ /**
+ * clears the session data.
+ */
+ public function clearSession() {
+ unset($_SESSION['multipersonsearch'][$this->name]);
+ }
+
+ /**
+ * sets default values of the internal variables.
+ */
+ private function setDefaultValues() {
+ $this->title = '';
+ $this->description = _('Bitte wählen Sie aus, wen Sie hinzufügen möchten.');
+ $this->linkIconPath = Icon::create('add');
+ }
+
+ /**
+ * clear unused sessions.
+ */
+ private function collectGarbage() {
+ $maxLifeTime = 30; // minutes
+ foreach ($_SESSION['multipersonsearch'] as $key=>$value) {
+ if (time() - $value['lastUse'] > $maxLifeTime * 60) {
+ unset($_SESSION['multipersonsearch'][$key]);
+ }
+ }
+ }
+
+}
diff --git a/lib/classes/NotificationCenter.class.php b/lib/classes/NotificationCenter.class.php
deleted file mode 100644
index aaaa9e8..0000000
--- a/lib/classes/NotificationCenter.class.php
+++ /dev/null
@@ -1,174 +0,0 @@
- $predicate,
- 'observer' => [$observer, $method]
- ];
- }
-
- /**
- * Remove an object registered with the NotificationCenter.
- * Trying to remove an observer that was not registered is
- * allowed and has no effect.
- *
- * @param object $observer object to be removed
- * @param string $event name of event (may be NULL)
- * @param mixed $object subject to observe (may be NULL)
- */
- public static function removeObserver($observer, $event = NULL, $object = NULL)
- {
- if ($event === NULL) {
- $events = array_keys(self::$observers);
- } else if (isset(self::$observers[$event])) {
- $events = [$event];
- } else {
- return;
- }
-
- foreach ($events as $event) {
- foreach (self::$observers[$event] as $index => $list) {
- if ($object === NULL
- || $list['predicate'] && $list['predicate']($object)) {
-
- if ($list['observer'][0] === $observer) {
- unset(self::$observers[$event][$index]);
- }
- }
- }
- }
- }
-
- /**
- * Post an event notification to all registered observers.
- * Only observers registered for this event type and subject
- * are notified.
- *
- * @param string $event name of this notification
- * @param mixed $object subject of this notification
- * @param mixed $user_data additional information (optional)
- *
- * @throws NotificationVetoException on observer veto
- */
- public static function postNotification($event, $object, $user_data = null)
- {
- $current_observers = [];
- foreach (self::$observers as $e => $l) {
- if ($e === '' || fnmatch($e, $event, FNM_NOESCAPE)) {
- $current_observers = array_merge($current_observers, $l);
- }
- }
-
- foreach ($current_observers as $list) {
- if (!$list['predicate'] || $list['predicate']($object)) {
- call_user_func($list['observer'], $event, $object, $user_data);
- }
- }
- }
-
- /**
- * Convenience method that uses a jQuery like structure for event
- * registration by closures.
- *
- * @param string $event
- * @param Callable $callback
- * @param mixed $object
- * @since Stud.IP 4.2
- */
- public static function on($event, Callable $callback, $object = null)
- {
- if ($callback instanceof Closure || is_object($callback)) {
- static::addObserver($callback, '__invoke', $event, $object);
- } elseif (is_array($callback)) {
- static::addObserver($callback[0], $callback[1], $event, $object);
- } elseif (is_string($callback)) {
- throw new Exception('Strings as callable may not be passed to ' . __METHOD__);
- }
- }
-
- /**
- * Convenience method that uses a jQuery like structure for event
- * unregistration by closures.
- *
- * @param string $event
- * @param Callable $callback
- * @param mixed $object
- * @since Stud.IP 4.2
- */
- public static function off($event, Callable $callback, $object = null)
- {
- if ($callback instanceof Closure || is_object($callback)) {
- static::removeObserver($callback, $event, $object);
- } elseif (is_array($callback)) {
- static::removeObserver($callback[0], $event, $object);
- } elseif (is_string($callback)) {
- throw new Exception('Strings as callable may not be passed to ' . __METHOD__);
- }
- }
-}
diff --git a/lib/classes/NotificationCenter.php b/lib/classes/NotificationCenter.php
new file mode 100644
index 0000000..aaaa9e8
--- /dev/null
+++ b/lib/classes/NotificationCenter.php
@@ -0,0 +1,174 @@
+ $predicate,
+ 'observer' => [$observer, $method]
+ ];
+ }
+
+ /**
+ * Remove an object registered with the NotificationCenter.
+ * Trying to remove an observer that was not registered is
+ * allowed and has no effect.
+ *
+ * @param object $observer object to be removed
+ * @param string $event name of event (may be NULL)
+ * @param mixed $object subject to observe (may be NULL)
+ */
+ public static function removeObserver($observer, $event = NULL, $object = NULL)
+ {
+ if ($event === NULL) {
+ $events = array_keys(self::$observers);
+ } else if (isset(self::$observers[$event])) {
+ $events = [$event];
+ } else {
+ return;
+ }
+
+ foreach ($events as $event) {
+ foreach (self::$observers[$event] as $index => $list) {
+ if ($object === NULL
+ || $list['predicate'] && $list['predicate']($object)) {
+
+ if ($list['observer'][0] === $observer) {
+ unset(self::$observers[$event][$index]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Post an event notification to all registered observers.
+ * Only observers registered for this event type and subject
+ * are notified.
+ *
+ * @param string $event name of this notification
+ * @param mixed $object subject of this notification
+ * @param mixed $user_data additional information (optional)
+ *
+ * @throws NotificationVetoException on observer veto
+ */
+ public static function postNotification($event, $object, $user_data = null)
+ {
+ $current_observers = [];
+ foreach (self::$observers as $e => $l) {
+ if ($e === '' || fnmatch($e, $event, FNM_NOESCAPE)) {
+ $current_observers = array_merge($current_observers, $l);
+ }
+ }
+
+ foreach ($current_observers as $list) {
+ if (!$list['predicate'] || $list['predicate']($object)) {
+ call_user_func($list['observer'], $event, $object, $user_data);
+ }
+ }
+ }
+
+ /**
+ * Convenience method that uses a jQuery like structure for event
+ * registration by closures.
+ *
+ * @param string $event
+ * @param Callable $callback
+ * @param mixed $object
+ * @since Stud.IP 4.2
+ */
+ public static function on($event, Callable $callback, $object = null)
+ {
+ if ($callback instanceof Closure || is_object($callback)) {
+ static::addObserver($callback, '__invoke', $event, $object);
+ } elseif (is_array($callback)) {
+ static::addObserver($callback[0], $callback[1], $event, $object);
+ } elseif (is_string($callback)) {
+ throw new Exception('Strings as callable may not be passed to ' . __METHOD__);
+ }
+ }
+
+ /**
+ * Convenience method that uses a jQuery like structure for event
+ * unregistration by closures.
+ *
+ * @param string $event
+ * @param Callable $callback
+ * @param mixed $object
+ * @since Stud.IP 4.2
+ */
+ public static function off($event, Callable $callback, $object = null)
+ {
+ if ($callback instanceof Closure || is_object($callback)) {
+ static::removeObserver($callback, $event, $object);
+ } elseif (is_array($callback)) {
+ static::removeObserver($callback[0], $event, $object);
+ } elseif (is_string($callback)) {
+ throw new Exception('Strings as callable may not be passed to ' . __METHOD__);
+ }
+ }
+}
diff --git a/lib/classes/PrivacyObject.interface.php b/lib/classes/PrivacyObject.interface.php
deleted file mode 100644
index 8129634..0000000
--- a/lib/classes/PrivacyObject.interface.php
+++ /dev/null
@@ -1,11 +0,0 @@
-questionnaire.
- */
-interface QuestionType {
-
- /**
- * Returns a specific icon for this type of question. Note this is not bound to the
- * object but called staticly.
- * @return Icon the specific icon for this type of question
- */
- static public function getIcon(bool $active = false) : Icon;
-
-
- /**
- * Returns the shape of the icon that is used in vue.
- * @return string
- */
- static public function getIconShape();
-
- /**
- * Returns the name of the type of question like "Frage" or "Test" or "Dateiablage"
- * This name is not showed to the participant of the questionnaire, but to the editor.
- * It might get displayed like "add another Frage to questionnaire", where 'Frage'
- * is the name of the type.
- * @return string : the name of this type of question.
- */
- static public function getName();
-
- /**
- * Returns an array with two parts: First one is the name of the component for editing the question. Second
- * one is the import path of the component. Plugins can use this to get their component imported.
- * @return Array
- */
- static public function getEditingComponent();
-
- /**
- * Usually the $questiondata is already in the correct format. But for some question types
- * some data have to be manipulated by for example the HTML-purifier. So this takes
- * the questiondata and changed them before they get stored.
- * @param $questiondata
- * @return mixed
- */
- public function beforeStoringQuestiondata($questiondata);
-
- /**
- * Display the question to the user. This template will be embedded into a
- * html