diff options
| author | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
|---|---|---|
| committer | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
| commit | 4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch) | |
| tree | 5c07151ae61276d334e88f6309c30d439a85c12e /app | |
| parent | da0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff) | |
| parent | 97a188592c679890a25c37ab78463add76a52ff7 (diff) | |
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'app')
292 files changed, 2540 insertions, 11257 deletions
diff --git a/app/controllers/accessibility/forms.php b/app/controllers/accessibility/forms.php index f4f9adf..e240a6c 100644 --- a/app/controllers/accessibility/forms.php +++ b/app/controllers/accessibility/forms.php @@ -3,6 +3,21 @@ class Accessibility_FormsController extends StudipController { protected $with_session = true; + public function before_filter(&$action, &$args) + { + parent::before_filter($action, $args); + + if ( + Config::get()->REPORT_BARRIER_MODE === 'off' + || ( + Config::get()->REPORT_BARRIER_MODE === 'logged-in' + && !User::findCurrent() + ) + ) { + throw new AccessDeniedException(); + } + } + public function report_barrier_action() { PageLayout::setTitle(_('Barriere melden')); @@ -131,6 +146,9 @@ class Accessibility_FormsController extends StudipController } $this->form->addPart($personal_data_part); + + $this->form->addPart(new \Studip\Forms\Captcha()); + $this->form->setSaveButtonText(_('Barriere melden')); $this->form->setSaveButtonName('report'); $this->form->setURL($this->report_barrierURL()); diff --git a/app/controllers/activityfeed.php b/app/controllers/activityfeed.php index 2f93aa1..ee83826 100644 --- a/app/controllers/activityfeed.php +++ b/app/controllers/activityfeed.php @@ -53,7 +53,7 @@ class ActivityfeedController extends AuthenticatedController unset($modules[Context::INSTITUTE]['participants']); unset($modules[Context::INSTITUTE]['schedule']); - $standard_plugins = PluginManager::getInstance()->getPlugins("StandardPlugin"); + $standard_plugins = PluginManager::getInstance()->getPlugins(StandardPlugin::class); foreach ($standard_plugins as $plugin) { if ($plugin instanceof ActivityProvider) { $modules[Context::COURSE][$plugin->getPluginName()] = $plugin->getPluginName(); @@ -67,7 +67,7 @@ class ActivityfeedController extends AuthenticatedController 'blubber' => _('Blubber'), ]; - $homepage_plugins = PluginEngine::getPlugins('HomepagePlugin'); + $homepage_plugins = PluginEngine::getPlugins(HomepagePlugin::class); foreach ($homepage_plugins as $plugin) { if ($plugin->isActivated($GLOBALS['user']->id, 'user')) { if ($plugin instanceof ActivityProvider) { @@ -92,4 +92,193 @@ class ActivityfeedController extends AuthenticatedController PageLayout::setTitle(_('Aktivitäten konfigurieren')); } + + public function load_action(): void + { + $user = User::findCurrent(); + + // failsafe einbauen - falls es keine älteren Aktivitäten mehr im System gibt, Abbruch! + + $oldest_activity = \Studip\Activity\Activity::getOldestActivity(); + $max_age = $oldest_activity ? $oldest_activity->mkdate : time(); + + + $contexts = []; + + // create system context + $system_context = new \Studip\Activity\SystemContext($user); + $contexts[] = $system_context; + + $contexts[] = new \Studip\Activity\UserContext($user, $user); + $user->contacts->each(function ($another_user) use (&$contexts, $user) { + $contexts[] = new \Studip\Activity\UserContext($another_user, $user); + }); + + if (!in_array($user->perms, ['admin','root'])) { + // create courses and institutes context + foreach (\Course::findMany($user->course_memberships->pluck('seminar_id')) as $course) { + $contexts[] = new \Studip\Activity\CourseContext($course, $user); + } + foreach (\Institute::findMany($user->institute_memberships->pluck('institut_id')) as $institute) { + $contexts[] = new \Studip\Activity\InstituteContext($institute, $user); + } + } + + + // add filters + $filter = new \Studip\Activity\Filter(); + + $start = Request::int('start', strtotime('yesterday')); + $end = Request::int('end', time()); + + + $scrollfrom = Request::int('scrollfrom', false); + $filtertype = Request::get('filtertype', ''); + + $objectType = Request::get('object_type'); + $filter->setObjectType($objectType); + + $objectId = Request::get('object_id'); + $filter->setObjectId($objectId); + + $context = Request::get('context_type'); + $filter->setContext($context); + + $contextId = Request::get('context_id'); + $filter->setContextId($contextId); + + if (!empty($filtertype)) { + $filter->setType(json_decode($filtertype)); + } + + if ($scrollfrom) { + // shorten "watch-window" by one second to prevent duplication of activities + $scrollfrom -= 1; + + if ($scrollfrom > $max_age){ + $end = $scrollfrom; + $start = strtotime('yesterday', $end); + $data = []; + + $backtrack = 1; + + while (empty($data)) { + $filter->setStartDate($start); + $filter->setEndDate($end); + + $data = $this->getStreamData($contexts, $filter); + + if ($start < $max_age) { + break; + } + + // move "watch-window" back one day at a time + $end = $start - 1; + $start = strtotime("-{$backtrack} days", $start); + + // enforce maximum "watch-window", currently 2 weeks + $backtrack = min(14, $backtrack + 1); + } + } else { + $data = false; + } + } else { + $filter->setStartDate($start); + $filter->setEndDate($end); + $data = $this->getStreamData($contexts, $filter); + } + + // set etag for preventing resending the same stuff over and over again + $etag = md5(serialize($data)); + $this->response->add_header('ETag', '"' . $etag . '"'); + if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $this->etagMatches($etag, $_SERVER['HTTP_IF_NONE_MATCH'])) { + $this->set_status(304); + $this->render_nothing(); + return; + } + if (isset($_SERVER['HTTP_IF_MATCH']) && !$this->etagMatches($etag, $_SERVER['HTTP_IF_MATCH'])) { + $this->set_status(412); + $this->render_nothing(); + return; + } + + $this->render_json($data); + } + + /** + * private helper function to get stream data for given contexts and filter + * + * @param $contexts + * @param $filter + * @return array + */ + + private function getStreamData($contexts, $filter): array + { + $stream = new Studip\Activity\Stream($contexts, $filter); + $data = $stream->toArray(); + + foreach ($data as $key => $act) { + $actor = [ + 'type' => $act['actor_type'], + 'id' => $act['actor_id'], + ]; + + if ($act['actor_type'] == 'user') { + $a_user = \User::findFull($act['actor_id']); + $actor['details'] = $this->getMiniUser($a_user ?: new \User()); + } elseif ($act['actor_type'] === 'anonymous') { + $actor['details'] = [ + 'name' => _('Anonym'), + ]; + } + + unset($data[$key]['actor_type']); + unset($data[$key]['actor_id']); + + $data[$key]['actor'] = $actor; + } + + return $data; + } + + private function getMiniUser(User $user): array + { + $avatar = \Avatar::getAvatar($user->id); + + return [ + 'id' => $user->id, + 'name' => $this->getNamesOfUser($user), + 'avatar_small' => $avatar->getURL(\Avatar::SMALL), + 'avatar_medium' => $avatar->getURL(\Avatar::MEDIUM), + 'avatar_normal' => $avatar->getURL(\Avatar::NORMAL), + 'avatar_original' => $avatar->getURL(\Avatar::NORMAL) + ]; + } + + private function getNamesOfUser(User $user): array + { + return [ + 'username' => $user->username, + 'formatted' => $user->getFullName(), + 'family' => $user->nachname, + 'given' => $user->vorname, + 'prefix' => $user->title_front, + 'suffix' => $user->title_rear, + ]; + } + + // Helper method checking if a ETag value list includes the current ETag. + private function etagMatches(string $etag, string $list) + { + if ($list === '*') { + return true; + } + + return in_array( + $etag, + preg_split('/\s*,\s*/', $list) + ); + } + } diff --git a/app/controllers/admin/additional.php b/app/controllers/admin/additional.php index a98da20..6f8f08c 100644 --- a/app/controllers/admin/additional.php +++ b/app/controllers/admin/additional.php @@ -30,6 +30,8 @@ class Admin_AdditionalController extends AuthenticatedController throw new AccessDeniedException(_("Sie haben keine Berechtigung diese " . "Veranstaltung zu verändern.")); } + + Sidebar::get()->addWidget(new CourseManagementSelectWidget()); } /** diff --git a/app/controllers/admin/api.php b/app/controllers/admin/api.php deleted file mode 100644 index 96adb65..0000000 --- a/app/controllers/admin/api.php +++ /dev/null @@ -1,210 +0,0 @@ -<?php -/** - * - **/ -class Admin_ApiController extends AuthenticatedController -{ - /** - * - **/ - public function before_filter(&$action, &$args) - { - parent::before_filter($action, $args); - - require_once 'lib/bootstrap-api.php'; - - $GLOBALS['perm']->check('root'); - - Navigation::activateItem('/admin/config/api'); - PageLayout::setTitle(_('API Verwaltung')); - - $this->types = [ - 'website' => _('Website'), - 'desktop' => _('Herkömmliches Desktopprogramm'), - 'mobile' => _('Mobile App') - ]; - - // Sidebar - $views = new ViewsWidget(); - $views->addLink(_('Registrierte Applikationen'), - $this->url_for('admin/api')) - ->setActive($action === 'index'); - $views->addLink(_('Globale Zugriffseinstellungen'), - $this->url_for('admin/api/permissions')) - ->setActive($action == 'permissions'); - $views->addLink(_('Konfiguration'), - $this->url_for('admin/api/config')) - ->setActive($action == 'config'); - Sidebar::get()->addWidget($views); - - $actions = new ActionsWidget(); - $actions->addLink(_('Neue Applikation registrieren'), - $this->url_for('admin/api/edit'), - Icon::create('add', 'clickable')) - ->asDialog(); - Sidebar::get()->addWidget($actions); - } - - /** - * - **/ - public function index_action() - { - $this->consumers = RESTAPI\Consumer\Base::findAll(); - $this->routes = RESTAPI\Router::getInstance()->getRoutes(true); - } - - /** - * - **/ - public function render_keys($id) - { - $consumer = RESTAPI\Consumer\Base::find($id); - - return [ - 'Consumer Key = ' . $consumer->auth_key, - 'Consumer Secret = ' . $consumer->auth_secret, - ]; - } - - /** - * - **/ - public function keys_action($id) - { - $details = $this->render_keys($id); - - if (Request::isXhr()) { - $this->render_text(implode('<br>', $details)); - } else { - PageLayout::postMessage(MessageBox::info(_('Die Schlüssel in den Details dieser Meldung sollten vertraulich behandelt werden!'), $details, true)); - $this->redirect('admin/api/#' . $id); - } - } - - /** - * - **/ - public function edit_action($id = null) - { - $consumer = $id - ? RESTAPI\Consumer\Base::find($id) - : RESTAPI\Consumer\Base::create(Request::option('consumer_type') ?: 'oauth'); - - if (Request::submitted('store')) { - $errors = []; - - $consumer->active = (bool) Request::int('active'); - $consumer->title = Request::get('title'); - $consumer->contact = Request::get('contact'); - $consumer->email = Request::get('email'); - $consumer->callback = Request::get('callback'); - $consumer->url = Request::get('url'); - $consumer->type = Request::get('type') ?: null; - $consumer->commercial = Request::int('commercial'); - $consumer->notes = Request::get('notes'); - $consumer->description = Request::get('description'); - - if (!empty($errors)) { - $message = MessageBox::error(_('Folgende Fehler sind aufgetreten:'), $errors); - PageLayout::postMessage($message); - return; - } - - $consumer->store(); - - if ($id) { - $message = MessageBox::success(_('Die Applikation wurde erfolgreich gespeichert.')); - } else { - $details = $this->render_keys($consumer->id); - $message = MessageBox::success(_('Die Applikation wurde erfolgreich erstellt, die Schlüssel finden Sie in den Details dieser Meldung.'), $details, true); - } - PageLayout::postMessage($message); - $this->redirect('admin/api/index#' . $consumer->id); - return; - } - - $this->consumer = $consumer; - $this->id = $id; - } - - /** - * - **/ - public function toggle_action($id, $state = null) - { - $consumer = RESTAPI\Consumer\Base::find($id); - - $consumer->active = $state === null ? !$consumer->active : ($state === 'on'); - $consumer->store(); - - $message = $state - ? _('Die Applikation wurde erfolgreich aktiviert.') - : _('Die Applikation wurde erfolgreich deaktiviert.'); - - PageLayout::postMessage(MessageBox::success($message)); - $this->redirect('admin/api/#' . $consumer->id); - } - - /** - * - **/ - public function delete_action($id) - { - if (!Request::isPost()) { - throw new MethodNotAllowedException(); - } - if ($consumer = RESTAPI\Consumer\Base::find($id)) { - $consumer->delete(); - - PageLayout::postSuccess(_('Die Applikation wurde erfolgreich gelöscht.')); - } - $this->redirect('admin/api'); - } - - /** - * - **/ - public function permissions_action($consumer_id = null) - { - if (Request::submitted('store')) { - $perms = Request::getArray('permission'); - $permissions = RESTAPI\ConsumerPermissions::get($consumer_id ?: 'global'); - - foreach ($perms as $route => $methods) { - foreach ($methods as $method => $granted) { - $permissions->set(urldecode($route), urldecode($method), (bool)$granted, true); - } - } - - $permissions->store(); - - PageLayout::postMessage(MessageBox::success(_('Die Zugriffsberechtigungen wurden erfolgreich gespeichert'))); - $this->redirect($consumer_id ? 'admin/api' : 'admin/api/permissions'); - return; - } - - $title = $consumer_id ? _('Zugriffsberechtigungen') : _('Globale Zugriffsberechtigungen'); - $title .= ' - ' . PageLayout::getTitle(); - PageLayout::setTitle($title); - - $this->consumer_id = $consumer_id; - $this->router = RESTAPI\Router::getInstance(); - $this->routes = $this->router->getRoutes(true, false); - $this->permissions = RESTAPI\ConsumerPermissions::get($consumer_id ?: 'global'); - $this->global = $consumer_id ? RESTAPI\ConsumerPermissions::get('global') : false; - } - - public function config_action() - { - $this->config = Config::get(); - - if (Request::isPost()) { - $this->config->store('API_ENABLED', Request::int('active', 0)); - $this->config->store('API_OAUTH_AUTH_PLUGIN', Request::option('auth')); - - PageLayout::postMessage(MessageBox::success(_('Die Einstellungen wurden gespeichert.'))); - $this->redirect('admin/api/config'); - } - } -} diff --git a/app/controllers/admin/cache.php b/app/controllers/admin/cache.php index 329aab7..c016af5 100644 --- a/app/controllers/admin/cache.php +++ b/app/controllers/admin/cache.php @@ -81,11 +81,10 @@ class Admin_CacheController extends AuthenticatedController /** * Fetches necessary configuration for given cache type. - * - * @param string $className */ - public function get_config_action($className) + public function get_config_action() { + $className = Request::get('cache'); $type = CacheType::findOneByClass_name($className); $this->render_json($type->class_name::getConfig()); @@ -111,7 +110,7 @@ class Admin_CacheController extends AuthenticatedController // Store settings to global config. if (Config::get()->store('SYSTEMCACHE', $settings)) { PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.')); - StudipCacheFactory::unconfigure(); + \Studip\Cache\Factory::unconfigure(); } $this->relocate('admin/cache/settings'); @@ -122,7 +121,7 @@ class Admin_CacheController extends AuthenticatedController */ public function flush_action() { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $cache->flush(); PageLayout::postSuccess(_('Die Inhalte des Caches wurden gelöscht.')); @@ -135,7 +134,7 @@ class Admin_CacheController extends AuthenticatedController */ public function stats_action() { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $this->stats = $cache->getStats(); } diff --git a/app/controllers/admin/courseplanning.php b/app/controllers/admin/courseplanning.php index b8971e2..44b372d 100644 --- a/app/controllers/admin/courseplanning.php +++ b/app/controllers/admin/courseplanning.php @@ -82,7 +82,7 @@ class Admin_CourseplanningController extends AuthenticatedController foreach ($this->events as $event) { $start_date_time = explode('T', $event['start']); $time_elements = explode(':', $start_date_time[1]); - if (!$event['comform'] || $time_elements[0] % 2) { + if (!$event['conform'] || $time_elements[0] % 2) { Sidebar::get()->getWidget('actions')->addLink( _('Veranstaltungen außerhalb des Rasters'), $this->nonconformURL(), diff --git a/app/controllers/admin/courses.php b/app/controllers/admin/courses.php index 6f9312d..5f60442 100644 --- a/app/controllers/admin/courses.php +++ b/app/controllers/admin/courses.php @@ -22,6 +22,7 @@ * @category Stud.IP * @since 3.1 */ + require_once 'lib/meine_seminare_func.inc.php'; require_once 'lib/object.inc.php'; require_once 'lib/archiv.inc.php'; //for lastActivity in getCourses() method @@ -293,7 +294,7 @@ class Admin_CoursesController extends AuthenticatedController PageLayout::setTitle(_('Verwaltung von Veranstaltungen und Einrichtungen')); // Add admission functions. PageLayout::addScript('studip-admission.js'); - $this->max_show_courses = 500; + $this->max_show_courses = Config::get()->MAX_SHOW_ADMIN_COURSES; } /** @@ -322,6 +323,9 @@ class Admin_CoursesController extends AuthenticatedController $institut_id = $configuration->MY_INSTITUTES_DEFAULT && $configuration->MY_INSTITUTES_DEFAULT !== 'all' ? $configuration->MY_INSTITUTES_DEFAULT : null; + if ($configuration->MY_INSTITUTES_INCLUDE_CHILDREN) { + $institut_id .= '_withinst'; + } $filters = array_merge( array_merge(...PluginEngine::sendMessage(AdminCourseWidgetPlugin::class, 'getFilters')), @@ -375,14 +379,14 @@ class Admin_CoursesController extends AuthenticatedController } PluginEngine::sendMessage(AdminCourseWidgetPlugin::class, 'applyFilters', $filter); - $count = $filter->countCourses(); - if ($count > $this->max_show_courses && !Request::submitted('without_limit')) { - $this->render_json([ - 'count' => $count - ]); + try { + $courses = $filter->fetchCourses( + Request::bool('without_limit') ? null: $this->max_show_courses + ); + } catch (OverflowException $e) { + $this->render_json(['count' => (int) $e->getMessage()]); return; } - $courses = AdminCourseFilter::get()->getCourses(); $data = [ 'data' => [] @@ -417,7 +421,7 @@ class Admin_CoursesController extends AuthenticatedController } } } - $tf = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/app/views'); + $tf = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/app/views'); switch ($GLOBALS['user']->cfg->MY_COURSES_ACTION_AREA) { case 1: case 2: @@ -487,12 +491,12 @@ class Admin_CoursesController extends AuthenticatedController ]); break; default: - foreach (PluginManager::getInstance()->getPlugins('AdminCourseAction') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(AdminCourseAction::class) as $plugin) { if ($GLOBALS['user']->cfg->MY_COURSES_ACTION_AREA === get_class($plugin)) { $multimode = $plugin->useMultimode(); if ($multimode) { $data['buttons_top'] = '<label>'._('Alle auswählen').'<input type="checkbox" data-proxyfor=".course-admin td:last-child :checkbox"></label>'; - if ($multimode instanceof Flexi_Template) { + if ($multimode instanceof Flex\Template) { $data['buttons_bottom'] = $multimode->render(); } elseif ($multimode instanceof \Studip\Button) { $data['buttons_bottom'] = (string) $multimode; @@ -531,14 +535,60 @@ class Admin_CoursesController extends AuthenticatedController 'institut_id' => 'MY_INSTITUTES_DEFAULT', ]; + if (!empty($filters['institut_id'])) { + $config->store( + 'MY_INSTITUTES_INCLUDE_CHILDREN', + str_contains($filters['institut_id'], '_') ? 1 : 0 + ); + if ($config->MY_INSTITUTES_INCLUDE_CHILDREN) { + $filters['institut_id'] = substr($filters['institut_id'], 0, strpos($filters['institut_id'], '_')); + } + } + foreach ($mapping as $key => $field) { if (isset($filters[$key])) { $config->store($field, $filters[$key]); } - unset($filters[$key]); } + if ($config->ADMIN_COURSES_TEACHERFILTER) { + if (!$config->MY_INSTITUTES_DEFAULT) { + $config->delete('ADMIN_COURSES_TEACHERFILTER'); + } else { + $include_children = $GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN ? ' OR Institute.fakultaets_id = :institut_id ' : ''; + + $exists = InstituteMember::countBySQL("INNER JOIN `Institute` USING (`Institut_id`) WHERE `user_inst`.`user_id` = :user_id AND (`Institute`.`Institut_id` = :institut_id $include_children) AND `user_inst`.`inst_perms` = 'dozent' ", [ + 'user_id' => $config->ADMIN_COURSES_TEACHERFILTER, + 'institut_id' => $config->MY_INSTITUTES_DEFAULT + ]) > 0; + if (!$exists) { + $config->delete('ADMIN_COURSES_TEACHERFILTER'); + } + } + } + if ($config->MY_COURSES_SELECTED_STGTEIL) { + if (!$config->MY_INSTITUTES_DEFAULT) { + $config->delete('MY_COURSES_SELECTED_STGTEIL'); + } else { + $statement = DBManager::get()->prepare(" + SELECT 1 + FROM `mvv_stg_stgteil` + INNER JOIN `mvv_studiengang` ON (`mvv_stg_stgteil`.`studiengang_id` = `mvv_studiengang`.`studiengang_id`) + WHERE `mvv_studiengang`.`institut_id` = :institut_id + AND `mvv_stg_stgteil`.`stgteil_id` = :stgteil_id + "); + $statement->execute([ + 'institut_id' => $config->MY_INSTITUTES_DEFAULT, + 'stgteil_id' => $config->MY_COURSES_SELECTED_STGTEIL + ]); + $exists = (bool) $statement->fetch(PDO::FETCH_COLUMN); + if (!$exists) { + $config->delete('MY_COURSES_SELECTED_STGTEIL'); + } + } + } + // Datafield filters $activeSidebarElements = $this->getActiveElements(); @@ -576,19 +626,19 @@ class Admin_CoursesController extends AuthenticatedController if (in_array('name', $activated_fields)) { $params = tooltip2(_('Veranstaltungsdetails anzeigen')); $params['style'] = 'cursor: pointer'; - $d['name'] = '<a href="'.URLHelper::getLink('seminar_main.php', ['auswahl' => $course->id]).'">' + $d['name'] = '<a href="'.URLHelper::getLink('dispatch.php/course/basicdata/view', ['cid' => $course->id]).'">' . htmlReady($course->name) .'</a> ' .'<a href="'.URLHelper::getLink('dispatch.php/course/details/index/'. $course->id).'" data-dialog><button class="undecorated">'.Icon::create('info-circle', Icon::ROLE_INACTIVE)->asImg($params).'</button></a> ' .(!$course->visible ? _('(versteckt)') : ''); } if (in_array('number', $activated_fields)) { - $d['number'] = '<a href="'.URLHelper::getLink('seminar_main.php', ['auswahl' => $course->id]).'">' + $d['number'] = '<a href="'.URLHelper::getLink('dispatch.php/course/basicdata/view', ['cid' => $course->id]).'">' .$course->veranstaltungsnummer .'</a>'; } if (in_array('avatar', $activated_fields)) { - $d['avatar'] = '<a href="'.URLHelper::getLink('seminar_main.php', ['auswahl' => $course->id]).'">' + $d['avatar'] = '<a href="'.URLHelper::getLink('dispatch.php/course/basicdata/view', ['cid' => $course->id]).'">' .CourseAvatar::getAvatar($course->getId())->getImageTag(Avatar::SMALL, ['title' => $course->name]) ."</a>"; } @@ -604,6 +654,7 @@ class Admin_CoursesController extends AuthenticatedController } if (in_array('semester', $activated_fields)) { $d['semester'] = $course->semester_text; + $d['semester_sort'] = $course->start_semester ? $course->start_semester->beginn : 0; } if (in_array('institute', $activated_fields)) { $d['institute'] = $course->home_institut ? $course->home_institut->name : $course->institute; @@ -620,7 +671,7 @@ class Admin_CoursesController extends AuthenticatedController } if (in_array('members', $activated_fields)) { $d['members'] = '<a href="'.URLHelper::getLink('dispatch.php/course/members', ['cid' => $course->id]).'">' - .$course->getNumParticipants() + .$course->countMembersWithStatus('user autor') .'</a>'; } if (in_array('waiting', $activated_fields)) { @@ -663,15 +714,18 @@ class Admin_CoursesController extends AuthenticatedController $d['last_activity_raw'] = $last_activity; } - foreach (PluginManager::getInstance()->getPlugins('AdminCourseContents') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(AdminCourseContents::class) as $plugin) { foreach ($plugin->adminAvailableContents() as $index => $label) { if (in_array($plugin->getPluginId() . '_' . $index, $activated_fields)) { $content = $plugin->adminAreaGetCourseContent($course, $index); - $d[$plugin->getPluginId()."_".$index] = $content instanceof Flexi_Template ? $content->render() : $content; + if ($content instanceof Flexi\Template) { + $content = $content->render(); + } + $d[$plugin->getPluginId()."_".$index] = $content; } } } - $tf = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'].'/app/views'); + $tf = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'].'/app/views'); switch ($GLOBALS['user']->cfg->MY_COURSES_ACTION_AREA) { case 1: @@ -789,10 +843,13 @@ class Admin_CoursesController extends AuthenticatedController $d['action'] = $template->render(); break; default: - foreach (PluginManager::getInstance()->getPlugins('AdminCourseAction') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(AdminCourseAction::class) as $plugin) { if ($GLOBALS['user']->cfg->MY_COURSES_ACTION_AREA === get_class($plugin)) { $output = $plugin->getAdminCourseActionTemplate($course->getId()); - $d['action'] = $output instanceof Flexi_Template ? $output->render() : (string) $output; + if ($output instanceof Flexi\Template) { + $output = $output->render(); + } + $d['action'] = (string) $output; break; } } @@ -981,14 +1038,14 @@ class Admin_CoursesController extends AuthenticatedController $row['institute'] = $course->home_institut ? (string) $course->home_institut['name'] : $course['institut_id']; } - foreach (PluginManager::getInstance()->getPlugins('AdminCourseContents') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(AdminCourseContents::class) as $plugin) { foreach ($plugin->adminAvailableContents() as $index => $label) { if (in_array($plugin->getPluginId() . "_" . $index, $filter_config)) { $content = $plugin->adminAreaGetCourseContent($course, $index); - $row[$plugin->getPluginId() . "_" . $index] = strip_tags(is_a($content, 'Flexi_Template') - ? $content->render() - : $content - ); + if ($content instanceof Flexi\Template) { + $content = $content->render(); + } + $row[$plugin->getPluginId() . "_" . $index] = strip_tags($content); } } } @@ -1000,7 +1057,7 @@ class Admin_CoursesController extends AuthenticatedController foreach ($filter_config as $index) { $captions[$index] = $view_filters[$index]; } - foreach (PluginManager::getInstance()->getPlugins('AdminCourseContents') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(AdminCourseContents::class) as $plugin) { foreach ($plugin->adminAvailableContents() as $index => $label) { if (in_array($plugin->getPluginId() . "_" . $index, $filter_config)) { $captions[$plugin->getPluginId() . "_" . $index] = $label; @@ -1398,7 +1455,7 @@ class Admin_CoursesController extends AuthenticatedController ksort($actions); - foreach (PluginManager::getInstance()->getPlugins('AdminCourseAction') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(AdminCourseAction::class) as $plugin) { $actions[get_class($plugin)] = [ 'name' => $plugin->getPluginName(), 'title' => $plugin->getPluginName(), @@ -1438,7 +1495,7 @@ class Admin_CoursesController extends AuthenticatedController 'contents' => _('Inhalt'), 'last_activity' => _('Letzte Aktivität'), ]; - foreach (PluginManager::getInstance()->getPlugins('AdminCourseContents') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(AdminCourseContents::class) as $plugin) { foreach ($plugin->adminAvailableContents() as $index => $label) { $views[$plugin->getPluginId() . "_" . $index] = $label; } @@ -1446,169 +1503,6 @@ class Admin_CoursesController extends AuthenticatedController return $views; } - /** - * Returns all courses matching set criteria. - * - * @param array $params Additional parameters - * @param bool $display_all : boolean should we show all courses or check for a limit of 500 courses? - * @return array of courses - */ - private function getCourses($params = [], $display_all = false) - { - // Init - if ($GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === "all") { - $inst = new SimpleCollection($this->insts); - $inst->filter(function ($a) use (&$inst_ids) { - $inst_ids[] = $a->Institut_id; - }); - } else { - //We must check, if the institute ID belongs to a faculty - //and has the string _i appended to it. - //In that case we must display the courses of the faculty - //and all its institutes. - //Otherwise we just display the courses of the faculty. - - $inst_id = $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT; - - $institut = new Institute($inst_id); - - if (!$institut->isFaculty() || $GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN) { - // If the institute is not a faculty or the child insts are included, - // pick the institute IDs of the faculty/institute and of all sub-institutes. - $inst_ids[] = $inst_id; - if ($institut->isFaculty()) { - foreach ($institut->sub_institutes->pluck('Institut_id') as $institut_id) { - $inst_ids[] = $institut_id; - } - } - } else { - // If the institute is a faculty and the child insts are not included, - // pick only the institute id of the faculty: - $inst_ids[] = $inst_id; - } - } - - $active_elements = $this->getActiveElements(); - - $filter = AdminCourseFilter::get(true); - - if ($params['datafields']) { - foreach ($params['datafields'] as $field_id => $value) { - $datafield = DataField::find($field_id); - if ($datafield) { - //enable filtering by datafield values: - //and use the where-clause for each datafield: - $filter->settings['query']['joins']['de_'.$field_id] = [ - 'table' => "datafields_entries", - 'join' => "LEFT JOIN", - 'on' => "seminare.seminar_id = de_".$field_id.".range_id" - ]; - $filter->where("(de_".$field_id.".datafield_id = :fieldId_".$field_id." " - . "AND de_".$field_id.".content = :fieldValue_".$field_id.") " - . ($datafield['default_value'] == $value ? " OR (de_".$field_id.".content IS NULL)" : "")." ", - [ - 'fieldId_'.$field_id => $field_id, - 'fieldValue_'.$field_id => $value - ] - ); - } - } - } - - $filter->where("sem_classes.studygroup_mode = '0'"); - - // Get only children of given course - if (!empty($params['parent_course'])) { - $filter->where("parent_course = :parent", - [ - 'parent' => $params['parent_course'] - ] - ); - } - - if ($active_elements['semester'] && is_object($this->semester)) { - $filter->filterBySemester($this->semester->getId()); - } - if ($active_elements['courseType'] && $params['typeFilter'] && $params['typeFilter'] !== "all") { - $parts = explode('_', $params['typeFilter']); - $class_filter = $parts[0]; - $type_filter = $parts[1] ?? null; - if (!$type_filter && !empty($GLOBALS['SEM_CLASS'][$class_filter])) { - $type_filter = array_keys($GLOBALS['SEM_CLASS'][$class_filter]->getSemTypes()); - } - $filter->filterByType($type_filter); - } - if ($active_elements['search'] && $GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT) { - $filter->filterBySearchString($GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT); - } - if ($active_elements['teacher'] && $GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER && ($GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER !== "all")) { - $filter->filterByDozent($GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER); - } - if ($active_elements['institute']) { - $filter->filterByInstitute($inst_ids); - } - if ($GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL && $GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL !== 'all') { - $filter->filterByStgTeil($GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL); - } - if ($params['sortby'] === "status") { - $filter->orderBy(sprintf('sem_classes.name %s, sem_types.name %s, VeranstaltungsNummer %s', $params['sortFlag'], $params['sortFlag'], $params['sortFlag']), $params['sortFlag']); - } elseif ($params['sortby'] === 'institute') { - $filter->orderBy('Institute.Name', $params['sortFlag']); - } elseif ($params['sortby']) { - $filter->orderBy($params['sortby'], $params['sortFlag']); - } - $filter->storeSettings(); - $this->count_courses = $filter->countCourses(); - if ($this->count_courses && ($this->count_courses <= $filter->max_show_courses || $display_all)) { - $courses = $filter->getCourses(); - } else { - return []; - } - - $seminars = []; - if (!empty($courses)) { - foreach ($courses as $seminar_id => $seminar) { - $seminars[$seminar_id] = $seminar[0]; - $seminars[$seminar_id]['seminar_id'] = $seminar_id; - $seminars[$seminar_id]['obj_type'] = 'sem'; - $dozenten = $this->getTeacher($seminar_id); - $seminars[$seminar_id]['dozenten'] = $dozenten; - - if (in_array('contents', $params['view_filter'])) { - $tools = new SimpleCollection(ToolActivation::findbyRange_id($seminar_id, "ORDER BY position")); - $visit_data = get_objects_visits([$seminar_id], 0, null, null, $tools->pluck('plugin_id')); - $seminars[$seminar_id]['visitdate'] = $visit_data[$seminar_id][0]['visitdate']; - $seminars[$seminar_id]['last_visitdate'] = $visit_data[$seminar_id][0]['last_visitdate']; - $seminars[$seminar_id]['tools'] = $tools; - $seminars[$seminar_id]['navigation'] = MyRealmModel::getAdditionalNavigations( - $seminar_id, - $seminars[$seminar_id], - $seminars[$seminar_id]['sem_class'] ?? null, - $GLOBALS['user']->id, - $visit_data[$seminar_id] - ); - } - //add last activity column: - if (in_array('last_activity', $params['view_filter'])) { - $seminars[$seminar_id]['last_activity'] = lastActivity($seminar_id); - } - if ((int)$this->selected_action === 17) { - $seminars[$seminar_id]['admission_locked'] = false; - if ($seminar[0]['course_set']) { - $set = new CourseSet($seminar[0]['course_set']); - if (!is_null($set) && $set->hasAdmissionRule('LockedAdmission')) { - $seminars[$seminar_id]['admission_locked'] = 'locked'; - } else { - $seminars[$seminar_id]['admission_locked'] = 'disable'; - } - unset($set); - } - } - } - } - - return $seminars; - } /** * Returns the teacher for a given cours @@ -1650,6 +1544,7 @@ class Admin_CoursesController extends AuthenticatedController $institut['Institut_id'], (!$institut['is_fak'] ? ' ' : '') . $institut['Name'], $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === $institut['Institut_id'] + && !$GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN ); //check if the institute is a faculty. @@ -1662,7 +1557,8 @@ class Admin_CoursesController extends AuthenticatedController new SelectElement( $institut['Institut_id'] . '_withinst', //_withinst = with institutes ' ' . $institut['Name'] . ' +' . _('Institute'), - ($GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === $institut['Institut_id'] && $GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN) + $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === $institut['Institut_id'] + && $GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN ); } } @@ -1695,6 +1591,9 @@ class Admin_CoursesController extends AuthenticatedController private function getStgteilSelector($institut_id = null) { $institut_id = $institut_id ?: $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT; + if (str_contains($institut_id, '_')) { + $institut_id = substr($institut_id, 0, strpos($institut_id, '_')); + } $stgteile = StudiengangTeil::getAllEnriched('fach_name', 'ASC', ['mvv_fach_inst.institut_id' => $institut_id]); $list = []; if (!$institut_id || $institut_id === 'all') { @@ -1782,27 +1681,42 @@ class Admin_CoursesController extends AuthenticatedController */ private function getTeacherWidget($institut_id = null) { - $institut_id = $institut_id ?: $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT; - $teachers = DBManager::get()->fetchAll(" + if ($institut_id) { + if (str_contains($institut_id, '_')) { + $institut_id = substr($institut_id, 0, strpos($institut_id, '_')); + $GLOBALS['user']->cfg->store('MY_INSTITUTES_INCLUDE_CHILDREN', 1); + } else { + $GLOBALS['user']->cfg->store('MY_INSTITUTES_INCLUDE_CHILDREN', 0); + } + } else { + $institut_id = $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT; + } + + $teachers = []; + $include_children = $GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN ? ' OR Institute.fakultaets_id = :institut_id ' : ''; + + if ($institut_id) { + $teachers = DBManager::get()->fetchAll(" SELECT auth_user_md5.*, user_info.* FROM auth_user_md5 LEFT JOIN user_info ON (auth_user_md5.user_id = user_info.user_id) INNER JOIN user_inst ON (user_inst.user_id = auth_user_md5.user_id) INNER JOIN Institute ON (Institute.Institut_id = user_inst.Institut_id) - WHERE (Institute.Institut_id = :institut_id OR Institute.fakultaets_id = :institut_id) - AND auth_user_md5.perms = 'dozent' + WHERE (Institute.Institut_id = :institut_id $include_children) + AND user_inst.inst_perms = 'dozent' + GROUP BY auth_user_md5.user_id ORDER BY auth_user_md5.Nachname ASC, auth_user_md5.Vorname ASC ", [ 'institut_id' => $institut_id ], - function ($data) { - $ret['user_id'] = $data['user_id']; - unset($data['user_id']); - $ret['fullname'] = User::build($data)->getFullName("full_rev"); - return $ret; - } - ); - + function ($data) { + $ret['user_id'] = $data['user_id']; + unset($data['user_id']); + $ret['fullname'] = User::build($data)->getFullName("full_rev"); + return $ret; + } + ); + } $list = []; if (!$institut_id || $institut_id === 'all') { diff --git a/app/controllers/admin/cronjobs/schedules.php b/app/controllers/admin/cronjobs/schedules.php index 294d78d..929ae29 100644 --- a/app/controllers/admin/cronjobs/schedules.php +++ b/app/controllers/admin/cronjobs/schedules.php @@ -29,7 +29,7 @@ class Admin_Cronjobs_SchedulesController extends AuthenticatedController if (empty($_SESSION['cronjob-filter'])) { $_SESSION['cronjob-filter'] = [ 'where' => '1', - 'values' => array_fill_keys(['type', 'status', 'task_id'], null), + 'values' => array_fill_keys(['status', 'task_id'], null), ]; } @@ -102,9 +102,6 @@ class Admin_Cronjobs_SchedulesController extends AuthenticatedController $filter = array_filter(Request::optionArray('filter')); $conditions = []; - if (!empty($filter['type'])) { - $conditions[] = "type = " . DBManager::get()->quote($filter['type']); - } if (!empty($filter['status'])) { $active = (int)($filter['status'] === 'active'); $conditions[] = "active = " . DBManager::get()->quote($active); @@ -130,7 +127,6 @@ class Admin_Cronjobs_SchedulesController extends AuthenticatedController if (Request::submitted('store')) { $parameters = Request::getArray('parameters'); - $schedule->priority = Request::option('priority', 'normal'); $schedule->title = Request::get('title'); $schedule->description = Request::get('description'); $schedule->active = Request::int('active', 0); @@ -138,27 +134,20 @@ class Admin_Cronjobs_SchedulesController extends AuthenticatedController $schedule->task_id = Request::option('task_id'); } $schedule->parameters = $parameters[$schedule->task_id]; - $schedule->type = Request::option('type') === 'once' - ? 'once' - : 'periodic'; - - if ($schedule->type === 'once') { - $temp = Request::getArray('once'); - $schedule->next_execution = strtotime($temp['date'] . ' ' . $temp['time']); - } else { - $temp = Request::getArray('periodic'); - $schedule->minute = $this->extractCronItem($temp['minute']); - $schedule->hour = $this->extractCronItem($temp['hour']); - $schedule->day = $this->extractCronItem($temp['day']); - $schedule->month = $this->extractCronItem($temp['month']); - $schedule->day_of_week = mb_strlen($temp['day_of_week']['value']) - ? (int) $temp['day_of_week']['value'] - : null; - - if ($schedule->active) { - $schedule->next_execution = $schedule->calculateNextExecution(); - } + + $temp = Request::getArray('periodic'); + $schedule->minute = $this->extractCronItem($temp['minute']); + $schedule->hour = $this->extractCronItem($temp['hour']); + $schedule->day = $this->extractCronItem($temp['day']); + $schedule->month = $this->extractCronItem($temp['month']); + $schedule->day_of_week = mb_strlen($temp['day_of_week']['value']) + ? (int) $temp['day_of_week']['value'] + : null; + + if ($schedule->active) { + $schedule->next_execution = $schedule->calculateNextExecution(); } + $schedule->store(); PageLayout::postSuccess(_('Die Änderungen wurden gespeichert.')); diff --git a/app/controllers/admin/datafields.php b/app/controllers/admin/datafields.php index bf390c9..6019a13 100644 --- a/app/controllers/admin/datafields.php +++ b/app/controllers/admin/datafields.php @@ -94,15 +94,17 @@ class Admin_DatafieldsController extends AuthenticatedController if (Request::submitted('uebernehmen')) { if (Request::get('datafield_name')) { - $datafield->name = Request::i18n('datafield_name'); - if ($datafield->object_type === 'moduldeskriptor' - || $datafield->object_type === 'modulteildeskriptor') { + $datafield->name = Request::i18n('datafield_name'); + if ( + $datafield->object_type === 'moduldeskriptor' + || $datafield->object_type === 'modulteildeskriptor' + ) { $object_class = implode(',', Request::getArray('object_class')); $datafield->object_class = (trim($object_class) && $object_class != 'NULL') ? $object_class : null; } elseif ($datafield->object_type === 'studycourse') { $datafield->object_class = trim(Request::option('object_class', 'all_settings')); } else { - $datafield->object_class = array_sum(Request::getArray('object_class')) ?: null; + $datafield->object_class = array_sum(Request::intArray('object_class')) ?: null; } $datafield->edit_perms = Request::get('edit_perms'); $datafield->view_perms = Request::get('visibility_perms'); diff --git a/app/controllers/admin/domain.php b/app/controllers/admin/domain.php index e4bb9c8..37ab11f 100644 --- a/app/controllers/admin/domain.php +++ b/app/controllers/admin/domain.php @@ -70,7 +70,7 @@ class Admin_DomainController extends AuthenticatedController { foreach ($args as $arg) { if ($arg && !preg_match('/' . UserDomain::REGEXP . '/', $arg)) { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } } diff --git a/app/controllers/admin/extern.php b/app/controllers/admin/extern.php index 41833b1..732b586 100644 --- a/app/controllers/admin/extern.php +++ b/app/controllers/admin/extern.php @@ -66,8 +66,8 @@ class Admin_ExternController extends AuthenticatedController ExternPageConfig::findEachBySQL( function ($c) use (&$configs, &$count_not_migrated) { $configs[$c->type][] = $c; - if (isset($c->conf['not_fixed_after_migration'])) { - $count_not_migrated++; + if (isset($c->conf['not_fixed_after_migration'])) { + $count_not_migrated++; } }, "range_id = ?", [$this->range] @@ -165,7 +165,7 @@ class Admin_ExternController extends AuthenticatedController if ($this->page->page_config->isNew()) { PageLayout::postSuccess(sprintf( _('Eine neue externe Seite "%$1s" vom Typ %$2s wurde angelegt.'), - htmlReady($this->page->name), + htmlReady($this->page->name), htmlReady($this->page->type) )); } else { @@ -259,7 +259,11 @@ class Admin_ExternController extends AuthenticatedController */ public function info_action(string $config_id) { - $this->page = ExternPage::get(ExternPageConfig::find($config_id)); + $config = ExternPageConfig::find($config_id); + if (!$config) { + throw new Exception('ExternPageConfig object not found!'); + } + $this->page = ExternPage::get($config); if ($this->page->author) { $this->author = '<a href="' . URLHelper::getLink('dispatch.php/profile', ['username' => $this->page->author->username]) @@ -364,7 +368,7 @@ class Admin_ExternController extends AuthenticatedController $config->author_id = $config->editor_id = $GLOBALS['user']->id; $config->store(); PageLayout::postSuccess( - sprintf(_('Die Konfiguration "%s" wurde erfolgreich importiert.'), + sprintf(_('Die Konfiguration "%s" wurde erfolgreich importiert.'), htmlReady($config->name) )); } @@ -436,7 +440,7 @@ class Admin_ExternController extends AuthenticatedController */ protected function fetchPlugins(bool $is_system): void { - $plugins = PluginEngine::getPlugins('ExternPagePlugin'); + $plugins = PluginEngine::getPlugins(ExternPagePlugin::class); foreach ($plugins as $plugin) { if ( $is_system === $plugin->isSystemPage() diff --git a/app/controllers/admin/install.php b/app/controllers/admin/install.php index 0054445..e45c281 100644 --- a/app/controllers/admin/install.php +++ b/app/controllers/admin/install.php @@ -1,7 +1,7 @@ <?php -require_once __DIR__ . '/../studip_controller_properties_trait.php'; +require_once __DIR__ . '/../../../lib/classes/StudipControllerPropertiesTrait.php'; -class Admin_InstallController extends Trails_Controller +class Admin_InstallController extends Trails\Controller { use StudipControllerPropertiesTrait; diff --git a/app/controllers/admin/lockrules.php b/app/controllers/admin/lockrules.php index 56879fe..202a2e9 100644 --- a/app/controllers/admin/lockrules.php +++ b/app/controllers/admin/lockrules.php @@ -164,7 +164,7 @@ class Admin_LockrulesController extends AuthenticatedController { $this->lock_rule = LockRule::find($lock_rule_id); if (!(!$this->lock_rule->isNew() && ($GLOBALS['perm']->have_perm('root') || $this->lock_rule->user_id === $GLOBALS['user']->id))) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } CSRFProtection::verifyUnsafeRequest(); if ($this->lock_rule->delete()) { @@ -186,4 +186,4 @@ class Admin_LockrulesController extends AuthenticatedController } return $this->lock_rule->store(); } -}
\ No newline at end of file +} diff --git a/app/controllers/admin/plugin.php b/app/controllers/admin/plugin.php index 903c45e..8a6935b 100644 --- a/app/controllers/admin/plugin.php +++ b/app/controllers/admin/plugin.php @@ -86,7 +86,7 @@ class Admin_PluginController extends AuthenticatedController * update information is available, an error message is set in * this controller and an empty array is returned. * - * @param array array of plugin meta data + * @param array $plugins array of plugin meta data */ private function get_update_info($plugins) { @@ -127,7 +127,6 @@ class Admin_PluginController extends AuthenticatedController } $plugin_manager = PluginManager::getInstance(); - $plugin_filter = Request::option('plugin_filter', ''); $plugins = $plugin_manager->getPluginInfos($this->plugin_filter); @@ -200,7 +199,6 @@ class Admin_PluginController extends AuthenticatedController _('Die Position von Plugin "%s" wurde verändert.'), $plugin['name'] ); - $changed = true; } } } @@ -344,7 +342,7 @@ class Admin_PluginController extends AuthenticatedController /** * Ask for confirmation from the user before deleting a plugin. * - * @param integer id of plugin to delete + * @param int $plugin_id id of plugin to delete */ public function ask_delete_action($plugin_id) { @@ -366,7 +364,7 @@ class Admin_PluginController extends AuthenticatedController /** * Completely delete a plugin from the system. * - * @param integer id of plugin to delete + * @param int $plugin_id id of plugin to delete */ public function delete_action($plugin_id) { @@ -390,7 +388,7 @@ class Admin_PluginController extends AuthenticatedController /** * Download a ZIP file containing the given plugin. * - * @param integer id of plugin to download + * @param int $plugin_id id of plugin to download */ public function download_action($plugin_id) { @@ -441,16 +439,16 @@ class Admin_PluginController extends AuthenticatedController $update_info = $this->plugin_admin->getUpdateInfo($plugins); $update = $this->flash['update']; - $update_status = []; - - // update each plugin in turn - foreach ($update as $id) { - if (isset($update_info[$id]['update'])) { - try { - $update_url = $update_info[$id]['update']['url']; - $this->plugin_admin->installPluginFromURL($update_url); - } catch (PluginInstallationException $ex) { - $update_errors[] = sprintf('%s: %s', $plugins[$id]['name'], $ex->getMessage()); + + if (!empty($update)) { // update each plugin in turn + foreach ($update as $id) { + if (isset($update_info[$id]['update'])) { + try { + $update_url = $update_info[$id]['update']['url']; + $this->plugin_admin->installPluginFromURL($update_url); + } catch (PluginInstallationException $ex) { + $update_errors[] = sprintf('%s: %s', $plugins[$id]['name'], $ex->getMessage()); + } } } } @@ -473,6 +471,8 @@ class Admin_PluginController extends AuthenticatedController /** * Show a page describing this plugin's meta data and description, * if available. + * + * @param int $plugin_id if of plugin to show manifest */ public function manifest_action($plugin_id) { @@ -491,7 +491,7 @@ class Admin_PluginController extends AuthenticatedController /** * migrate a plugin to top version * - * @param integer id of plugin to migrate + * @param int $plugin_id id of plugin to migrate */ public function migrate_action($plugin_id) { @@ -516,7 +516,7 @@ class Admin_PluginController extends AuthenticatedController * register a plugin in database when it * already exists in file system * - * @param integer number of found plugin + * @param int $number number of found plugin */ public function register_action($number) { diff --git a/app/controllers/admin/sem_classes.php b/app/controllers/admin/sem_classes.php index c0b098f..7993c4b 100644 --- a/app/controllers/admin/sem_classes.php +++ b/app/controllers/admin/sem_classes.php @@ -65,7 +65,7 @@ class Admin_SemClassesController extends AuthenticatedController { Navigation::activateItem("/admin/locations/sem_classes"); - $plugins = PluginManager::getInstance()->getPlugins("StudipModule"); + $plugins = PluginManager::getInstance()->getPlugins(StudipModule::class); $this->sem_class = SemClass::getClasses()[Request::get("id")]; $modules = []; foreach ($this->sem_class->getModuleObjects() as $plugin) { diff --git a/app/controllers/admin/tree.php b/app/controllers/admin/tree.php index 1afc438..c8f2a8f 100644 --- a/app/controllers/admin/tree.php +++ b/app/controllers/admin/tree.php @@ -130,12 +130,15 @@ class Admin_TreeController extends AuthenticatedController $node->parent_id = Request::option('parent_id'); $parent = $classname::getNode(Request::option('parent_id')); - $maxprio = max(array_map( - function ($c) { - return $c->priority; - }, - $parent->getChildNodes() - )); + $children = $parent->getChildNodes(); + $maxprio = !empty($children) + ? max(array_map( + function ($c) { + return $c->priority; + }, + $children + )) + : 0; $node->priority = $maxprio + 1; if (Request::option('studip_object_id')) { diff --git a/app/controllers/admin/user.php b/app/controllers/admin/user.php index 70dfdf1..37a5d2e 100644 --- a/app/controllers/admin/user.php +++ b/app/controllers/admin/user.php @@ -1440,17 +1440,13 @@ class Admin_UserController extends AuthenticatedController ]; $queries[] = [ 'desc' => _("Anzahl der Wikiseiten"), - 'query' => "SELECT COUNT(*) FROM wiki WHERE user_id = ? GROUP BY user_id", + 'query' => "SELECT COUNT(*) FROM `wiki_pages` WHERE `user_id` = ? GROUP BY `user_id`", ]; $queries[] = [ 'desc' => _("Anzahl der Umfragen"), 'query' => "SELECT COUNT(*) FROM questionnaires WHERE user_id = ? GROUP BY user_id", ]; $queries[] = [ - 'desc' => _("Anzahl der Evaluationen"), - 'query' => "SELECT COUNT(*) FROM eval WHERE author_id = ? GROUP BY author_id", - ]; - $queries[] = [ 'desc' => _("Anzahl der Dateien in Veranstaltungen und Einrichtungen"), 'query' => "SELECT COUNT(file_refs.id) FROM (file_refs INNER JOIN files ON file_refs.file_id = files.id) @@ -1478,7 +1474,7 @@ class Admin_UserController extends AuthenticatedController 'details' => "files", ]; - foreach (PluginEngine::getPlugins('ForumModule') as $plugin) { + foreach (PluginEngine::getPlugins(ForumModule::class) as $plugin) { $table = $plugin->getEntryTableInfo(); $queries[] = [ 'desc' => $plugin->getPluginName() . ' - ' . _("Anzahl der Postings"), @@ -1723,7 +1719,7 @@ class Admin_UserController extends AuthenticatedController )->asDialog(); $actions->addLink( _('Konten zusammenführen'), - $this->url_for('admin/user/migrate/' . ((!empty($this->user) && is_array($this->user)) ? $this->user['user_id'] : '')), + $this->url_for('admin/user/migrate/' . (!empty($this->user['user_id']) ? $this->user['user_id'] : '')), Icon::create('community') ); diff --git a/app/controllers/admission/courseset.php b/app/controllers/admission/courseset.php index b39cdfb..65d4247 100644 --- a/app/controllers/admission/courseset.php +++ b/app/controllers/admission/courseset.php @@ -23,18 +23,17 @@ class Admission_CoursesetController extends AuthenticatedController { parent::before_filter($action, $args); - if (!Request::isXhr()) { - PageLayout::setTitle(_('Anmeldesets')); - // Get only own courses if user doesn't have permission to edit institute-wide coursesets. - $this->onlyOwnCourses = true; - if ($GLOBALS['perm']->have_perm('admin') || ($GLOBALS['perm']->have_perm('dozent') && Config::get()->ALLOW_DOZENT_COURSESET_ADMIN)) { - // We have access to institute-wide course sets, so all courses may be assigned. - $this->onlyOwnCourses = false; - Navigation::activateItem('/browse/coursesets/sets'); - } else { - throw new AccessDeniedException(); - } + PageLayout::setTitle(_('Anmeldesets')); + // Get only own courses if user doesn't have permission to edit institute-wide coursesets. + $this->onlyOwnCourses = true; + if ($GLOBALS['perm']->have_perm('admin') || ($GLOBALS['perm']->have_perm('dozent') && Config::get()->ALLOW_DOZENT_COURSESET_ADMIN)) { + // We have access to institute-wide course sets, so all courses may be assigned. + $this->onlyOwnCourses = false; + Navigation::activateItem('/browse/coursesets/sets'); + } else { + throw new AccessDeniedException(); } + PageLayout::addScript('studip-admission.js'); $views = new ActionsWidget(); @@ -44,6 +43,7 @@ class Admission_CoursesetController extends AuthenticatedController Icon::create('add') )->setActive($action === 'configure'); Sidebar::Get()->addWidget($views); + if (!isset($this->instant_course_set_view)) { $this->instant_course_set_view = false; } @@ -327,15 +327,19 @@ class Admission_CoursesetController extends AuthenticatedController * * @param String $coursesetId the course set to delete */ - public function delete_action($coursesetId) { + public function delete_action($coursesetId) + { $this->courseset = new CourseSet($coursesetId); - if (Request::int('really')) { - $this->courseset->delete(); - $this->redirect($this->url_for('admission/courseset')); + + if (!$this->courseset->isUserAllowedToEdit(User::findCurrent()->id)) { + throw new AccessDeniedException(_('Sie dürfen diese Anmelderegel nicht löschen.')); } - if (Request::int('cancel')) { - $this->redirect($this->url_for('admission/courseset')); + + if (Request::bool('really')) { + $this->courseset->delete(); } + + $this->redirect($this->url_for('admission/courseset')); } /** @@ -757,11 +761,17 @@ class Admission_CoursesetController extends AuthenticatedController $ids = Request::optionArray('ids'); if (Request::submitted('delete')) { + $deleted = 0; foreach ($ids as $id) { $courseset = new CourseSet($id); - $courseset->delete(); + if ($courseset->isUserAllowedToEdit(User::findCurrent()->id)) { + $courseset->delete(); + $deleted += 1; + } + } + if ($deleted > 0) { + PageLayout::postSuccess(_('Die Anmeldesets wurden gelöscht.')); } - PageLayout::postSuccess(_('Die Anmeldesets wurden gelöscht.')); } $this->redirect('admission/courseset'); diff --git a/app/controllers/api/authorizations.php b/app/controllers/api/authorizations.php deleted file mode 100644 index 543bc79..0000000 --- a/app/controllers/api/authorizations.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -require_once 'lib/bootstrap-api.php'; - -/** -* @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - **/ -class Api_AuthorizationsController extends AuthenticatedController -{ - /** - * - **/ - public function before_filter(&$action, &$args) - { - parent::before_filter($action, $args); - - $GLOBALS['perm']->check('autor'); - - Navigation::activateItem('/profile/settings/api'); - PageLayout::setTitle(_('Applikationen')); - - $this->types = [ - 'website' => _('Website'), - 'program' => _('Herkömmliches Desktopprogramm'), - 'app' => _('Mobile App') - ]; - } - - /** - * - **/ - public function index_action() - { - $this->consumers = RESTAPI\UserPermissions::get($GLOBALS['user']->id)->getConsumers(); - $this->types = [ - 'website' => _('Website'), - 'program' => _('Herkömmliches Desktopprogramm'), - 'app' => _('Mobile App') - ]; - - $widget = new SidebarWidget(); - $widget->setTitle(_('Informationen')); - $widget->addElement(new WidgetElement(_('Dies sind die Apps, die Zugriff auf Ihren Account haben.'))); - Sidebar::Get()->addWidget($widget); - } - - /** - * - **/ - public function revoke_action($id) - { - $consumer = new RESTAPI\Consumer\OAuth($id); - $consumer->revokeAccess($GLOBALS['user']->id); - - PageLayout::postMessage(MessageBox::success(_('Der Applikation wurde der Zugriff auf Ihre Daten untersagt.'))); - $this->redirect('api/authorizations'); - } -} diff --git a/app/controllers/api/oauth.php b/app/controllers/api/oauth.php deleted file mode 100644 index bc80c90..0000000 --- a/app/controllers/api/oauth.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -require_once 'lib/bootstrap-api.php'; - -/** - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - **/ -class Api_OauthController extends StudipController -{ - /** - * - **/ - public function before_filter(&$action, &$args) - { - parent::before_filter($action, $args); - - # initialize Stud.IP-Session - page_open(['sess' => 'Seminar_Session', - 'auth' => 'Seminar_Default_Auth', - 'perm' => 'Seminar_Perm', - 'user' => 'Seminar_User']); - - $this->set_layout(null); - } - - /** - * - **/ - public function index_action() - { - $this->render_text('TODO'); - } - - /** - * - **/ - public function request_token_action() - { - $server = new OAuthServer(); - $token = $server->requestToken(); - - $this->response->headers = []; - $this->render_nothing(); - } - - /** - * - **/ - public function authorize_action() - { - global $user, $auth; - - $auth_plugin = Config::get()->API_OAUTH_AUTH_PLUGIN; - if ($GLOBALS['user']->id === 'nobody' && $auth_plugin !== 'Standard' && !Request::option('sso')) { - $params = $_GET; - $params['sso'] = strtolower($auth_plugin); - $this->redirect($this->url_for('api/oauth/authorize?' . http_build_query($params))); - return; - } else { - $auth->login_if($user->id === 'nobody'); - } - - $user_id = RESTAPI\Consumer\OAuth::getOAuthId($GLOBALS['user']->id); - - try { - $consumer = RESTAPI\Consumer\Base::detectConsumer('oauth', 'request'); - if (!$consumer) { - $this->response->set_status(400, 'No consumer detected'); - $this->render_nothing(); - return; - } - - if (Request::submitted('allow')) { - $result = $consumer->grantAccess($GLOBALS['user']->id); - - $redirect_uri = Request::get('oauth_callback', $consumer->callback); - - if ($redirect_uri) { - $this->redirect($redirect_uri); - } else { - // No oauth_callback, show the user the result of the authorization - // ** your code here ** - PageLayout::postMessage(MessageBox::success(_('Sie haben der Applikation Zugriff auf Ihre Daten gewährt.'))); - $this->redirect('api/authorizations#' . $consumer->auth_key); - } - return; - } - } catch (OAuthException2 $e) { - // No token to be verified in the request, show a page where the user can enter the token to be verified - // **your code here** - die('invalid'); - } - - PageLayout::disableHeader(); - PageLayout::setTitle(sprintf(_('"%s" bittet um Zugriff'), $consumer->title)); - $this->set_layout($GLOBALS['template_factory']->open('layouts/base.php')); - $this->consumer = $consumer; - $this->token = Request::option('oauth_token'); - $this->oauth_callback = Request::get('oauth_callback'); - } - - /** - * - **/ - public function access_token_action() - { - $server = new OAuthServer(); - $server->accessToken(); - - $this->response->headers = []; - $this->render_nothing(); - } -} diff --git a/app/controllers/api/oauth2/applications.php b/app/controllers/api/oauth2/applications.php index d08ec1e..fd6a1bb 100644 --- a/app/controllers/api/oauth2/applications.php +++ b/app/controllers/api/oauth2/applications.php @@ -31,7 +31,7 @@ class Api_Oauth2_ApplicationsController extends AuthenticatedController $this->application = $this->formatApplication($accessToken); if (!$this->application) { - throw new Trails_Exception(500, 'Error finding client.'); + throw new Trails\Exception(500, 'Error finding client.'); } } @@ -42,7 +42,7 @@ class Api_Oauth2_ApplicationsController extends AuthenticatedController $user = User::findCurrent(); $accessToken = AccessToken::find(Request::option('application')); if (!$accessToken) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } if ($accessToken['user_id'] !== $user->id) { throw new AccessDeniedException(); diff --git a/app/controllers/api/oauth2/authorize.php b/app/controllers/api/oauth2/authorize.php index 5628d49..6387937 100644 --- a/app/controllers/api/oauth2/authorize.php +++ b/app/controllers/api/oauth2/authorize.php @@ -13,7 +13,7 @@ class Api_Oauth2_AuthorizeController extends OAuth2Controller parent::before_filter($action, $args); if ('index' !== $action) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } $action = $this->determineAction(); @@ -55,7 +55,7 @@ class Api_Oauth2_AuthorizeController extends OAuth2Controller if ('nobody' === $GLOBALS['user']->id && 'Standard' !== $authPlugin && !Request::option('sso')) { $queryParams = $psrRequest->getQueryParams(); $queryParams['sso'] = strtolower($authPlugin); - $this->redirect($this->authorizeURL($queryParams)); + $this->redirect($this->url_for('api/oauth2/authorize', $queryParams)); return; } else { diff --git a/app/controllers/api/oauth2/oauth2_controller.php b/app/controllers/api/oauth2/oauth2_controller.php index fd02ea9..6b3dacd 100644 --- a/app/controllers/api/oauth2/oauth2_controller.php +++ b/app/controllers/api/oauth2/oauth2_controller.php @@ -42,7 +42,7 @@ abstract class OAuth2Controller extends StudipController return $this->convertPsrResponse($psrResponse); } - return new Trails_Response($exception->getMessage(), [], 500); + return new Trails\Response($exception->getMessage(), [], 500); } protected function getAuthorizationServer(): AuthorizationServer diff --git a/app/controllers/api/oauth2/token.php b/app/controllers/api/oauth2/token.php index 0ae7ffb..755d6b7 100644 --- a/app/controllers/api/oauth2/token.php +++ b/app/controllers/api/oauth2/token.php @@ -8,11 +8,11 @@ class Api_Oauth2_TokenController extends OAuth2Controller parent::before_filter($action, $args); if ('index' !== $action) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } if (!Request::isPost()) { - throw new Trails_Exception(405); + throw new Trails\Exception(405); } $action = 'issue_token'; diff --git a/app/controllers/authenticated_controller.php b/app/controllers/authenticated_controller.php deleted file mode 100644 index e051ffa..0000000 --- a/app/controllers/authenticated_controller.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -/* - * Copyright (C) 2009 - Marcus Lunzenauer <mlunzena@uos.de> - * - * 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. - */ - -class AuthenticatedController extends StudipController -{ - protected $with_session = true; //we do need to have a session for this controller - protected $allow_nobody = false; //nobody is not allowed and always gets a login-screen - - public function before_filter(&$action, &$args) - { - parent::before_filter($action, $args); - - // Restore request if present - if (isset($this->flash['request'])) { - foreach ($this->flash['request'] as $key => $value) { - Request::set($key, $value); - } - } - } - - protected function keepRequest() - { - $this->flash['request'] = Request::getInstance()->getIterator()->getArrayCopy(); - } -} diff --git a/app/controllers/avatar.php b/app/controllers/avatar.php index c549cf0..aafbb98 100644 --- a/app/controllers/avatar.php +++ b/app/controllers/avatar.php @@ -62,8 +62,14 @@ class AvatarController extends AuthenticatedController Navigation::activateItem('/admin/institute/details'); } else { Navigation::activateItem('/course/admin/avatar'); + + if ($GLOBALS['perm']->have_studip_perm('admin', $id)) { + $widget = new CourseManagementSelectWidget(); + Sidebar::get()->addWidget($widget); + } } + $avatar = $class::getAvatar($id); $this->avatar = $avatar->getURL($class::NORMAL); $this->customized = $avatar->is_customized(); diff --git a/app/controllers/blubber.php b/app/controllers/blubber.php index c0490f0..aedf9b6 100644 --- a/app/controllers/blubber.php +++ b/app/controllers/blubber.php @@ -135,7 +135,7 @@ class BlubberController extends AuthenticatedController 'user_id' => $user_id, ]); } - $this->redirect("blubber/index/{$blubber->getId()}"); + $this->relocate("blubber/index/{$blubber->getId()}"); return; } @@ -271,13 +271,12 @@ class BlubberController extends AuthenticatedController $output = []; foreach ($_FILES as $file) { - $newfile = null; //is filled below $file_ref = null; //is also filled below if ($file['size']) { $document['user_id'] = $GLOBALS['user']->id; - $document['filesize'] = $file['size']; - + $success = false; + $url = ''; try { $root_dir = Folder::findTopFolder($GLOBALS['user']->id); $root_dir = $root_dir->getTypedFolder(); @@ -339,7 +338,6 @@ class BlubberController extends AuthenticatedController } } catch (Exception $e) { $output['errors'][] = $e->getMessage(); - $success = false; } if ($success) { @@ -373,21 +371,22 @@ class BlubberController extends AuthenticatedController } PageLayout::setTitle(_('Person hinzufügen')); if (Request::isPost() && Request::option('user_id')) { - $query = "INSERT IGNORE INTO blubber_mentions - SET thread_id = :thread_id, - user_id = :user_id, - external_contact = 0, - mkdate = UNIX_TIMESTAMP()"; - $statement = DBManager::get()->prepare($query); - $statement->execute([ - 'thread_id' => $thread_id, - 'user_id' => Request::option('user_id'), - ]); - $this->response->add_header('X-Dialog-Execute', 'STUDIP.Blubber.refreshThread'); - $this->response->add_header('X-Dialog-Close', '1'); - $this->render_json([ - 'thread_id' => $thread_id, - ]); + $data = [ + 'user_id' => Request::option('user_id'), + 'thread_id' => $thread_id, + 'external_contact' => 0, + ]; + + $blubber_mention = BlubberMention::findOneBySQL('user_id = ? AND thread_id = ?', [Request::option('user_id'), $thread_id]); + + if ($blubber_mention) { + $blubber_mention->setData($data); + } else { + $blubber_mention = BlubberMention::create($data); + } + $blubber_mention->store(); + $this->relocate('blubber/index/' . $thread_id); + return; } } @@ -408,13 +407,9 @@ class BlubberController extends AuthenticatedController CourseAvatar::getAvatar($course->getId())->createFromUpload('avatar'); } - $query = "SELECT user_id - FROM blubber_mentions - WHERE thread_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->thread->id]); - foreach ($statement->fetchFirst() as $user_id) { - CourseMember::insertCourseMember($course->getId(), $user_id, $user_id === $this->thread['user_id'] ? 'dozent' : 'tutor'); + $blubber_mentions = BlubberMention::findBySQL('thread_id = ?', [$this->thread->id]); + foreach ($blubber_mentions as $blubber_mention) { + CourseMember::insertCourseMember($course->getId(), $blubber_mention->user_id, $blubber_mention->user_id === $this->thread['user_id'] ? 'dozent' : 'tutor'); } $this->thread['context_type'] = 'course'; @@ -424,13 +419,13 @@ class BlubberController extends AuthenticatedController PluginManager::getInstance()->setPluginActivated( PluginManager::getInstance() - ->getPlugin('Blubber') + ->getPlugin(Blubber::class) ->getPluginId(), $course->getId(), true ); - PageLayout::postSuccess(sprintf(_("Studiengruppe '%s' wurde angelegt."), htmlReady($course['name']))); + PageLayout::postSuccess(sprintf(_('Studiengruppe "%s" wurde angelegt.'), htmlReady($course['name']))); $this->redirect(URLHelper::getURL('seminar_main.php', ['auswahl' => $course->getId()])); } } diff --git a/app/controllers/calendar/calendar.php b/app/controllers/calendar/calendar.php index c605c01..0cb96ad 100644 --- a/app/controllers/calendar/calendar.php +++ b/app/controllers/calendar/calendar.php @@ -12,8 +12,12 @@ class Calendar_CalendarController extends AuthenticatedController } - protected function buildSidebar($schedule = false) - { + protected function buildSidebar( + bool $schedule = false, + string $user_id = '', + string $group_id = '' + ) { + $sidebar = Sidebar::get(); $actions = new ActionsWidget(); @@ -25,11 +29,17 @@ class Calendar_CalendarController extends AuthenticatedController ['data-dialog' => 'size=default'] ); } else { + $params = []; + if ($user_id) { + $params['user_id'] = $user_id; + } elseif ($group_id) { + $params['group_id'] = $group_id; + } $actions->addLink( _('Termin anlegen'), - $this->url_for('calendar/date/add'), + $this->url_for('calendar/date/add', $params), Icon::create('add'), - ['data-dialog' => 'size=auto'] + ['data-dialog' => 'size=auto', 'class' => 'calendar-action'] ); } @@ -101,6 +111,8 @@ class Calendar_CalendarController extends AuthenticatedController { PageLayout::setTitle(_('Kalender')); + $default_date = \Studip\Calendar\Helper::getDefaultCalendarDate(); + if (Request::isPost()) { //In case the checkbox of the options widget is clicked, the resulting //POST request must be catched here and result in a redirect. @@ -181,7 +193,11 @@ class Calendar_CalendarController extends AuthenticatedController throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); } - $this->buildSidebar(false); + $this->buildSidebar( + false, + $calendar_owner ? $calendar_owner->id : '', + $selected_group ? $selected_group->id : '' + ); $sidebar = Sidebar::get(); @@ -189,6 +205,7 @@ class Calendar_CalendarController extends AuthenticatedController if ($calendar_owner && $calendar_owner->id === User::findCurrent()->id) { //The user is viewing their own calendar. $options = new OptionsWidget(); + $options->addLayoutCSSClass('calendar-action'); $options->addCheckbox( _('Abgelehnte Termine anzeigen'), Request::bool('show_declined'), @@ -223,6 +240,7 @@ class Calendar_CalendarController extends AuthenticatedController $this->url_for('calendar/calendar/index', ['view' => 'group']), 'group_id' ); + $group_select->addLayoutCSSClass('calendar-action'); $options = [ '' => _('(bitte wählen)') ]; @@ -249,6 +267,7 @@ class Calendar_CalendarController extends AuthenticatedController $this->url_for('calendar/calendar'), 'user_id' ); + $calendar_select->addLayoutCSSClass('calendar-action'); $select_options = [ '' => _('(bitte wählen)'), User::findCurrent()->id => _('Eigener Kalender') @@ -321,7 +340,6 @@ class Calendar_CalendarController extends AuthenticatedController $slot_durations = $this->getUserCalendarSlotSettings(); //Create the fullcalendar object: - $default_date = \Studip\Calendar\Helper::getDefaultCalendarDate(); $data_url_params = []; if (Request::bool('show_declined')) { @@ -432,7 +450,7 @@ class Calendar_CalendarController extends AuthenticatedController _('Termin anlegen'), $this->url_for('calendar/date/add/course_' . $course->id), Icon::create('add'), - ['data-dialog' => 'size=default'] + ['data-dialog' => 'size=default', 'class' => 'calendar-action'] ); $actions->addLink( _('Drucken'), @@ -564,20 +582,18 @@ class Calendar_CalendarController extends AuthenticatedController $course_dates = CalendarCourseDate::getEvents($begin, $end, $owner->id); foreach ($course_dates as $course_date) { $event = $course_date->toEventData(User::findCurrent()->id); - $event->background_colour = '#ffffff'; + $event->background_colour = ''; $event->text_colour = '#000000'; - $event->border_colour = '#000000'; - $event->event_classes = []; + $event->border_colour = ''; $result[] = $event->toFullcalendarEvent(); } //Include relevant cancelled course dates: $cancelled_course_dates = CalendarCourseExDate::getEvents($begin, $end, $owner->id); foreach ($cancelled_course_dates as $cancelled_course_date) { $event = $cancelled_course_date->toEventData(User::findCurrent()->id); - $event->background_colour = '#ffffff'; + $event->background_colour = ''; $event->text_colour = '#000000'; - $event->border_colour = '#000000'; - $event->event_classes = []; + $event->border_colour = ''; $result[] = $event->toFullcalendarEvent(); } } @@ -660,7 +676,7 @@ class Calendar_CalendarController extends AuthenticatedController public function add_courses_action() { $selected_semester_pseudo_id = Request::option('semester_id'); - $this->selected_semesters_id = ''; + $this->selected_semester_id = ''; $this->available_semester_data = []; $semesters = Semester::getAll(); foreach ($semesters as $semester) { @@ -691,8 +707,9 @@ class Calendar_CalendarController extends AuthenticatedController $this->selected_semester_id = $semester->id; } else { $this->selected_semester_id = $selected_semester_pseudo_id ?? ''; - if (!Semester::exists($this->selected_semesters_id)) { - $this->selected_semester_id = ''; + if (!Semester::exists($this->selected_semester_id)) { + $semester = Semester::findCurrent(); + $this->selected_semester_id = $semester->id; } } @@ -755,29 +772,41 @@ class Calendar_CalendarController extends AuthenticatedController PageLayout::postError(_('Bitte wählen Sie aus, welche Termine exportiert werden sollen!')); return; } - $ical = ''; - $calendar_export = new ICalendarExport(); - if ($this->dates_to_export === 'user') { - $ical = $calendar_export->exportCalendarDates(User::findCurrent()->id, $this->begin, $this->end); - } elseif ($this->dates_to_export === 'course') { - $ical = $calendar_export->exportCourseDates(User::findCurrent()->id, $this->begin, $this->end); - $ical .= $calendar_export->exportCourseExDates(User::findCurrent()->id, $this->begin, $this->end); - } elseif ($this->dates_to_export === 'all') { - $ical = $calendar_export->exportCalendarDates(User::findCurrent()->id, $this->begin, $this->end); - $ical .= $calendar_export->exportCourseDates(User::findCurrent()->id, $this->begin, $this->end); - $ical .= $calendar_export->exportCourseExDates(User::findCurrent()->id, $this->begin, $this->end); - } - $ical = $calendar_export->writeHeader() . $ical . $calendar_export->writeFooter(); - $this->response->add_header('Content-Type', 'text/calendar;charset=utf-8'); - $this->response->add_header('Content-Disposition', 'attachment; filename="studip.ics"'); - $this->response->add_header('Content-Transfer-Encoding', 'binary'); - $this->response->add_header('Pragma', 'public'); - $this->response->add_header('Cache-Control', 'private'); - $this->response->add_header('Content-Length', strlen($ical)); - $this->render_text($ical); + $this->relocate($this->url_for('calendar/calendar/export_file', [ + 'begin' => $this->begin->format('d.m.Y'), + 'end' => $this->end->format('d.m.Y'), + 'dates_to_export' => $this->dates_to_export + ])); } } + public function export_file_action() + { + $begin = Request::getDateTime('begin', 'd.m.Y'); + $end = Request::getDateTime('end', 'd.m.Y'); + $dates_to_export = Request::option('dates_to_export', 'user'); + $ical = ''; + $calendar_export = new ICalendarExport(); + if ($dates_to_export === 'user') { + $ical = $calendar_export->exportCalendarDates(User::findCurrent()->id, $begin, $end); + } elseif ($dates_to_export === 'course') { + $ical = $calendar_export->exportCourseDates(User::findCurrent()->id, $begin, $end); + $ical .= $calendar_export->exportCourseExDates(User::findCurrent()->id, $begin, $end); + } elseif ($dates_to_export === 'all') { + $ical = $calendar_export->exportCalendarDates(User::findCurrent()->id, $begin, $end); + $ical .= $calendar_export->exportCourseDates(User::findCurrent()->id, $begin, $end); + $ical .= $calendar_export->exportCourseExDates(User::findCurrent()->id, $begin, $end); + } + $ical = $calendar_export->writeHeader() . $ical . $calendar_export->writeFooter(); + $this->response->add_header('Content-Type', 'text/calendar;charset=utf-8'); + $this->response->add_header('Content-Disposition', 'attachment; filename="studip.ics"'); + $this->response->add_header('Content-Transfer-Encoding', 'binary'); + $this->response->add_header('Pragma', 'public'); + $this->response->add_header('Cache-Control', 'private'); + $this->response->add_header('Content-Length', strlen($ical)); + $this->render_text($ical); + } + public function import_action() {} public function import_file_action() diff --git a/app/controllers/calendar/contentbox.php b/app/controllers/calendar/contentbox.php index 8ba5215..b684905 100644 --- a/app/controllers/calendar/contentbox.php +++ b/app/controllers/calendar/contentbox.php @@ -78,21 +78,29 @@ class Calendar_ContentboxController extends StudipController if ($this->admin) { $this->isProfile = $this->single && $this->userRange; } + + // Sort dates + usort($this->termine, function ($a, $b) { + [$a_begin, $a_end] = $this->parseBeginAndEndFromDate($a); + [$b_begin, $b_end] = $this->parseBeginAndEndFromDate($b); + + return $a_begin - $b_begin + ?: $a_end - $b_end; + }); } private function parseSeminar($id) { - $course = Course::find($id); - $this->termine = $course->getDatesWithExdates()->findBy('end_time', [$this->start, $this->start + $this->timespan], '><'); - foreach ($this->termine as $course_date) { - if ($this->course_range) { - //Display only date and time: - $this->titles[$course_date->id] = $course_date->getFullName('include-room'); - } else { - //Include the course title: - $this->titles[$course_date->id] = $course_date->getFullName('verbose'); - } - } + // Display only date and time if in course range, include course title + // otherwise + $date_format = $this->course_range ? 'include-room' : 'verbose'; + + $this->termine = Course::find($id)->getDatesWithExdates() + ->findBy('end_time', [$this->start, $this->start + $this->timespan], '><') + ->map(function ($course_date) use ($date_format) { + $this->titles[$course_date->id] = $course_date->getFullName($date_format); + return $course_date; + }); } private function parseUser($id) @@ -170,4 +178,23 @@ class Calendar_ContentboxController extends StudipController $this->termine[] = $assignment; } } + + private function parseBeginAndEndFromDate($date): array + { + if ($date instanceof CalendarDateAssignment) { + return [ + $date->calendar_date->begin, + $date->calendar_date->end, + ]; + } + + if ($date instanceof CourseDate || $date instanceof CourseExDate) { + return [ + $date->date, + $date->end_time, + ]; + } + + throw new Exception('Invalid date type passed: ' . get_class($date)); + } } diff --git a/app/controllers/calendar/date.php b/app/controllers/calendar/date.php index 72f3a40..cb61a7e 100644 --- a/app/controllers/calendar/date.php +++ b/app/controllers/calendar/date.php @@ -27,9 +27,8 @@ class Calendar_DateController extends AuthenticatedController $range_id = $range_and_id[1]; } if (!$range) { - //Show the personal calendar of the current user: + $range_id = Request::option('user_id', $GLOBALS['user']->id); $range = 'user'; - $range_id = $GLOBALS['user']->id; } $owner = null; @@ -233,6 +232,14 @@ class Calendar_DateController extends AuthenticatedController $this->date->repetition_end = $this->date->end; } else { $time = new DateTime(); + if (Request::submitted('timestamp')) { + $time->setTimestamp(Request::int('timestamp')); + } elseif (Request::submitted('defaultDate')) { + $date_parts = explode('-', Request::get('defaultDate')); + if (count($date_parts) === 3) { + $time->setDate($date_parts[0], $date_parts[1], $date_parts[2]); + } + } $time = $time->add(new DateInterval('PT1H')); $time->setTime(intval($time->format('H')), 0, 0); $this->date->begin = $time->getTimestamp(); @@ -325,15 +332,12 @@ class Calendar_DateController extends AuthenticatedController if ($this->date->isNew()) { if (!($owner instanceof Course)) { - //Assign the date to the calendar of the current user by default: - $user = User::findCurrent(); - if ($user) { - $this->calendar_assignment_items[] = [ - 'value' => $user->id, - 'name' => $user->getFullName(), - 'deletable' => true - ]; - } + //Assign the date to the calendar of the owner by default: + $this->calendar_assignment_items[] = [ + 'value' => $owner->id, + 'name' => $owner->getFullName(), + 'deletable' => true + ]; } } else { $exceptions = CalendarDateException::findBySql( diff --git a/app/controllers/calendar/schedule.php b/app/controllers/calendar/schedule.php index 88c4304..5d9a26e 100644 --- a/app/controllers/calendar/schedule.php +++ b/app/controllers/calendar/schedule.php @@ -143,7 +143,7 @@ class Calendar_ScheduleController extends AuthenticatedController 'entry_height' => $this->calendar_view->getHeight() ]; - $factory = new Flexi_TemplateFactory($this->dispatcher->trails_root . '/views'); + $factory = new Flexi\Factory($this->dispatcher->trails_root . '/views'); PageLayout::addStyle($factory->render('calendar/schedule/stylesheet', $style_parameters), 'screen, print'); if (Request::option('printview')) { diff --git a/app/controllers/captcha.php b/app/controllers/captcha.php new file mode 100644 index 0000000..37bac47 --- /dev/null +++ b/app/controllers/captcha.php @@ -0,0 +1,12 @@ +<?php +final class CaptchaController extends StudipController +{ + public function challenge_action(): void + { + $this->response->add_header( + 'Expires', + gmdate('D, d M Y H:i:s', time() + CaptchaChallenge::CHALLENGE_EXPIRATION) . ' GMT' + ); + $this->render_json(CaptchaChallenge::createNewChallenge()); + } +} diff --git a/app/controllers/consultation/admin.php b/app/controllers/consultation/admin.php index bc63a1a..8ee7575 100644 --- a/app/controllers/consultation/admin.php +++ b/app/controllers/consultation/admin.php @@ -138,6 +138,7 @@ class Consultation_AdminController extends ConsultationController $this->room = ''; $this->responsible = false; + $this->slot_count_threshold = self::SLOT_COUNT_THRESHOLD; // TODO: inst_default? if ($this->range instanceof User) { @@ -155,6 +156,8 @@ class Consultation_AdminController extends ConsultationController $block->range = $this->range; $this->responsible = $block->getPossibleResponsibilites(); } + + $this->response->add_header('X-No-Buttons', ''); } public function store_action() @@ -186,7 +189,7 @@ class Consultation_AdminController extends ConsultationController $end, Request::int('day-of-week'), Request::int('interval'), - Request::int('duration'), + $duration, $pause_time, $pause_duration ); @@ -214,6 +217,7 @@ class Consultation_AdminController extends ConsultationController $block->note = Request::get('note'); $block->size = Request::int('size', 1); $block->lock_time = Request::int('lock_time'); + $block->consecutive = Request::bool('consecutive', false); $slots = $block->createSlots(Request::int('duration'), $pause_time, $pause_duration); if (count($slots) === 0) { @@ -403,6 +407,7 @@ class Consultation_AdminController extends ConsultationController $this->block->mail_to_tutors = Request::bool('mail-to-tutors', false); $this->block->confirmation_text = trim(Request::get('confirmation-text')); $this->block->lock_time = Request::int('lock_time'); + $this->block->consecutive = Request::bool('consecutive', false); foreach ($this->block->slots as $slot) { $slot->note = ''; @@ -535,7 +540,7 @@ class Consultation_AdminController extends ConsultationController public function toggle_action($what, $state, $expired = false) { if ($what === 'messages') { - // TODO: Applicable everywhere? + // TODO: Applicable everywhere? $this->getUserConfig()->store( 'CONSULTATION_SEND_MESSAGES', (bool) $state @@ -808,7 +813,7 @@ class Consultation_AdminController extends ConsultationController _('Terminblöcke anlegen'), $this->createURL(), Icon::create('add') - )->asDialog('size=auto'); + )->asDialog('size=big'); $actions->addLink( _('Namen des Reiters ändern'), $this->tabURL($action === 'expired'), diff --git a/app/controllers/consultation/consultation_controller.php b/app/controllers/consultation/consultation_controller.php index d6927af..00e10ad 100644 --- a/app/controllers/consultation/consultation_controller.php +++ b/app/controllers/consultation/consultation_controller.php @@ -19,7 +19,7 @@ abstract class ConsultationController extends AuthenticatedController $this->range = Context::get(); $type = 'object'; } else { - $this->range = $GLOBALS['user']->getAuthenticatedUser(); + $this->range = User::findCurrent(); } if (!$this->range) { @@ -60,7 +60,7 @@ abstract class ConsultationController extends AuthenticatedController $this->render_template('consultation/not_found', $this->layout); } - protected function activateNavigation($path) + protected function activateNavigation($path): void { $path = ltrim($path, '/'); @@ -73,7 +73,7 @@ abstract class ConsultationController extends AuthenticatedController } } - protected function getConsultationTitle() + protected function getConsultationTitle(): string { return $this->range->getConfiguration()->CONSULTATION_TAB_TITLE; } @@ -103,7 +103,8 @@ abstract class ConsultationController extends AuthenticatedController return $block; } - protected function loadSlot($block_id, $slot_id) + + protected function loadSlot($block_id, $slot_id): ConsultationSlot { $block = $this->loadBlock($block_id); $slot = $block->slots->find($slot_id); @@ -115,7 +116,7 @@ abstract class ConsultationController extends AuthenticatedController return $slot; } - protected function loadBooking($block_id, $slot_id, $booking_id) + protected function loadBooking($block_id, $slot_id, $booking_id): ConsultationBooking { $slot = $this->loadSlot($block_id, $slot_id); $booking = $slot->bookings->find($booking_id); diff --git a/app/controllers/consultation/overview.php b/app/controllers/consultation/overview.php index ce6cd31..2afb710 100644 --- a/app/controllers/consultation/overview.php +++ b/app/controllers/consultation/overview.php @@ -71,6 +71,8 @@ class Consultation_OverviewController extends ConsultationController if ($this->slot->isOccupied()) { PageLayout::postError(_('Dieser Termin ist bereits belegt.')); + } elseif (!$this->slot->isBookable()) { + PageLayout::postError(_('Dieser Termin ist für Buchungen gesperrt.')); } else { $booking = new ConsultationBooking(); $booking->slot_id = $this->slot->id; diff --git a/app/controllers/contact.php b/app/controllers/contact.php index 7dd2b05..2148777 100644 --- a/app/controllers/contact.php +++ b/app/controllers/contact.php @@ -189,9 +189,21 @@ class ContactController extends AuthenticatedController $user = User::findManyByUsername(Request::getArray('user')); } if ($group) { - $user = User::findMany(Statusgruppen::find($group)->members->pluck('user_id')); + $group_object = Statusgruppen::find($group); + if (!$group_object) { + $this->set_status(404); + $this->render_nothing(); + return; + } + $user = User::findMany($group_object->members->pluck('user_id')); } if (!$user) { + $user_object = User::findCurrent(); + if (!$user_object) { + $this->set_status(404); + $this->render_nothing(); + return; + } $user = User::findCurrent()->contacts; } diff --git a/app/controllers/contents/courseware.php b/app/controllers/contents/courseware.php index f0d5023..d4291df 100644 --- a/app/controllers/contents/courseware.php +++ b/app/controllers/contents/courseware.php @@ -262,7 +262,7 @@ class Contents_CoursewareController extends CoursewareController */ private function isCoursewareEnabled($course_id): bool { - $studip_module = PluginManager::getInstance()->getPlugin('CoursewareModule'); + $studip_module = PluginManager::getInstance()->getPlugin(CoursewareModule::class); if (!$studip_module || !$studip_module->isActivated($course_id)) { return false; @@ -311,7 +311,7 @@ class Contents_CoursewareController extends CoursewareController ); if (!$struct) { - throw new Trails_Exception(404, _('Der geteilte Inhalt kann nicht gefunden werden.')); + throw new Trails\Exception(404, _('Der geteilte Inhalt kann nicht gefunden werden.')); } if (!$struct->canRead($user) && !$struct->canEdit($user)) { diff --git a/app/controllers/course/admission.php b/app/controllers/course/admission.php index 56342bb..14a9b20 100644 --- a/app/controllers/course/admission.php +++ b/app/controllers/course/admission.php @@ -31,7 +31,7 @@ class Course_AdmissionController extends AuthenticatedController if (!get_object_type($this->course_id, ['sem']) || SeminarCategories::GetBySeminarId($this->course_id)->studygroup_mode || !$GLOBALS['perm']->have_studip_perm('tutor', $this->course_id)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $this->course = Course::find($this->course_id); @@ -488,7 +488,7 @@ class Course_AdmissionController extends AuthenticatedController } $this->course_set_name = $course_set->getName(); } else { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } } @@ -503,7 +503,7 @@ class Course_AdmissionController extends AuthenticatedController $this->redirect($response->headers['Location']); } } else { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } } @@ -518,7 +518,7 @@ class Course_AdmissionController extends AuthenticatedController $this->redirect($response->headers['Location']); } } else { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } } diff --git a/app/controllers/course/basicdata.php b/app/controllers/course/basicdata.php index 8cc7d94..97ec053 100644 --- a/app/controllers/course/basicdata.php +++ b/app/controllers/course/basicdata.php @@ -445,6 +445,14 @@ class Course_BasicdataController extends AuthenticatedController $widget = new CourseManagementSelectWidget(); $sidebar->addWidget($widget); } + + foreach ($this->flash['msg'] ?? [] as $msg) { + match ($msg[0]) { + 'msg' => PageLayout::postSuccess($msg[1]), + 'error' => PageLayout::postError($msg[1]), + 'info' => PageLayout::postInfo($msg[1]), + }; + } } /** @@ -956,20 +964,30 @@ class Course_BasicdataController extends AuthenticatedController private function _getTypes($sem, $data, &$changable = true) { $sem_types = []; + + $sem_classes = []; if ($GLOBALS['perm']->have_perm("admin")) { foreach (SemClass::getClasses() as $sc) { if (!$sc['course_creation_forbidden']) { - $sem_types[$sc['name']] = array_map(function ($st) { - return $st['name']; - }, $sc->getSemTypes()); + $sem_classes[] = $sc; } } } else { - $sc = $sem->getSemClass(); + $sem_classes[] = $sem->getSemClass(); + } + + if (!$sem->isStudyGroup()) { + $sem_classes = array_filter($sem_classes, function (SemClass $sc) { + return !$sc['studygroup_mode']; + }); + } + + foreach ($sem_classes as $sc) { $sem_types[$sc['name']] = array_map(function ($st) { return $st['name']; }, $sc->getSemTypes()); } + if (!in_array($data['status'], array_flatten(array_values(array_map('array_keys', $sem_types))))) { $class_name = $sem->getSemClass()->offsetGet('name'); if (!isset($sem_types[$class_name])) { diff --git a/app/controllers/course/block_appointments.php b/app/controllers/course/block_appointments.php index fef0d31..ad28b1e 100644 --- a/app/controllers/course/block_appointments.php +++ b/app/controllers/course/block_appointments.php @@ -33,7 +33,7 @@ class Course_BlockAppointmentsController extends AuthenticatedController SeminarCategories::GetBySeminarId($this->course_id)->studygroup_mode || !$GLOBALS['perm']->have_studip_perm("tutor", $this->course_id) ) { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } PageLayout::setHelpKeyword('Basis.VeranstaltungenVerwaltenAendernVonZeitenUndTerminen'); PageLayout::setTitle(Course::findCurrent()->getFullName() . " - " . _('Blockveranstaltungstermine anlegen')); diff --git a/app/controllers/course/cancel_dates.php b/app/controllers/course/cancel_dates.php index 0d5463c..8da0d09 100644 --- a/app/controllers/course/cancel_dates.php +++ b/app/controllers/course/cancel_dates.php @@ -39,7 +39,7 @@ class Course_CancelDatesController extends AuthenticatedController $this->course_id = $this->dates[0]->range_id; } if (!get_object_type($this->course_id, ['sem']) || !$perm->have_studip_perm("tutor", $this->course_id)) { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } PageLayout::setHelpKeyword('Basis.VeranstaltungenVerwaltenAendernVonZeitenUndTerminen'); PageLayout::setTitle(Course::findCurrent()->getFullName() . " - " . _('Veranstaltungstermine absagen')); diff --git a/app/controllers/course/change_view.php b/app/controllers/course/change_view.php index 156a68a..63395b6 100644 --- a/app/controllers/course/change_view.php +++ b/app/controllers/course/change_view.php @@ -16,7 +16,6 @@ */ class Course_ChangeViewController extends AuthenticatedController { - // see Trails_Controller#before_filter public function before_filter(&$action, &$args) { parent::before_filter($action, $args); @@ -28,7 +27,7 @@ class Course_ChangeViewController extends AuthenticatedController * Sets the current course into participant view. * Only available for tutor upwards. * - * @throws Trails_Exception Someone with unfitting rights tried to call here. + * @throws Trails\Exception Someone with unfitting rights tried to call here. */ public function set_changed_view_action() { @@ -43,7 +42,7 @@ class Course_ChangeViewController extends AuthenticatedController * Resets a course currently in participant view to normal view * with real rights. * - * @throws Trails_Exception Someone with unfitting rights tried to call here. + * @throws Trails\Exception Someone with unfitting rights tried to call here. */ public function reset_changed_view_action() { diff --git a/app/controllers/course/contentmodules.php b/app/controllers/course/contentmodules.php index 923c61b..d37d1bb 100644 --- a/app/controllers/course/contentmodules.php +++ b/app/controllers/course/contentmodules.php @@ -261,14 +261,19 @@ class Course_ContentmodulesController extends AuthenticatedController } } - PageLayout::setTitle(sprintf(_('Informationen über %s'), $this->metadata['displayname'])); + $this->metadata['icon'] = $this->getIconFromMetadata($this->metadata, $this->plugin); + + PageLayout::setTitle(sprintf( + _('Informationen über %s'), + $this->metadata['displayname'] ?? $this->plugin->getPluginName() + )); } private function getModules(Range $context) { $list = []; - foreach (PluginEngine::getPlugins('StudipModule') as $plugin) { + foreach (PluginEngine::getPlugins(StudipModule::class) as $plugin) { if (!$plugin->isActivatableForContext($context)) { continue; } @@ -291,6 +296,7 @@ class Course_ContentmodulesController extends AuthenticatedController $visibility = $tool ? $tool->getVisibilityPermission() : 'nobody'; $metadata = $plugin->getMetadata(); + $icon = $this->getIconFromMetadata($metadata, $plugin); $list[$plugin_id] = [ 'id' => $plugin_id, 'moduleclass' => get_class($plugin), @@ -299,7 +305,7 @@ class Course_ContentmodulesController extends AuthenticatedController 'displayname' => $displayname, 'visibility' => $visibility, 'active' => (bool) $tool, - 'icon' => $this->getIconFromMetadata($metadata, $plugin), + 'icon' => $icon ? $icon->asImagePath() : null, 'summary' => $metadata['summary'] ?? null, 'mandatory' => $this->sem_class->isModuleMandatory(get_class($plugin)), 'highlighted' => (bool) $plugin->isHighlighted(), @@ -315,7 +321,7 @@ class Course_ContentmodulesController extends AuthenticatedController * @param array $metadata * @param CorePlugin|StudIPPlugin $plugin */ - private function getIconFromMetadata(array $metadata, $plugin): ?string + private function getIconFromMetadata(array $metadata, $plugin): ?Icon { $icon = $metadata['icon_clickable'] ?? $metadata['icon'] ?? null; @@ -332,7 +338,7 @@ class Course_ContentmodulesController extends AuthenticatedController $icon = Icon::create($plugin->getPluginURL() . '/' . $icon); } - return $icon->copyWithRole(Icon::ROLE_CLICKABLE)->asImagePath(); + return $icon->copyWithRole(Icon::ROLE_CLICKABLE); } private function getCoreIcon(string $path): ?Icon diff --git a/app/controllers/course/courseware.php b/app/controllers/course/courseware.php index 22b124b..f0b873a 100644 --- a/app/controllers/course/courseware.php +++ b/app/controllers/course/courseware.php @@ -52,22 +52,20 @@ class Course_CoursewareController extends CoursewareController public function courseware_action($unit_id = null): void { - global $user; - Navigation::activateItem('course/courseware/unit'); if ($this->unitsNotFound) { PageLayout::postMessage(MessageBox::info(_('Es wurde kein Lernmaterial gefunden.'))); return; } + $user = User::findCurrent(); $this->setCoursewareSidebar(); - $this->user_id = $user->id; /** @var array<mixed> $last */ - $last = UserConfig::get($this->user_id)->getValue('COURSEWARE_LAST_ELEMENT'); + $last = UserConfig::get($user->id)->getValue('COURSEWARE_LAST_ELEMENT'); $lastStructuralElement = \Courseware\StructuralElement::findOneById($last); if ($unit_id === null) { - if (isset($lastStructuralElement) && $lastStructuralElement->canVisit(User::findCurrent())) { + if (isset($lastStructuralElement) && $lastStructuralElement->canVisit($user)) { $this->redirectToFirstUnit('course', Context::getId(), $last); } else { $this->redirectToFirstUnit('course', Context::getId(), []); diff --git a/app/controllers/course/dates.php b/app/controllers/course/dates.php index a4e18e9..f64d66b 100644 --- a/app/controllers/course/dates.php +++ b/app/controllers/course/dates.php @@ -80,7 +80,10 @@ class Course_DatesController extends AuthenticatedController )->asDialog(); } - if (Seminar::setInstance(new Seminar(Course::findCurrent()))->getSlotModule('documents') && CourseDateFolder::availableInRange(Course::findCurrent(), User::findCurrent()->id)) { + if ( + Seminar::setInstance(new Seminar(Course::findCurrent()))->getSlotModule('documents') + && CourseDateFolder::availableInRange(Course::findCurrent(), User::findCurrent() ? User::findCurrent()->id : null) + ) { $actions->addLink( _('Sitzungsordner anlegen'), $this->url_for('course/dates/create_folders'), diff --git a/app/controllers/course/details.php b/app/controllers/course/details.php index 2a330e3..e1c9493 100644 --- a/app/controllers/course/details.php +++ b/app/controllers/course/details.php @@ -32,7 +32,7 @@ class Course_DetailsController extends AuthenticatedController $this->course = Course::find($course_id); if (!$this->course) { - throw new Trails_Exception( + throw new Trails\Exception( 404, _('Es konnte keine Veranstaltung gefunden werden') ); diff --git a/app/controllers/course/enrolment.php b/app/controllers/course/enrolment.php index eb4428e..420b5ca 100644 --- a/app/controllers/course/enrolment.php +++ b/app/controllers/course/enrolment.php @@ -37,7 +37,7 @@ class Course_EnrolmentController extends AuthenticatedController return false; } if (!get_object_type($this->course_id, ['sem'])) { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } $course = Seminar::GetInstance($this->course_id); $enrolment_info = $course->getEnrolmentInfo($GLOBALS['user']->id); diff --git a/app/controllers/course/forum/forum_controller.php b/app/controllers/course/forum/forum_controller.php index 71d1aa0..65eec63 100644 --- a/app/controllers/course/forum/forum_controller.php +++ b/app/controllers/course/forum/forum_controller.php @@ -23,7 +23,7 @@ abstract class ForumController extends StudipController { parent::before_filter($action, $args); - $this->flash = Trails_Flash::instance(); + $this->flash = Trails\Flash::instance(); // Set help keyword for Stud.IP's user-documentation and page title PageLayout::setHelpKeyword('Basis.Forum'); diff --git a/app/controllers/course/gradebook/lecturers.php b/app/controllers/course/gradebook/lecturers.php index b3b1f82..7ab02e7 100644 --- a/app/controllers/course/gradebook/lecturers.php +++ b/app/controllers/course/gradebook/lecturers.php @@ -258,7 +258,7 @@ class Course_Gradebook_LecturersController extends AuthenticatedController public function edit_custom_definition_action($definitionId) { if (!$this->definition = Definition::findOneBySQL('id = ? AND course_id = ?', [$definitionId, \Context::getId()])) { - throw new \Trails_Exception(404); + throw new \Trails\Exception(404); } // show template @@ -271,7 +271,7 @@ class Course_Gradebook_LecturersController extends AuthenticatedController { CSRFProtection::verifyUnsafeRequest(); if (!$definition = Definition::findOneBySQL('id = ? AND course_id = ?', [$definitionId, \Context::getId()])) { - throw new \Trails_Exception(404); + throw new \Trails\Exception(404); } $name = trim(\Request::get('name', '')); diff --git a/app/controllers/course/grouping.php b/app/controllers/course/grouping.php index 4f35b53..3cef673 100644 --- a/app/controllers/course/grouping.php +++ b/app/controllers/course/grouping.php @@ -36,6 +36,11 @@ class Course_GroupingController extends AuthenticatedController if (!$GLOBALS['perm']->have_studip_perm('tutor', $this->course->id)) { throw new AccessDeniedException(_('Sie haben leider nicht die notwendige Berechtigung für diese Aktion.')); } + + if ($GLOBALS['perm']->have_studip_perm('admin', $this->course->id)) { + $widget = new CourseManagementSelectWidget(); + Sidebar::get()->addWidget($widget); + } } /** diff --git a/app/controllers/course/lti.php b/app/controllers/course/lti.php index e0ca2cf..4db7776 100644 --- a/app/controllers/course/lti.php +++ b/app/controllers/course/lti.php @@ -1,4 +1,7 @@ <?php + +use Studip\OAuth2\NegotiatesWithPsr7; + /** * course/lti.php - LTI consumer API for Stud.IP * @@ -13,6 +16,8 @@ class Course_LtiController extends StudipController { + use NegotiatesWithPsr7; + /** * Callback function being called before an action is executed. */ @@ -268,22 +273,15 @@ class Course_LtiController extends StudipController */ public function save_link_action($tool_id) { - require_once 'vendor/oauth-php/library/OAuthRequestVerifier.php'; - $tool = LtiTool::find($tool_id); $lti_msg = Request::get('lti_msg'); $lti_errormsg = Request::get('lti_errormsg'); $content_items = Request::get('content_items'); $content_items = json_decode($content_items, true); - OAuthStore::instance('PDO', [ - 'dsn' => 'mysql:host=' . $GLOBALS['DB_STUDIP_HOST'] . ';dbname=' . $GLOBALS['DB_STUDIP_DATABASE'], - 'username' => $GLOBALS['DB_STUDIP_USER'], - 'password' => $GLOBALS['DB_STUDIP_PASSWORD'] - ]); - - $oarv = new OAuthRequestVerifier(); - $oarv->verifySignature($tool->consumer_secret, false, false); + if (!Studip\OAuth1::verifyRequest($this->getPsrRequest(), $tool->consumer_secret, '')) { + throw new Exception('Could not verify request.'); + } if (is_array($content_items) && count($content_items['@graph'])) { // we only support selecting a single content item @@ -452,18 +450,11 @@ class Course_LtiController extends StudipController */ public function outcome_action($id) { - require_once 'vendor/oauth-php/library/OAuthRequestVerifier.php'; - $lti_data = LtiData::find($id); - OAuthStore::instance('PDO', [ - 'dsn' => 'mysql:host=' . $GLOBALS['DB_STUDIP_HOST'] . ';dbname=' . $GLOBALS['DB_STUDIP_DATABASE'], - 'username' => $GLOBALS['DB_STUDIP_USER'], - 'password' => $GLOBALS['DB_STUDIP_PASSWORD'] - ]); - - $oarv = new OAuthRequestVerifier(); - $oarv->verifySignature($lti_data->getConsumerSecret(), false, false); + if (!Studip\OAuth1::verifyRequest($this->getPsrRequest(), $lti_data->getConsumerSecret(), '')) { + throw new Exception('Could not verify request.'); + } // fetch and parse POST data $message = file_get_contents('php://input'); diff --git a/app/controllers/course/lvgselector.php b/app/controllers/course/lvgselector.php index 3542a11..c8223a9 100644 --- a/app/controllers/course/lvgselector.php +++ b/app/controllers/course/lvgselector.php @@ -17,15 +17,13 @@ require 'config/mvv_config.php'; class Course_LvgselectorController extends AuthenticatedController { - - // see Trails_Controller#before_filter public function before_filter(&$action, &$args) { parent::before_filter($action, $args); $this->course = Course::findCurrent(); if (!$this->course) { - throw new Trails_Exception(404, _('Es wurde keine Veranstaltung ausgewählt!')); + throw new Trails\Exception(404, _('Es wurde keine Veranstaltung ausgewählt!')); } $this->course_id = $this->course->id; if (!$GLOBALS['perm']->have_studip_perm('tutor', $this->course_id)) { @@ -37,6 +35,11 @@ class Course_LvgselectorController extends AuthenticatedController $widget = new HelpbarWidget(); $widget->addElement(new WidgetElement(_('Auf dieser Seite kann die Veranstaltung ausgewählten Lehrveranstaltungsgruppen zugeordnet werden.'))); Helpbar::get()->addWidget($widget); + + if ($GLOBALS['perm']->have_studip_perm('admin', $this->course_id)) { + $widget = new CourseManagementSelectWidget(); + Sidebar::get()->addWidget($widget); + } } /** diff --git a/app/controllers/course/members.php b/app/controllers/course/members.php index 3eae083..1ec1587 100644 --- a/app/controllers/course/members.php +++ b/app/controllers/course/members.php @@ -232,7 +232,7 @@ class Course_MembersController extends AuthenticatedController $course_member = AdmissionApplication::find([$user_id, $this->course_id]); } if (is_null($course_member)) { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } $this->comment = $course_member->comment; $this->user = User::find($user_id); @@ -265,7 +265,7 @@ class Course_MembersController extends AuthenticatedController $course_member = AdmissionApplication::find([$user_id, $this->course_id]); } if (!Request::submitted('save') || is_null($course_member)) { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } $course_member->comment = Request::get('comment'); diff --git a/app/controllers/course/messenger.php b/app/controllers/course/messenger.php index 3e692aa..710ac87 100644 --- a/app/controllers/course/messenger.php +++ b/app/controllers/course/messenger.php @@ -12,16 +12,20 @@ class Course_MessengerController extends AuthenticatedController public function course_action($thread_id = null) { - if (Context::get()) { - PageLayout::setTitle(Context::get()->getFullName() . ' - ' . _('Blubber')); + $context = Context::get(); + + if (!$context) { + throw new CheckObjectException(_('Sie haben kein Objekt gewählt.')); } if (Navigation::hasItem('/course/blubber')) { Navigation::activateItem('/course/blubber'); } + PageLayout::setTitle($context->getFullName() . ' - ' . _('Blubber')); + $this->search = ''; - $this->threads = BlubberThread::findByContext(Context::get()->id, true, Context::getType()); + $this->threads = BlubberThread::findByContext($context->id, true, Context::getType()); $this->thread = null; $this->threads_more_down = 0; diff --git a/app/controllers/course/overview.php b/app/controllers/course/overview.php index 96e7f5a..876de5a 100644 --- a/app/controllers/course/overview.php +++ b/app/controllers/course/overview.php @@ -58,8 +58,6 @@ class Course_OverviewController extends AuthenticatedController // Fetch votes if (Config::get()->VOTE_ENABLE) { - $response = $this->relay('evaluation/display/' . $this->course_id); - $this->evaluations = $response->body; $response = $this->relay('questionnaire/widget/' . $this->course_id); $this->questionnaires = $response->body; } @@ -113,7 +111,7 @@ class Course_OverviewController extends AuthenticatedController $this->avatar = StudygroupAvatar::getAvatar($this->course_id); } - $this->plugins = PluginEngine::getPlugins('StandardPlugin', $this->course_id); + $this->plugins = PluginEngine::getPlugins(StandardPlugin::class, $this->course_id); $sidebar = Sidebar::get(); diff --git a/app/controllers/course/room_requests.php b/app/controllers/course/room_requests.php index ab802b7..b091047 100644 --- a/app/controllers/course/room_requests.php +++ b/app/controllers/course/room_requests.php @@ -46,7 +46,7 @@ class Course_RoomRequestsController extends AuthenticatedController SeminarCategories::GetBySeminarId($this->course_id)->studygroup_mode || !$GLOBALS['perm']->have_studip_perm("tutor", $this->course_id) ) { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } PageLayout::setHelpKeyword('Basis.VeranstaltungenVerwaltenAendernVonZeitenUndTerminen'); @@ -143,25 +143,15 @@ class Course_RoomRequestsController extends AuthenticatedController // a single date or whole course $this->request_range_id = Request::get('range_id', Context::getId()); - if (!isset($_SESSION[$this->request_id])) { - $_SESSION[$this->request_id] = []; - } + $this->init_session(); $_SESSION[$this->request_id]['range'] = $this->request_range ?: $_SESSION[$this->request_id]['range'] ?? null; $_SESSION[$this->request_id]['range_ids'] = $this->request_range_ids ?: [$this->request_range_id]; - $_SESSION[$this->request_id]['search_by'] = ''; - $_SESSION[$this->request_id]['room_category_id'] = ''; - $_SESSION[$this->request_id]['room_id'] = ''; - $_SESSION[$this->request_id]['room_name'] = ''; - $_SESSION[$this->request_id]['selected_properties'] = []; - - $this->request = null; // look for existing request or create a new one $this->request = new RoomRequest($this->request_id); // time ranges (start date, end date) $this->request->setRangeFields($_SESSION[$this->request_id]['range'], $_SESSION[$this->request_id]['range_ids']); $this->request_time_intervals = $this->request->getTimeIntervals(); - } /** @@ -211,7 +201,6 @@ class Course_RoomRequestsController extends AuthenticatedController ); } } - } /** @@ -227,7 +216,6 @@ class Course_RoomRequestsController extends AuthenticatedController _('Das Erstellen von Raumanfragen ist nicht erlaubt!') ); } - $this->request_id = $request_id; $this->step = (int)$step; $this->room_name = $_SESSION[$request_id]['room_name']; @@ -266,15 +254,17 @@ class Course_RoomRequestsController extends AuthenticatedController $this->selected_room = Resource::find($_SESSION[$request_id]['room_id'] ?: $this->request->resource_id); $this->selected_room_category_id = $this->selected_room->category_id ?? $_SESSION[$request_id]['room_category_id'] ?? null; + $this->category = $this->selected_room_category_id ? ResourceCategory::find($this->selected_room_category_id) : null; $_SESSION[$request_id]['room_category_id'] = $_SESSION[$request_id]['room_category_id'] ?? $this->selected_room->category_id ?? null; // after selecting a room, go to next step or stay here if no room was selected at all if (Request::submitted('select_room')) { $this->selected_room_id = Request::get('selected_room_id'); + $room = Room::find($this->selected_room_id); $_SESSION[$request_id]['room_id'] = $this->selected_room_id; + $_SESSION[$request_id]['room_category_id'] = $room->category_id; $_SESSION[$request_id]['select_room'] = true; - $this->redirect( 'course/room_requests/request_check_properties/' . $this->request_id ); @@ -289,8 +279,6 @@ class Course_RoomRequestsController extends AuthenticatedController ); return; } - - // or we filter via category else if (Request::get('category_id') && Request::submitted('select_properties')) { $_SESSION[$request_id]['search_by'] = 'category'; $_SESSION[$request_id]['room_category_id'] = Request::get('category_id'); @@ -299,74 +287,35 @@ class Course_RoomRequestsController extends AuthenticatedController ); return; } else if (Request::submitted('reset_category')) { - //Delete all selected properties from the session since the category is reset - $_SESSION[$request_id]['selected_properties'] = []; - $_SESSION[$request_id]['room_category_id'] = ''; - $_SESSION[$request_id]['room_name'] = ''; - $_SESSION[$request_id]['room_id'] = ''; - $this->redirect('course/room_requests/request_find_available_properties/' . $this->request_id . '/1'); + $this->init_session(); + $this->redirect('course/room_requests/new_request'); return; } // for step 2: after choosing a specific room OR searching via properties if ($this->step === 2) { - if ($_SESSION[$request_id]['search_by'] == 'roomname') { - // find category via room - $this->category = ResourceCategory::find($this->selected_room_category_id); - if ($this->category) { - $this->available_properties = $this->category->getRequestableProperties(); - } - - $this->selected_properties = $_SESSION[$request_id]['selected_properties']; - $this->room = Room::find($_SESSION[$request_id]['room_id']); - $this->selected_properties['seats'] = $_SESSION[$request_id]['selected_properties']['seats'] - ?: $this->course->admission_turnout - ?: Config::get()->RESOURCES_ROOM_REQUEST_DEFAULT_SEATS; - $_SESSION[$request_id]['selected_properties']['seats'] = $this->selected_properties['seats']; - } else if ($_SESSION[$request_id]['search_by'] === 'category') { + if (!empty(Request::getArray('selected_properties'))) { + $this->selected_properties = Request::getArray('selected_properties'); + } else { + $this->selected_properties = $_SESSION[$request_id]['selected_properties']; + } + $_SESSION[$request_id]['selected_properties'] = $this->selected_properties; + if ($_SESSION[$request_id]['search_by'] === 'roomname') { + $this->selected_properties = $_SESSION[$request_id]['selected_properties'] ?? null; $this->room = Room::find($_SESSION[$request_id]['room_id']); - if ($this->room) { - $this->grouped_properties = $this->room->getGroupedProperties(); - foreach ($this->grouped_properties as $properties) { - foreach ($properties as $property) { - $this->selected_properties[$property->name] = $property->state; - } - } + if (!isset($_SESSION[$request_id]['selected_properties']['seats'])) { + $this->selected_properties['seats'] = $this->course->admission_turnout ?? Config::get()->RESOURCES_ROOM_REQUEST_DEFAULT_SEATS; } - + $_SESSION[$request_id]['selected_properties']['seats'] = $this->selected_properties['seats']; + $_SESSION[$request_id]['room_category_id'] = $this->selected_room_category_id; + } else { + // let's find all the properties belonging to the selected category + $this->room_category_id = $_SESSION[$request_id]['room_category_id']; } - // find rooms fitting to category and properties - if (Request::submitted('search_rooms')) { - $this->selected_properties = Request::getArray('selected_properties'); - $_SESSION[$request_id]['selected_properties'] = $this->selected_properties; - // no min number of seats - if ( - (!$_SESSION[$request_id]['selected_properties']['seats'] || $_SESSION[$request_id]['selected_properties']['seats'] < 1) - && $_SESSION[$request_id]['search_by'] === 'category' - ) { - PageLayout::postError( - _('Die Mindestanzahl der Sitzplätze beträgt 1!') - ); - $this->redirect( - 'course/room_requests/request_find_matching_rooms/' . $request_id . '/' . $this->step - ); - return; - } else { - $this->redirect( - 'course/room_requests/request_find_matching_rooms/' . $request_id . '/' . $this->step - ); - return; - } + if ($this->category) { + $this->available_properties = $this->category->getRequestableProperties(); } - - // let's find all the properties belonging to the selected category - $this->room_category_id = $_SESSION[$request_id]['room_category_id']; - $this->category = ResourceCategory::find($this->room_category_id); - $this->available_properties = $this->category->getRequestableProperties(); - - // properties, like 'Sitzplätze', 'behindertengerecht' etc - $this->selected_properties = $_SESSION[$request_id]['selected_properties'] ?? null; $this->preparation_time = $_SESSION[$request_id]['preparation_time'] ?? null; $this->comment = $_SESSION[$request_id]['comment'] ?? null; $this->request->category_id = $_SESSION[$request_id]['room_category_id']; @@ -375,7 +324,10 @@ class Course_RoomRequestsController extends AuthenticatedController if (Request::submitted('show_summary')) { $this->selected_room_id = Request::get('selected_room_id'); $_SESSION[$request_id]['room_id'] = $this->selected_room_id; - $_SESSION[$request_id]['selected_properties'] = Request::getArray('selected_properties'); + $room = Room::find($this->selected_room_id); + if ($room) { + $_SESSION[$request_id]['room_category_id'] = $room->category_id; + } $this->redirect('course/room_requests/request_show_summary/' . $this->request_id ); } } @@ -394,16 +346,18 @@ class Course_RoomRequestsController extends AuthenticatedController _('Das Erstellen von Raumanfragen ist nicht erlaubt!') ); } - $this->request_id = $request_id; $this->step = (int)$step; $this->request = new RoomRequest($this->request_id); - $this->request->setRangeFields($_SESSION[$this->request_id]['range'], $_SESSION[$this->request_id]['range_ids']); + $this->request->setRangeFields( + $_SESSION[$this->request_id]['range'] ?? null, + $_SESSION[$this->request_id]['range_ids'] ?? null + ); // let's find all the properties belonging to the selected category $this->room_category_id = $_SESSION[$request_id]['room_category_id'] ?: $this->request->category_id; - $this->room_name = $_SESSION[$request_id]['room_name']; + $this->room_name = $_SESSION[$request_id]['room_name'] ?? ''; $this->selected_room = Resource::find($_SESSION[$request_id]['room_id'] ?: $this->request->resource_id); $this->category = $this->room_category_id ? ResourceCategory::find($this->room_category_id) : ''; $this->available_properties = $this->room_category_id ? $this->category->getRequestableProperties() : ''; @@ -418,7 +372,10 @@ class Course_RoomRequestsController extends AuthenticatedController $this->comment = $_SESSION[$request_id]['comment'] ?? null; // when searching for a room name, list found room - if ($_SESSION[$request_id]['room_name'] !== '') { + if ( + isset($_SESSION[$request_id]['room_name']) + && $_SESSION[$request_id]['room_name'] !== '' + ) { $search_properties['room_category_id'] = $this->room_category_id; $search_properties['seats'] = [ 1, @@ -455,11 +412,13 @@ class Course_RoomRequestsController extends AuthenticatedController } $this->request_id = $request_id; - + $this->selected_properties = Request::getArray('selected_properties'); // select a room, search for a room name or search for rooms matching properties if (Request::submitted('select_room')) { $this->selected_room_id = Request::get('selected_room_id'); + $room = Room::find($this->selected_room_id); $_SESSION[$request_id]['room_id'] = $this->selected_room_id; + $_SESSION[$request_id]['room_category_id'] = $room->category_id; $_SESSION[$request_id]['select_room'] = true; $this->step = 2; $this->request = new RoomRequest($this->request_id); @@ -467,7 +426,6 @@ class Course_RoomRequestsController extends AuthenticatedController 'course/room_requests/request_find_matching_rooms/' . $this->request_id . '/' . $this->step ); } else if (Request::get('room_name') && Request::submitted('search_by_name')) { - $this->selected_properties = Request::getArray('selected_properties'); $this->category_id = Request::get('category_id'); $_SESSION[$request_id]['selected_properties'] = $this->selected_properties; $_SESSION[$request_id]['room_category_id'] = $this->category_id; @@ -479,7 +437,6 @@ class Course_RoomRequestsController extends AuthenticatedController ); } else if (Request::submitted('search_rooms')) { - $this->selected_properties = Request::getArray('selected_properties'); $this->category_id = Request::get('category_id'); $_SESSION[$request_id]['room_category_id'] = $this->category_id; $_SESSION[$request_id]['selected_properties'] = $this->selected_properties; @@ -506,8 +463,7 @@ class Course_RoomRequestsController extends AuthenticatedController } } else if (Request::submitted('reset_category')) { //Delete all selected properties from the session since the category is reset - $_SESSION[$request_id]['selected_properties'] = []; - $_SESSION[$request_id]['room_category_id'] = ''; + $this->init_session(); $this->redirect('course/room_requests/request_find_available_properties/' . $this->request_id . '/1'); } else if (Request::submitted('search_by_category')) { if (Request::get('category_id') === '0') { @@ -521,12 +477,11 @@ class Course_RoomRequestsController extends AuthenticatedController ); } else if (Request::submitted('show_summary')) { $this->request = new RoomRequest($this->request_id); - $this->selected_properties = Request::getArray('selected_properties'); - - $_SESSION[$request_id]['selected_properties'] = $this->selected_properties; $this->selected_room_id = Request::get('selected_room_id'); + $room = Room::find($this->selected_room_id); $_SESSION[$request_id]['room_id'] = $this->selected_room_id; - + $_SESSION[$request_id]['room_category_id'] = $room->category_id ?? $_SESSION[$request_id]['room_category_id']; + $_SESSION[$request_id]['selected_properties'] = $this->selected_properties; $this->redirect('course/room_requests/request_show_summary/' . $this->request_id ); } else { $room = Room::find($_SESSION[$request_id]['room_id']); @@ -569,7 +524,6 @@ class Course_RoomRequestsController extends AuthenticatedController ); $this->selected_room_category = ResourceCategory::find($_SESSION[$request_id]['room_category_id'] ?? $this->request->category_id); - $this->selected_room = Resource::find($_SESSION[$request_id]['room_id'] ?? $this->request->resource_id); $this->room_id = $_SESSION[$request_id]['room_id'] ?? $this->request->resource_id; @@ -592,7 +546,7 @@ class Course_RoomRequestsController extends AuthenticatedController $_SESSION[$request_id]['search_by'] = $this->selected_room ? 'roomname' : 'category'; $_SESSION[$request_id]['room_category_id'] = $this->selected_room_category->id; - $_SESSION[$request_id]['room_id'] = $this->selected_room->id; + $_SESSION[$request_id]['room_id'] = $this->selected_room ? $this->selected_room->id : ''; } public function store_request_action($request_id) @@ -629,8 +583,12 @@ class Course_RoomRequestsController extends AuthenticatedController $this->request->store(); //Store the properties: - foreach ($_SESSION[$request_id]['selected_properties'] as $name => $state) { - $this->request->setProperty($name, $state); + if (isset($_SESSION[$request_id]['selected_properties'])) { + foreach ($_SESSION[$request_id]['selected_properties'] as $name => $state) { + if (!empty($state)) { + $this->request->setProperty($name, $state); + } + } } // once stored, we can delete the session data for this request @@ -707,7 +665,7 @@ class Course_RoomRequestsController extends AuthenticatedController { $request = RoomRequest::find($request_id); if (!$request) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } if (Request::isGet()) { PageLayout::postQuestion(sprintf( @@ -723,4 +681,19 @@ class Course_RoomRequestsController extends AuthenticatedController } $this->redirect('course/timesrooms/index'); } + + private function init_session() + { + $_SESSION[$this->request_id] = array_merge( + $_SESSION[$this->request_id] ?? [], + [ + 'search_by' => '', + 'room_category_id' => '', + 'room_id' => '', + 'room_name' => '', + 'select_room' => false, + 'selected_properties' => [], + ] + ); + } } diff --git a/app/controllers/course/scm.php b/app/controllers/course/scm.php index 66af981..4c5d4eb 100644 --- a/app/controllers/course/scm.php +++ b/app/controllers/course/scm.php @@ -93,7 +93,7 @@ class Course_ScmController extends AuthenticatedController $this->scm = $id ? $this->scms->find($id) : $this->scms->first(); if (!$this->scm && $this->scms->count() > 0) { - throw new Trails_Exception(404, _('Es konnte keine freie Informationsseite mit der angegebenen Id gefunden werden.')); + throw new Trails\Exception(404, _('Es konnte keine freie Informationsseite mit der angegebenen Id gefunden werden.')); } if (Request::get('verify') === 'delete') { diff --git a/app/controllers/course/statusgroups.php b/app/controllers/course/statusgroups.php index c15733d..6f96299 100644 --- a/app/controllers/course/statusgroups.php +++ b/app/controllers/course/statusgroups.php @@ -762,7 +762,7 @@ class Course_StatusgroupsController extends AuthenticatedController // Safety check if no group_id at all. if (!$group_id) { - throw new Trails_Exception(400); + throw new Trails\Exception(400); } } @@ -1487,4 +1487,20 @@ class Course_StatusgroupsController extends AuthenticatedController return $members->orderBy($order); } + + public function details_action(Statusgruppen $group): void + { + $course = Course::findCurrent(); + + if ($course->id !== $group->range_id) { + throw new AccessDeniedException(); + } + + PageLayout::setTitle(sprintf( + _('Personen der Gruppe %s'), + $group->name + )); + + $this->group = $group; + } } diff --git a/app/controllers/course/study_areas.php b/app/controllers/course/study_areas.php index df54bb4..e951d11 100644 --- a/app/controllers/course/study_areas.php +++ b/app/controllers/course/study_areas.php @@ -18,7 +18,6 @@ require_once 'lib/webservices/api/studip_lecture_tree.php'; class Course_StudyAreasController extends AuthenticatedController { - // see Trails_Controller#before_filter public function before_filter(&$action, &$args) { parent::before_filter($action, $args); @@ -101,7 +100,7 @@ class Course_StudyAreasController extends AuthenticatedController public function save_action() { if($this->locked) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $params = []; diff --git a/app/controllers/course/studygroup.php b/app/controllers/course/studygroup.php index b75d91e..cd08ba3 100644 --- a/app/controllers/course/studygroup.php +++ b/app/controllers/course/studygroup.php @@ -10,8 +10,6 @@ require_once 'lib/user_visible.inc.php'; */ class Course_StudygroupController extends AuthenticatedController { - - // see Trails_Controller#before_filter public function before_filter(&$action, &$args) { parent::before_filter($action, $args); @@ -596,6 +594,11 @@ class Course_StudygroupController extends AuthenticatedController global $perm; $id = Context::getId(); + + if (!$id) { + throw new CheckObjectException(_('Sie haben kein Objekt gewählt.')); + } + $user = Request::username('user'); if ($from_status === 'moderator') { @@ -780,7 +783,7 @@ class Course_StudygroupController extends AuthenticatedController // send invite message to user $msg = new messaging(); $sem = new Seminar($id); - $message = sprintf(_("%s möchte Sie auf die Studiengruppe %s aufmerksam machen. Klicken Sie auf den untenstehenden Link, um direkt zur Studiengruppe zu gelangen.\n\n %s"), + $message = sprintf(_("%s möchte Sie auf die Studiengruppe %s aufmerksam machen. Klicken Sie auf den folgenden Link, um direkt zur Studiengruppe zu gelangen.\n\n %s"), get_fullname(), $sem->name, URLHelper::getlink("dispatch.php/course/studygroup/details/" . $id, ['cid' => null])); $subject = _("Sie wurden in eine Studiengruppe eingeladen"); $msg->insert_message($message, get_username($receiver), '', '', '', '', '', $subject); @@ -846,7 +849,7 @@ class Course_StudygroupController extends AuthenticatedController return; } } - throw new Trails_Exception(401); + throw new Trails\Exception(401); } diff --git a/app/controllers/course/timesrooms.php b/app/controllers/course/timesrooms.php index 2e3b3fe..4788eee 100644 --- a/app/controllers/course/timesrooms.php +++ b/app/controllers/course/timesrooms.php @@ -14,7 +14,7 @@ class Course_TimesroomsController extends AuthenticatedController * @param String $action Action to be executed * @param Array $args Arguments passed to the action * - * @throws Trails_Exception when either no course was found or the user + * @throws Trails\Exception when either no course was found or the user * may not access this area */ public function before_filter(&$action, &$args) @@ -23,7 +23,7 @@ class Course_TimesroomsController extends AuthenticatedController // Try to find a valid course if (!Course::findCurrent()) { - throw new Trails_Exception(404, _('Es wurde keine Veranstaltung ausgewählt!')); + throw new Trails\Exception(404, _('Es wurde keine Veranstaltung ausgewählt!')); } if (!$GLOBALS['perm']->have_studip_perm('tutor', Course::findCurrent()->id)) { @@ -250,7 +250,7 @@ class Course_TimesroomsController extends AuthenticatedController /** * Edit the start-semester of a course * - * @throws Trails_DoubleRenderError + * @throws Trails\Exceptions\DoubleRenderError */ public function editSemester_action() { @@ -331,11 +331,6 @@ class Course_TimesroomsController extends AuthenticatedController $this->date = CourseDate::find($termin_id) ?: CourseExDate::find($termin_id); $this->attributes = []; - if ($request = RoomRequest::findByDate($this->date->id)) { - $this->params = ['request_id' => $request->getId()]; - } else { - $this->params = ['new_room_request_type' => 'date_' . $this->date->id]; - } $this->only_bookable_rooms = Request::submitted('only_bookable_rooms'); if (Config::get()->RESOURCES_ENABLE) { @@ -373,7 +368,7 @@ class Course_TimesroomsController extends AuthenticatedController * * @param $termin_id * - * @throws Trails_DoubleRenderError + * @throws Trails\Exceptions\DoubleRenderError */ public function saveDate_action($termin_id) { @@ -439,7 +434,7 @@ class Course_TimesroomsController extends AuthenticatedController } // Set assigned groups - $assigned_groups = Request::optionArray('assigned_groups'); + $assigned_groups = Request::optionArray('assigned-groups'); $termin->statusgruppen = Statusgruppen::findMany($assigned_groups); $termin->store(); @@ -449,7 +444,11 @@ class Course_TimesroomsController extends AuthenticatedController } // Set Room - $old_room_id = $termin->room_booking->resource_id; + if ($termin->room_booking) { + $old_room_id = $termin->room_booking->resource_id; + } else { + $old_room_id = null; + } $singledate = new SingleDate($termin); if ($singledate->setTime($date, $end_time)) { $singledate->store(); @@ -517,7 +516,7 @@ class Course_TimesroomsController extends AuthenticatedController '<strong>' . htmlReady($singledate->toString()) . '</strong>' )); } - if ($singledate->messages['error']) { + if (!empty($singledate->messages['error'])) { PageLayout::postError( _('Die folgenden Fehler traten beim Bearbeiten des Termins auf:'), htmlReady($singledate->messages['error']) @@ -549,7 +548,7 @@ class Course_TimesroomsController extends AuthenticatedController /** * Save Single Date * - * @throws Trails_DoubleRenderError + * @throws Trails\Exceptions\DoubleRenderError */ public function saveSingleDate_action() { @@ -1490,23 +1489,10 @@ class Course_TimesroomsController extends AuthenticatedController } Sidebar::Get()->addWidget($widget); - if ($GLOBALS['perm']->have_perm('admin')) { - $list = new SelectWidget( - _('Veranstaltungen'), - $this->indexURL(), - 'cid' - ); - foreach (AdminCourseFilter::get()->getCoursesForAdminWidget() as $seminar) { - $list->addElement(new SelectElement( - $seminar['Seminar_id'], - $seminar['Name'], - $seminar['Seminar_id'] === Context::getId(), - $seminar['VeranstaltungsNummer'] . ' ' . $seminar['Name'] - )); - } - $list->size = 8; - Sidebar::Get()->addWidget($list); + if ($GLOBALS['perm']->have_studip_perm('admin', $this->course->id)) { + $widget = new CourseManagementSelectWidget(); + Sidebar::get()->addWidget($widget); } } @@ -1701,7 +1687,7 @@ class Course_TimesroomsController extends AuthenticatedController } else { $user_rooms = RoomManager::getUserRooms($current_user); foreach ($user_rooms as $room) { - if ($room->userHasBookingRights($current_user, $begin, $end)) { + if ($room->userHasBookingRights($current_user, $begin ?? null, $end ?? null)) { $rooms_with_booking_permissions++; if ($only_bookable_rooms) { foreach ($all_time_intervals as $interval) { diff --git a/app/controllers/course/wiki.php b/app/controllers/course/wiki.php index 7a3a09e..4de56a7 100644 --- a/app/controllers/course/wiki.php +++ b/app/controllers/course/wiki.php @@ -18,7 +18,7 @@ class Course_WikiController extends AuthenticatedController parent::before_filter($action, $args); object_set_visit_module('wiki'); $this->range = Context::get(); - $this->plugin = PluginManager::getInstance()->getPlugin('CoreWiki'); + $this->plugin = PluginManager::getInstance()->getPlugin(CoreWiki::class); PageLayout::setTitle(Navigation::getItem('/course/wiki')->getTitle()); } @@ -117,8 +117,9 @@ class Course_WikiController extends AuthenticatedController $startPage = WikiPage::find($this->range->getConfiguration()->WIKI_STARTPAGE_ID); $this->contentbar = ContentBar::get() ->setTOC(CoreWiki::getTOC($this->page)) - ->setIcon(Icon::create('wiki')) - ->setInfo(sprintf( + ->setIcon(Icon::create('wiki')); + if (!$this->page->isNew()) { + $this->contentbar->setInfo(sprintf( _('Version %1$s, geändert von %2$s <br> am %3$s'), $this->page->versionnumber, sprintf( @@ -128,33 +129,44 @@ class Course_WikiController extends AuthenticatedController ), date('d.m.Y H:i:s', $this->page['chdate']) )); - $action_menu = ActionMenu::get(); - if ($this->page->isEditable()) { - $action_menu->addLink( - $this->editURL($this->page), - _('Bearbeiten'), - Icon::create('edit') - ); + $action_menu = ActionMenu::get(); + if ($this->page->isEditable()) { + $action_menu->addLink( + $this->editURL($this->page), + _('Bearbeiten'), + Icon::create('edit') + ); + $action_menu->addLink( + $this->pagesettingsURL($this->page->id), + _('Seiteneinstellungen'), + Icon::create('settings'), + ['data-dialog' => 'width=700'] + ); + if (count($this->page->versions) > 0) { + $action_menu->addLink( + $this->ask_deletingURL($this->page), + _('Seite / Version löschen'), + Icon::create('trash'), + ['data-dialog' => 'size=auto'] + ); + } else { + $action_menu->addButton( + 'delete', + _('Seite löschen'), + Icon::create('trash'), + ['data-confirm' => _('Wollen Sie wirklich die komplette Seite löschen?'), 'form' => 'delete_page'] + ); + } + } $action_menu->addLink( - $this->pagesettingsURL($this->page->id), - _('Seiteneinstellungen'), - Icon::create('settings'), - ['data-dialog' => 'width=700'] - ); - $action_menu->addButton( - 'delete', - _('Seite löschen'), - Icon::create('trash'), - ['data-confirm' => _('Wollen Sie wirklich die komplette Seite löschen?'), 'form' => 'delete_page'] + '#', + _('Als Vollbild anzeigen'), + Icon::create('screen-full'), + ['class' => 'fullscreen-trigger hidden-medium-down'] ); + $this->contentbar->setActionMenu($action_menu); } - $action_menu->addLink( - '#', - _('Als Vollbild anzeigen'), - Icon::create('screen-full'), - ['class' => 'fullscreen-trigger hidden-medium-down'] - ); - $this->contentbar->setActionMenu($action_menu); + } public function pagesettings_action(WikiPage $page) @@ -253,7 +265,10 @@ class Course_WikiController extends AuthenticatedController "[[ " . $values['name'], $p2['content'] ); - $p2->store(); + if ($p2->isDirty()) { + $p2['user_id'] = User::findCurrent()->id; + $p2->store(); + } } })->validate(); if (Request::isPost()) { @@ -269,9 +284,17 @@ class Course_WikiController extends AuthenticatedController $this->render_form($this->form); } + public function ask_deleting_action(WikiPage $page) + { + if (!$page->isEditable()) { + throw new AccessDeniedException(); + } + PageLayout::setTitle(_('Was genau soll gelöscht werden?')); + } + public function delete_action(WikiPage $page) { - if (!Request::isPost() || !CSRFProtection::verifyRequest()) { + if (!Request::isPost() || !$page->isEditable() || !CSRFProtection::verifyRequest()) { throw new AccessDeniedException(); } $name = $page->name; @@ -280,13 +303,52 @@ class Course_WikiController extends AuthenticatedController $this->redirect($this->allpagesURL()); } + public function deleteversion_action(WikiPage $page, $version_id = null) + { + if (!Request::isPost() || !$page->isEditable() || !CSRFProtection::verifyRequest()) { + throw new AccessDeniedException(); + } + if ($version_id === null) { + $version = $page->versions[0]; + if ($version) { + $page['name'] = $version['name']; + $page['content'] = $version['content']; + $page['user_id'] = $version['user_id']; + $page['chdate'] = $version['mkdate']; + $page->store(); + $version->delete(); + } else { + $page->delete(); + } + } else { + $version = WikiVersion::find($version_id); + if ($version['page_id'] === $page->id) { + $version->delete(); + } + } + PageLayout::postSuccess(_('Version wurde gelöscht.')); + if (Request::get('redirect_to') === 'page') { + $this->redirect($this->page($page)); + } else { + $this->redirect($this->history($page)); + } + + } + public function allpages_action() { - Navigation::activateItem('/course/wiki/allpages'); $this->pages = WikiPage::findBySQL( "`range_id` = ? ORDER BY `name` ASC", [$this->range->id] ); + + if (count($this->pages) === 0) { + $this->redirect($this->pageURL()); + return; + } + + Navigation::activateItem('/course/wiki/allpages'); + if ($GLOBALS['perm']->have_studip_perm('tutor', $this->range->id)) { $actions = new ActionsWidget(); $actions->addLink( @@ -379,7 +441,7 @@ class Course_WikiController extends AuthenticatedController $this->redirect($this->editURL($page)); return; } - if (!$page->isEditable()) { + if ($page->isNew() || !$page->isEditable()) { throw new AccessDeniedException(); } Navigation::activateItem('/course/wiki/start'); @@ -393,7 +455,7 @@ class Course_WikiController extends AuthenticatedController ); $pageData = [ 'page_id' => $page->id, - 'user_id' => $user->id + 'user_id' => $user ? $user->id : null, ]; $online_user = WikiOnlineEditingUser::findOneBySQL( '`page_id` = :page_id AND `user_id` = :user_id', @@ -404,7 +466,7 @@ class Course_WikiController extends AuthenticatedController } $editingUsers = WikiOnlineEditingUser::countBySQL( "`page_id` = ? AND `editing` = 1 AND `user_id` != ?", - [$page->id, $user->id] + [$page->id, $user ? $user->id : null] ); $online_user->editing = $editingUsers === 0 ? 1 : 0; $online_user->chdate = time(); @@ -501,6 +563,7 @@ class Course_WikiController extends AuthenticatedController $this->render_json([ 'error' => 'user_not_requested_edit_mode' ]); + return; } $online_user_me->editing = 0; @@ -524,7 +587,19 @@ class Course_WikiController extends AuthenticatedController } $page->content = \Studip\Markup::markAsHtml(trim(Request::get('content'))); - $page->store(); + $user = User::findCurrent(); + if ($page->isDirty()) { + $page['user_id'] = $user->id; + $page->store(); + } + $pageData = [ + 'page_id' => $page->id, + 'user_id' => $user->id + ]; + WikiOnlineEditingUser::deleteBySQL( + '`page_id` = :page_id AND `user_id` = :user_id', + $pageData + ); PageLayout::postSuccess(_('Die Seite wurde gespeichert.')); $this->redirect($this->pageURL($page)); } @@ -560,7 +635,7 @@ class Course_WikiController extends AuthenticatedController $statement->execute([ 'range_id' => $this->range->id, 'threshold' => $this->last_visit, - 'me' => User::findCurrent()->id + 'me' => User::findCurrent() ? User::findCurrent()->id : null ]); $this->num_entries = $statement->fetch(PDO::FETCH_COLUMN); $this->pagenumber = Request::int('page', 0); @@ -754,6 +829,10 @@ class Course_WikiController extends AuthenticatedController 'type' => 'no', 'mapper' => function () { return $this->range->id; } ], + 'user_id' => [ + 'type' => 'no', + 'mapper' => function () { return User::findCurrent()->id; } + ], 'name' => [ 'required' => true, 'label' => _('Name der Seite'), @@ -808,7 +887,10 @@ class Course_WikiController extends AuthenticatedController '[[ ' . $values['name'] . ' ]]', $page->content ); - $page->store(); + if ($page->isDirty()) { + $page['user_id'] = User::findCurrent()->id; + $page->store(); + } } } } @@ -894,6 +976,30 @@ class Course_WikiController extends AuthenticatedController Sidebar::Get()->addWidget($search); } + public function searchpage_action(WikiPage $page) + { + if (!$page->isReadable()) { + throw new AccessDeniedException(); + } + Navigation::activateItem('/course/wiki/allpages'); + if (!Request::get('search')) { + throw new Exception('No search text.'); + } + $search = str_replace(['\\', '_', '%'], ['\\\\', '\\_', '\\%'], Request::get('search')); + $this->versions = WikiVersion::findBySQL("`page_id` = :page_id AND (`wiki_versions`.`content` LIKE :searchterm OR `wiki_versions`.`name` LIKE :searchterm) ORDER BY `mkdate` DESC ", [ + 'page_id' => $page->id, + 'searchterm' => '%' . $search . '%' + ]); + + $search = new SearchWidget($this->searchURL()); + $search->addNeedle( + _('Im Wiki suchen'), + 'search', + true + ); + Sidebar::Get()->addWidget($search); + } + public function pdf_action(WikiPage $page) { if (!$page->isReadable()) { @@ -1135,4 +1241,41 @@ class Course_WikiController extends AuthenticatedController return $from_end ? mb_strlen($str0) - $length : $length; } + + public function findTextualHits($text, $search, $length = 80) + { + $content = Studip\Markup::removeHtml($text); + $offset = 0; + $output = []; + + // find all occurences + while ($offset < mb_strlen($content)) { + $pos = mb_stripos($content, Request::get('search'), $offset); + if ($pos === false) { + break; + } + $offset = $pos + 1; + + // show max 200 chars + $fragment = ''; + $split_fragment = preg_split( + '/(' . preg_quote(Request::get('search'), '/') . ')/i', + mb_substr($content, max(0, $pos - floor($length / 2)), $length), + -1, + PREG_SPLIT_DELIM_CAPTURE + ); + for ($i = 0; $i < count($split_fragment); ++$i) { + if ($i % 2) { + $fragment .= '<span class="wiki_highlight">'; + $fragment .= htmlready($split_fragment[$i], false); + $fragment .= '</span>'; + } else { + $fragment .= htmlready($split_fragment[$i], false); + } + } + $found_in_fragment = (count($split_fragment) - 1) / 2; // number of hits in fragment + $output[] = '...' . $fragment . '...'; + } + return implode('<br>', $output); + } } diff --git a/app/controllers/course/wizard.php b/app/controllers/course/wizard.php index b45f9e5..c1571ad 100644 --- a/app/controllers/course/wizard.php +++ b/app/controllers/course/wizard.php @@ -25,14 +25,9 @@ class Course_WizardController extends AuthenticatedController public function before_filter (&$action, &$args) { parent::before_filter($action, $args); - global $perm; - if (Request::isXhr()) { - $this->dialog = true; - } - - $sidebar = Sidebar::get(); - $this->studygroup = Request::int('studygroup') ?: $this->flash['studygroup']; + $this->dialog = Request::isXhr(); + $this->studygroup = Request::bool('studygroup', $this->flash['studygroup'] ?? false); if (!$this->studygroup) { PageLayout::setTitle(_('Neue Veranstaltung anlegen')); diff --git a/app/controllers/debugbar.php b/app/controllers/debugbar.php new file mode 100644 index 0000000..59627f2 --- /dev/null +++ b/app/controllers/debugbar.php @@ -0,0 +1,24 @@ +<?php +final class DebugbarController extends Trails_Controller +{ + public function __construct( + Trails\Dispatcher $dispatcher, + private readonly DebugBar\DebugBar $debugbar + ) { + parent::__construct($dispatcher); + } + + public function css_action(): void + { + $this->set_content_type('text/css;charset=utf-8'); + $this->render_nothing(); + $this->debugbar->getJavascriptRenderer()->dumpCssAssets(); + } + + public function js_action(): void + { + $this->set_content_type('text/javascript;charset=utf-8'); + $this->render_nothing(); + $this->debugbar->getJavascriptRenderer()->setIncludeVendors(false)->dumpJsAssets(); + } +} diff --git a/app/controllers/document.php b/app/controllers/document.php index da15ebe..5057d9e 100644 --- a/app/controllers/document.php +++ b/app/controllers/document.php @@ -21,7 +21,7 @@ class DocumentController extends StudipController if ($file_ref) { $this->redirect($file_ref->getDownloadURL($disposition === 'inline' ? 'normal' : 'force')); } else { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } } } diff --git a/app/controllers/evaluation.php b/app/controllers/evaluation.php deleted file mode 100644 index 4a157a9..0000000 --- a/app/controllers/evaluation.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -# Lifter010: TODO -/** - * vote.php - Votecontroller controller - * - * 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. - */ - -class EvaluationController extends AuthenticatedController -{ - public function display_action($range_id) - { - // Bind some params - URLHelper::bindLinkParam('show_expired', $null1); - - // Bind range_id - $this->range_id = $range_id; - - $this->nobody = !$GLOBALS['user']->id || $GLOBALS['user']->id == 'nobody'; - - // Check if we ned administration icons - $this->admin = $range_id == $GLOBALS['user']->id || $GLOBALS['perm']->have_studip_perm('tutor', $range_id); - - // Load evaluations - if (!$this->nobody) { - $eval_db = new EvaluationDB(); - $this->evaluations = StudipEvaluation::findMany($eval_db->getEvaluationIDs($range_id, EVAL_STATE_ACTIVE)); - } else { - $this->evaluations = []; - } - // Check if we got expired - if (Request::get('show_expired')) { - if ($this->admin) { - $this->evaluations = array_merge($this->evaluations, StudipEvaluation::findMany($eval_db->getEvaluationIDs($range_id, EVAL_STATE_STOPPED))); - } - } - if (!empty($this->suppress_empty_output) && count($this->evaluations) === 0) { - $this->render_nothing(); - } else { - $this->visit(); - } - } - - public function visit() - { - if ($GLOBALS['user']->id && $GLOBALS['user']->id != 'nobody' && Request::option('contentbox_open') && in_array(Request::option('contentbox_type'), words('vote eval'))) { - object_set_visit(Request::option('contentbox_open'), Request::option('contentbox_type')); - } - } - - public function visit_action() - { - $this->visit(); - $this->render_nothing(); - } - -} diff --git a/app/controllers/extern.php b/app/controllers/extern.php index 6cde6f3..9296a34 100644 --- a/app/controllers/extern.php +++ b/app/controllers/extern.php @@ -21,7 +21,7 @@ class ExternController extends StudipController * Action shows rendered external page. * * @param string $config_id The id of the configuration of the external page to show. - * @throws Trails_DoubleRenderError + * @throws Trails\Exceptions\DoubleRenderError */ public function index_action(string $config_id) { diff --git a/app/controllers/fachabschluss/kategorien.php b/app/controllers/fachabschluss/kategorien.php index 205f1f1..f9ba041 100644 --- a/app/controllers/fachabschluss/kategorien.php +++ b/app/controllers/fachabschluss/kategorien.php @@ -114,7 +114,7 @@ class Fachabschluss_KategorienController extends MVVController if (Request::submitted('delete')) { CSRFProtection::verifyUnsafeRequest(); if (!MvvPerm::get('AbschlussKategorie')->haveFieldPerm('position')) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } if (!count($abschluss_kategorie->abschluesse)) { PageLayout::postSuccess(sprintf( @@ -142,7 +142,7 @@ class Fachabschluss_KategorienController extends MVVController $orderedIds = Request::getArray('newOrder'); if ($list === 'abschluss_kategorien') { if (!MvvPerm::get('AbschlussKategorie')->haveFieldPerm('position')) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $kategorien = SimpleORMapCollection::createFromArray( AbschlussKategorie::findBySql('1 ORDER BY position') @@ -162,7 +162,7 @@ class Fachabschluss_KategorienController extends MVVController } } else { if (!MvvPerm::get('AbschlussZuord')->haveFieldPerm('position')) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } list(, $kategorie_id) = explode('_', $list); $abschluss_kategorie = AbschlussKategorie::find($kategorie_id); diff --git a/app/controllers/file.php b/app/controllers/file.php index 65344ef..376a74e 100644 --- a/app/controllers/file.php +++ b/app/controllers/file.php @@ -66,7 +66,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $folder = $plugin->getFolder($folder_id); } else { @@ -184,10 +184,10 @@ class FileController extends AuthenticatedController //Plugin file area. $plugin = PluginManager::getInstance()->getPlugin($this->to_plugin); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } if (!($plugin instanceof FilesystemPlugin)) { - throw new Trails_Exception(400, _('Das Plugin ist kein Dateibereich-Plugin.')); + throw new Trails\Exception(400, _('Das Plugin ist kein Dateibereich-Plugin.')); } $file_ids = Request::getArray('file_refs'); foreach ($file_ids as $file_id) { @@ -294,7 +294,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin($this->from_plugin); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $this->file = $plugin->getPreparedFile($file_id); } else { @@ -364,7 +364,7 @@ class FileController extends AuthenticatedController //The file system object is a folder. //Calculate the files and the folder size: - list($this->folder_size, $this->folder_file_amount) = $this->getFolderSize($this->folder); + [$this->folder_size, $this->folder_file_amount] = $this->getFolderSize($this->folder); PageLayout::setTitle($this->folder->name); $this->render_action('folder_details'); } @@ -383,7 +383,7 @@ class FileController extends AuthenticatedController $file_ref_id = $file_id; $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $this->file = $plugin->getPreparedFile($file_id); @@ -666,7 +666,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $this->file = $plugin->getPreparedFile($file_id); $this->from_plugin = Request::get("from_plugin"); @@ -760,7 +760,7 @@ class FileController extends AuthenticatedController $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $this->file_ref = $plugin->getPreparedFile($file_id); @@ -814,7 +814,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $foldertype = $plugin->getFolder($folder_id); @@ -980,7 +980,7 @@ class FileController extends AuthenticatedController ); } else { $this->top_folder = $this->filesystemplugin->getFolder($folder_id, true); - if (is_a($this->top_folder, 'Flexi_Template')) { + if ($this->top_folder instanceof Flexi\Template) { $this->top_folder->select = true; $this->top_folder->to_folder = $this->to_folder; $this->render_text($this->top_folder); @@ -1103,7 +1103,7 @@ class FileController extends AuthenticatedController return; } - $this->library_plugins = $plugin_manager->getPlugins('LibraryPlugin'); + $this->library_plugins = $plugin_manager->getPlugins(LibraryPlugin::class); //Build the query parameter array: $search_parameters = []; @@ -1128,7 +1128,7 @@ class FileController extends AuthenticatedController $this->search_id = md5(json_encode($search_parameters)); - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $merged_results = LibrarySearchManager::search( $search_parameters, @@ -1182,12 +1182,12 @@ class FileController extends AuthenticatedController ); } } elseif (Request::get('search_id')) { - $this->library_plugins = $plugin_manager->getPlugins('LibraryPlugin'); + $this->library_plugins = $plugin_manager->getPlugins(LibraryPlugin::class); $this->search_id = Request::get('search_id'); $this->page = Request::get('page'); - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $cache_data = $cache->read($this->search_id); $results = $cache_data['results']; $this->total_results = count($results); @@ -1247,7 +1247,7 @@ class FileController extends AuthenticatedController } if ($item_id) { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $documents = $cache->read($search_id); $document = $documents['results'][$item_id]; if (!($document instanceof LibraryDocument)) { @@ -1255,7 +1255,7 @@ class FileController extends AuthenticatedController } $file = LibraryFile::createFromLibraryDocument($document, $folder_id); } else { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $search = $cache->read($search_id); if (!$search) { throw new Exception('Search not found in cache!'); @@ -1325,7 +1325,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $filetype = $plugin->getPreparedFile($file_id); $folder = $filetype->getFolderType(); @@ -1335,7 +1335,7 @@ class FileController extends AuthenticatedController $filetype = $file_ref->getFileType(); } if (!$filetype) { - throw new Trails_Exception(404, _('Datei nicht gefunden.')); + throw new Trails\Exception(404, _('Datei nicht gefunden.')); } if (!$filetype->isWritable($GLOBALS['user']->id)) { @@ -1462,7 +1462,7 @@ class FileController extends AuthenticatedController $this->current_folder = $this->to_folder_type; $this->marked_element_ids = []; - $plugins = PluginManager::getInstance()->getPlugins('FileUploadHook'); + $plugins = PluginManager::getInstance()->getPlugins(FileUploadHook::class); $redirects = []; foreach ($plugins as $plugin) { @@ -1505,7 +1505,7 @@ class FileController extends AuthenticatedController $folder_id = substr($folder_id, 0, strpos($folder_id, "?")); } $this->top_folder = $this->filesystemplugin->getFolder($folder_id, true); - if (is_a($this->top_folder, 'Flexi_Template')) { + if ($this->top_folder instanceof Flexi\Template) { $this->top_folder->select = true; $this->top_folder->to_folder = $this->to_folder; $this->render_text($this->top_folder->render()); @@ -1685,7 +1685,7 @@ class FileController extends AuthenticatedController ); } - $plugins = PluginManager::getInstance()->getPlugins('FileUploadHook'); + $plugins = PluginManager::getInstance()->getPlugins(FileUploadHook::class); $redirect = null; foreach ($plugins as $upload_hook_plugin) { $url = $upload_hook_plugin->getAdditionalUploadWizardPage($file_ref); @@ -1792,7 +1792,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("to_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $this->top_folder = $plugin->getFolder($folder_id); } else { @@ -1820,7 +1820,7 @@ class FileController extends AuthenticatedController $payload['html'][] = FilesystemVueDataManager::getFileVueData($this->file, $this->top_folder); - $plugins = PluginManager::getInstance()->getPlugins('FileUploadHook'); + $plugins = PluginManager::getInstance()->getPlugins(FileUploadHook::class); $redirects = []; foreach ($plugins as $plugin) { @@ -1857,7 +1857,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $parent_folder = $plugin->getFolder($folder_id); } else { @@ -1963,7 +1963,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $folder = $plugin->getFolder($folder_id); } else { @@ -2036,7 +2036,7 @@ class FileController extends AuthenticatedController PageLayout::postMessage($result); } } - list($this->folder_size, $this->folder_file_amount) = $this->getFolderSize($folder); + [$this->folder_size, $this->folder_file_amount] = $this->getFolderSize($folder); } public function delete_folder_action($folder_id) @@ -2050,7 +2050,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $folder = $plugin->getFolder($folder_id); } else { @@ -2085,7 +2085,7 @@ class FileController extends AuthenticatedController } $plugin = PluginManager::getInstance()->getPlugin(Request::get("from_plugin")); if (!$plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $parent_folder = $plugin->getFolder($folder_id); } else { diff --git a/app/controllers/files.php b/app/controllers/files.php index 3c77f4e..df4657c 100644 --- a/app/controllers/files.php +++ b/app/controllers/files.php @@ -68,7 +68,7 @@ class FilesController extends AuthenticatedController $this->url_for("files/index"), Icon::create("files", "clickable") ); - foreach (PluginManager::getInstance()->getPlugins('FilesystemPlugin') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(FilesystemPlugin::class) as $plugin) { if ($plugin->isPersonalFileArea()) { $subnav = $plugin->getFileSelectNavigation(); $sources->addLink( @@ -110,7 +110,7 @@ class FilesController extends AuthenticatedController } $config_urls = []; - foreach (PluginManager::getInstance()->getPlugins('FilesystemPlugin') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(FilesystemPlugin::class) as $plugin) { $url = $plugin->filesystemConfigurationURL(); if ($url) { $navigation = $plugin->getFileSelectNavigation(); @@ -647,7 +647,7 @@ class FilesController extends AuthenticatedController PageLayout::setTitle(_('Dateibereich zur Konfiguration auswählen')); $this->configure_urls = []; - foreach (PluginManager::getInstance()->getPlugins('FilesystemPlugin') as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(FilesystemPlugin::class) as $plugin) { $url = $plugin->filesystemConfigurationURL(); if ($url) { $navigation = $plugin->getFileSelectNavigation(); @@ -711,7 +711,7 @@ class FilesController extends AuthenticatedController $destination_plugin = PluginManager::getInstance()->getPlugin($to_plugin); if (!$destination_plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } $destination_folder = $destination_plugin->getFolder($destination_id); } else { @@ -731,7 +731,7 @@ class FilesController extends AuthenticatedController if ($from_plugin) { $source_plugin = PluginManager::getInstance()->getPlugin($from_plugin); if (!$source_plugin) { - throw new Trails_Exception(404, _('Plugin existiert nicht.')); + throw new Trails\Exception(404, _('Plugin existiert nicht.')); } if (Request::get("isfolder")) { if ($source_folder = $source_plugin->getFolder($fileref)) { diff --git a/app/controllers/institute/basicdata.php b/app/controllers/institute/basicdata.php index fb1ce96..9c800fd 100644 --- a/app/controllers/institute/basicdata.php +++ b/app/controllers/institute/basicdata.php @@ -435,7 +435,7 @@ class Institute_BasicdataController extends AuthenticatedController } // delete all contents in forum-modules - foreach (PluginEngine::getPlugins('ForumModule') as $plugin) { + foreach (PluginEngine::getPlugins(ForumModule::class) as $plugin) { $plugin->deleteContents($i_id); // delete content irrespective of plugin-activation in the seminar if ($plugin->isActivated($i_id)) { // only show a message, if the plugin is activated, to not confuse the user $details[] = sprintf(_('Einträge in %s gelöscht.'), $plugin->getPluginName()); diff --git a/app/controllers/institute/overview.php b/app/controllers/institute/overview.php index 66d55e1..3713dcc 100644 --- a/app/controllers/institute/overview.php +++ b/app/controllers/institute/overview.php @@ -138,9 +138,6 @@ class Institute_OverviewController extends AuthenticatedController // Fetch votes if (Config::get()->VOTE_ENABLE) { - $response = $this->relay('evaluation/display/' . $this->institute_id . '/institute'); - $this->evaluations = $response->body; - $response = $this->relay('questionnaire/widget/' . $this->institute_id . '/institute'); $this->questionnaires = $response->body; } diff --git a/app/controllers/jsupdater.php b/app/controllers/jsupdater.php index eb6032d..a5425f9 100644 --- a/app/controllers/jsupdater.php +++ b/app/controllers/jsupdater.php @@ -290,71 +290,77 @@ class JsupdaterController extends AuthenticatedController ] ); $page = WikiPage::find($page_id); - if ($page && $page->isEditable()) { - if ( - $pageInfo['wiki_editor_status']['focussed'] == $page_id - && !empty($pageInfo['wiki_editor_status']['page_content']) - ) { - $page->content = \Studip\Markup::markAsHtml( - $pageInfo['wiki_editor_status']['page_content'] + if ($page) { + if ($page->isEditable()) { + if ( + $pageInfo['wiki_editor_status']['focussed'] == $page_id + && !empty($pageInfo['wiki_editor_status']['page_content']) + ) { + $page->content = \Studip\Markup::markAsHtml( + $pageInfo['wiki_editor_status']['page_content'] + ); + if ($page->isDirty()) { + $page['user_id'] = User::findCurrent()->id; + $page->store(); + } + } + $onlineData = [ + 'user_id' => $user->id, + 'page_id' => $page_id + ]; + $online = WikiOnlineEditingUser::findOneBySQL( + "`user_id` = :user_id AND `page_id` = :page_id", + $onlineData ); - $page->store(); - } - $onlineData = [ - 'user_id' => $user->id, - 'page_id' => $page_id - ]; - $online = WikiOnlineEditingUser::findOneBySQL( - "`user_id` = :user_id AND `page_id` = :page_id", - $onlineData - ); - if (!$online) { - $online = WikiOnlineEditingUser::build($onlineData); - } - $editingUsers = WikiOnlineEditingUser::countBySQL( - "`page_id` = ? AND `editing` = 1 AND `user_id` != ?", - [$page->id, $user->id] - ); - if ($editingUsers > 0) { - $online->editing = 0; - } elseif ($online->editing && $online->editing_request) { - // this is the mode that this user requested the editing mode and was granted to get it: - $online->editing_request = 0; - } elseif ($online->editing_request) { - $other_requests = WikiOnlineEditingUser::countBySql("`page_id` = ? AND `editing_request` = 1 AND `user_id` != ?", [ - $page->id, - $user->id, - ]); - if ($other_requests === 0) { - $online->editing_request = 0; - $online->editing = 1; + if (!$online) { + $online = WikiOnlineEditingUser::build($onlineData); } - } else { - if ($pageInfo['wiki_editor_status']['focussed'] == $page_id) { - $online->editing = 1; - } else { - $other_users = WikiOnlineEditingUser::countBySql("`page_id` = ? AND `user_id` != ?", [ + $editingUsers = WikiOnlineEditingUser::countBySQL( + "`page_id` = ? AND `editing` = 1 AND `user_id` != ?", + [$page->id, $user->id] + ); + if ($editingUsers > 0) { + $online->editing = 0; + } else if ($online->editing && $online->editing_request) { + // this is the mode that this user requested the editing mode and was granted to get it: + $online->editing_request = 0; + } else if ($online->editing_request) { + $other_requests = WikiOnlineEditingUser::countBySql("`page_id` = ? AND `editing_request` = 1 AND `user_id` != ?", [ $page->id, $user->id, ]); - if ($other_users === 0) { - // if I'm the only user I don't need to lose the edit mode + if ($other_requests === 0) { + $online->editing_request = 0; + $online->editing = 1; + } + } else { + if ($pageInfo['wiki_editor_status']['focussed'] == $page_id) { $online->editing = 1; } else { - $online->editing = 0; + $other_users = WikiOnlineEditingUser::countBySql("`page_id` = ? AND `user_id` != ?", [ + $page->id, + $user->id, + ]); + if ($other_users === 0) { + // if I'm the only user I don't need to lose the edit mode + $online->editing = 1; + } else { + $online->editing = 0; + } } } + $online->chdate = time(); + $online->store(); + $data['contents'][$page_id] = wikiReady($page->content, true, $page->range_id, $page_id); + $data['wysiwyg_contents'][$page_id] = $page->content; + $data['pages'][$page_id]['editing'] = $online->editing; + } + else { + $data['pages'][$page_id]['editing'] = 0; } - $online->chdate = time(); - $online->store(); - $data['contents'][$page_id] = wikiReady($page->content, true, $page->range_id, $page_id); - $data['wysiwyg_contents'][$page_id] = $page->content; - $data['pages'][$page_id]['editing'] = $online->editing; - } else { - $data['pages'][$page_id]['editing'] = 0; + $data['pages'][$page_id]['chdate'] = $page->chdate; + $data['users'][$page_id] = $page->getOnlineUsers(); } - $data['pages'][$page_id]['chdate'] = $page->chdate; - $data['users'][$page_id] = $page->getOnlineUsers(); } } return $data; diff --git a/app/controllers/loncapa.php b/app/controllers/loncapa.php index 3119178..dd7ea40 100644 --- a/app/controllers/loncapa.php +++ b/app/controllers/loncapa.php @@ -1,6 +1,4 @@ <?php -require_once 'app/controllers/authenticated_controller.php'; - class LoncapaController extends AuthenticatedController { public function enter_action() @@ -15,8 +13,8 @@ class LoncapaController extends AuthenticatedController if ($GLOBALS['perm']->have_studip_perm('user', $course_id) && isset($GLOBALS['ELEARNING_INTERFACE_MODULES'][$cms_type])) { - require_once 'lib/elearning/ELearningUtils.class.php'; - require_once 'lib/elearning/ObjectConnections.class.php'; + require_once 'lib/elearning/ELearningUtils.php'; + require_once 'lib/elearning/ObjectConnections.php'; $object_connections = new ObjectConnections($course_id); $connected_modules = $object_connections->getConnections(); diff --git a/app/controllers/lvgruppen/lvgruppen.php b/app/controllers/lvgruppen/lvgruppen.php index 5bac0fd..8562836 100644 --- a/app/controllers/lvgruppen/lvgruppen.php +++ b/app/controllers/lvgruppen/lvgruppen.php @@ -149,10 +149,12 @@ class Lvgruppen_LvgruppenController extends MVVController $semester = Semester::find($this->semester_filter); if ($semester && $semester->isCurrent()) { $this->next_sem = Semester::findNext(); - $this->display_semesters[] = $this->next_sem; - $this->courses = array_merge($this->courses, - $this->lvgruppe->getAllAssignedCourses(false, $this->next_sem->id) - ); + if ($this->next_sem) { + $this->display_semesters[] = $this->next_sem; + $this->courses = array_merge($this->courses, + $this->lvgruppe->getAllAssignedCourses(false, $this->next_sem->id) + ); + } } $this->current_sem = $semester; $this->display_semesters[] = $semester; @@ -451,7 +453,7 @@ class Lvgruppen_LvgruppenController extends MVVController ); $widget->class = 'nested-select'; $widget->addElement( - new SelectElement('select-none', _('Alle'), $selected_abschlussh === '') + new SelectElement('select-none', _('Alle'), $selected_abschluss === '') ); $abschluesse = Abschluss::findBySQL(' 1 ORDER BY `name`'); foreach ($abschluesse as $abschluss) { @@ -533,11 +535,12 @@ class Lvgruppen_LvgruppenController extends MVVController private function set_trails_filter($start, $end) { // show only pathes with modules valid in the selected semester - ModuleManagementModelTreeItem::setObjectFilter('Modulteil', + ModuleManagementModelTreeItem::setObjectFilter( + Modulteil::class, function ($mt) use ($start, $end) { - $modul_start = Semester::find($mt->modul->start)->beginn ?: 0; - $modul_end = Semester::find($mt->modul->end)->ende ?: PHP_INT_MAX; - return ($modul_start <= $end && $modul_end >= $start); + $modul_start = Semester::find($mt->modul->start)->beginn ?? 0; + $modul_end = Semester::find($mt->modul->end)->ende ?? PHP_INT_MAX; + return $modul_start <= $end && $modul_end >= $start; } ); } diff --git a/app/controllers/materialien/files.php b/app/controllers/materialien/files.php index 804668e..429a98c 100644 --- a/app/controllers/materialien/files.php +++ b/app/controllers/materialien/files.php @@ -362,29 +362,37 @@ class Materialien_FilesController extends MVVController public function upload_attachment_action() { - if ($GLOBALS['user']->id === "nobody") { + $user = User::findCurrent(); + if (!$user) { throw new AccessDeniedException(); } - $file = $_FILES['file']; - $output = [ - 'name' => $file['name'], - 'size' => $file['size']]; - $mvvfile_id = Request::option('mvvfile_id'); - $output['mvvfile_id'] = $mvvfile_id; - $range_id = Request::option('range_id', $mvvfile_id); - $output['range_id'] = $range_id; - $file_language = Request::option('file_language'); + $document_id = Request::option('document_id'); - $top_folder = $this->getTopFolder($mvvfile_id); - - $user = User::findCurrent(); - - $file = StandardFile::create($_FILES['file']); - $error = $top_folder->validateUpload($file, $GLOBALS['user']->id); - if ($error != null) { - $file->delete(); + $file = $_FILES['file']; + $output = [ + 'name' => $file['name'], + 'size' => $file['size'], + 'mvvfile_id' => $mvvfile_id, + 'range_id' => Request::option('range_id', $mvvfile_id), + ]; + + $top_folder = $this->getTopFolder($output['mvvfile_id']); + + if ($document_id) { + $file = File::find($document_id); + $file->mime_type = $_FILES['file']['type'] ?? get_mime_type($_FILES['file']['name']); + $file->size = $_FILES['file']['size'] ?? filesize($_FILES['file']['tmp_name']); + $file->connectWithDataFile($_FILES['file']['tmp_name']); + } else { + $file = StandardFile::create($_FILES['file']); + } + $error = $top_folder->validateUpload($file, $user->id); + if ($error !== null) { + if (!$document_id) { + $file->delete(); + } $this->response->set_status(400); $this->render_json(compact('error')); return; @@ -399,18 +407,15 @@ class Materialien_FilesController extends MVVController return; } - $mvv_file_fileref = new MvvFileFileref([$mvvfile_id, $file_language]); - $mvv_file_fileref->fileref_id = $file->getId(); + $mvv_file_fileref = new MvvFileFileref([ + $output['mvvfile_id'], + Request::option('file_language'), + ]); + $mvv_file_fileref->fileref_id = $file->id; $mvv_file_fileref->store(); - $output['document_id'] = $file->getId(); - - $output['icon'] = Icon::create( - FileManager::getIconNameForMimeType( - $file->getMimeType() - ), - 'clickable' - )->asImg(['class' => "text-bottom"]); + $output['document_id'] = $file->id; + $output['icon'] = $file->getIcon(Icon::ROLE_CLICKABLE)->asImg(['class' => 'text-bottom']); $this->render_json($output); } @@ -510,7 +515,7 @@ class Materialien_FilesController extends MVVController $mvv_file = MvvFile::find($mvvfile_id); if (!$mvv_file) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } $this->doc_year = $mvv_file->year; diff --git a/app/controllers/media_proxy.php b/app/controllers/media_proxy.php index 50dc3df..b98b49c 100644 --- a/app/controllers/media_proxy.php +++ b/app/controllers/media_proxy.php @@ -46,7 +46,7 @@ class MediaProxyController extends StudipController ini_set('default_socket_timeout', 5); $this->render_nothing(); - //stop output buffering started in Trails_Dispatcher::dispatch() + //stop output buffering started in Trails\Dispatcher::dispatch() while (ob_get_level()) { ob_end_clean(); } diff --git a/app/controllers/module/module.php b/app/controllers/module/module.php index 2f6ba4c..854b045 100644 --- a/app/controllers/module/module.php +++ b/app/controllers/module/module.php @@ -53,7 +53,7 @@ class Module_ModuleController extends MVVController if (count($search_result) > 0) { $module_ids = $search_result; } else { - if ($_SESSION['mvv_filter_module_fach_id']) { + if (!empty($_SESSION['mvv_filter_module_fach_id'])) { $module_ids = $this->findModuleIdsByFach($_SESSION['mvv_filter_module_fach_id']); } if (!empty($_SESSION['mvv_filter_module_abschluss_id'])) { @@ -395,17 +395,17 @@ class Module_ModuleController extends MVVController * Deletes a descriptor from module * * @param type $deskriptor_id - * @throws Trails_Exception + * @throws Trails\Exception */ public function delete_modul_deskriptor_action($deskriptor_id, $language) { $deskriptor = ModulDeskriptor::find($deskriptor_id); if (is_null($deskriptor)) { - throw new Trails_Exception(404, _('Unbekannter Deskriptor')); + throw new Trails\Exception(404, _('Unbekannter Deskriptor')); } $def_lang = $deskriptor->modul->getDefaultLanguage(); if ($language === $def_lang) { - throw new Trails_Exception(403, _('Ein Deskriptor in der Original-Sprache kann nicht gelöscht werden.')); + throw new Trails\Exception(403, _('Ein Deskriptor in der Original-Sprache kann nicht gelöscht werden.')); } if (Request::submitted('delete')) { CSRFProtection::verifyUnsafeRequest(); @@ -819,17 +819,17 @@ class Module_ModuleController extends MVVController * Deletes a descriptor from Modulteil * * @param type $deskriptor_id - * @throws Trails_Exception + * @throws Trails\Exception */ public function delete_modulteil_deskriptor_action($deskriptor_id, $language) { $deskriptor = ModulteilDeskriptor::find($deskriptor_id); if (is_null($deskriptor)) { - throw new Trails_Exception(404, _('Unbekannter Deskriptor')); + throw new Trails\Exception(404, _('Unbekannter Deskriptor')); } $def_lang = $deskriptor->modulteil->getDefaultLanguage(); if ($language === $def_lang) { - throw new Trails_Exception(403, _('Ein Deskriptor in der Original-Sprache kann nicht gelöscht werden.')); + throw new Trails\Exception(403, _('Ein Deskriptor in der Original-Sprache kann nicht gelöscht werden.')); } if (Request::submitted('delete')) { CSRFProtection::verifyUnsafeRequest(); diff --git a/app/controllers/module/mvv_controller.php b/app/controllers/module/mvv_controller.php index e84b37c..b70ddef 100644 --- a/app/controllers/module/mvv_controller.php +++ b/app/controllers/module/mvv_controller.php @@ -77,7 +77,7 @@ abstract class MVVController extends AuthenticatedController PageLayout::setTitle(_('Module')); // Setup flash instance - $this->flash = Trails_Flash::instance(); + $this->flash = Trails\Flash::instance(); $this->me = 'mvv'; self::$items_per_page = Config::get()->getValue('ENTRIES_PER_PAGE'); @@ -218,7 +218,7 @@ abstract class MVVController extends AuthenticatedController * This action is used to show a select box instead of an input field * if the user has clicked on the magnifier icon of a quicksearch. * - * @throws Trails_Exception + * @throws Trails\Exception */ public function qs_result_action() { @@ -228,7 +228,7 @@ abstract class MVVController extends AuthenticatedController Request::get('qs_term') )); } else { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } } diff --git a/app/controllers/my_courses.php b/app/controllers/my_courses.php index 410947b..8205c12 100644 --- a/app/controllers/my_courses.php +++ b/app/controllers/my_courses.php @@ -131,26 +131,16 @@ class MyCoursesController extends AuthenticatedController throw new AccessDeniedException(); } - $this->with_modules = Request::bool('modules'); - - $this->sem_data = Semester::getAllAsArray(); - - $this->group_field = 'sem_number'; - - // Needed parameters for selecting courses - $params = [ - 'group_field' => $this->group_field, + $template = $this->get_template_factory()->open('my_courses/courseexport'); + $template->sem_courses = MyRealmModel::getPreparedCourses('', [ + 'group_field' => 'sem_number', 'order_by' => null, 'order' => 'asc', 'studygroups_enabled' => Config::get()->MY_COURSES_ENABLE_STUDYGROUPS, 'deputies_enabled' => Config::get()->DEPUTIES_ENABLE, - ]; - - $this->sem_courses = MyRealmModel::getPreparedCourses('all', $params); - - $factory = $this->get_template_factory(); - $template = $factory->open('my_courses/courseexport'); - $template->set_attributes($this->get_assigned_variables()); + ]); + $template->sem_data = Semester::getAllAsArray(); + $template->with_modules = Request::bool('modules'); $template->image_style = 'height: 6px; width: 8px;'; $doc = new ExportPDF(); @@ -200,7 +190,7 @@ class MyCoursesController extends AuthenticatedController LEFT JOIN `mvv_lvgruppe` AS ml ON (mls.`lvgruppe_id` = ml.`lvgruppe_id`) LEFT JOIN `mvv_lvgruppe_modulteil` AS mlm on(mls.`lvgruppe_id` = mlm.`lvgruppe_id`) LEFT JOIN `mvv_modulteil` AS mmt ON (mlm.`modulteil_id` = mmt.`modulteil_id`) - LEFT JOIN `mvv_modul` AS mm ON (mmt.`modul_id` = mm.`modul_id`)"; + LEFT JOIN `mvv_modul` AS mm ON (mmt.`modul_id` = mm.`modul_id` AND mm.`stat` = 'genehmigt')"; } @@ -354,7 +344,10 @@ class MyCoursesController extends AuthenticatedController $semesters = MyRealmModel::getSelectedSemesters($sem); $min_sem_key = min($semesters); $max_sem_key = max($semesters); - $courses = MyRealmModel::getCourses($min_sem_key, $max_sem_key, compact('deputies_enabled')); + $courses = MyRealmModel::getCourses($min_sem_key, $max_sem_key, [ + 'deputies_enabled' => $deputies_enabled, + 'exactly' => $semesters, + ]); foreach ($courses as $index => $course) { MyRealmModel::setObjectVisits($course, $GLOBALS['user']->id, $timestamp); } @@ -1178,6 +1171,7 @@ class MyCoursesController extends AuthenticatedController 'future' => _('Aktuelles und nächstes Semester'), 'last' => _('Aktuelles und letztes Semester'), 'lastandnext' => _('Letztes, aktuelles, nächstes Semester'), + 'lastbutone' => _('Aktuelles und vorletztes Semester'), ]; if (Config::get()->MY_COURSES_ENABLE_ALL_SEMESTERS) { diff --git a/app/controllers/new_password.php b/app/controllers/new_password.php index 525d73b..1e58659 100644 --- a/app/controllers/new_password.php +++ b/app/controllers/new_password.php @@ -41,36 +41,32 @@ class NewPasswordController extends StudipController $users = User::findByEmail(Request::get('mail')); - if (sizeof($users) == 1) { - $user = $users[0]; - } else if (sizeof($users) > 1) { - setTempLanguage($users[0]->id); - - // there are mutliple accounts with this mail addresses! - $subject = sprintf( - _("[Stud.IP - %s] Passwortänderung angefordert"), - Config::get()->UNI_NAME_CLEAN - ); - - $mailbody = sprintf( - _("Dies ist eine Informationsmail des Stud.IP-Systems\n" - ."(Studienbegleitender Internetsupport von Präsenzlehre)\n- %s -\n\n" - . "Für die Mail-Adresse %s wurde ein Link angefordert\n" - . "um das Passwort zurückzusetzen.\n" - . "Dieser Mail-Adresse sind jedoch mehrere Zugänge zugeordnet,\n" - . "deshalb ist es nicht möglich, das Passwort hierüber zurückzusetzen.\n" - . "Wenden sie sich bitte stattdessen an\n%s" - ), - Config::get()->UNI_NAME_CLEAN, - $users[0]->email, - $GLOBALS['UNI_CONTACT'] - ); - - StudipMail::sendMessage($users[0]->email, $subject, $mailbody); - - restoreLanguage(); - } - + $user = $users[0]; + setTempLanguage($user->id); + + // there are mutliple accounts with this mail addresses! + $subject = sprintf( + _("[Stud.IP - %s] Passwortänderung angefordert"), + Config::get()->UNI_NAME_CLEAN + ); + + $mailbody = sprintf( + _("Dies ist eine Informationsmail des Stud.IP-Systems\n" + ."(Studienbegleitender Internetsupport von Präsenzlehre)\n- %s -\n\n" + . "Für die Mail-Adresse %s wurde ein Link angefordert\n" + . "um das Passwort zurückzusetzen.\n" + . "Dieser Mail-Adresse sind jedoch mehrere Zugänge zugeordnet,\n" + . "deshalb ist es nicht möglich, das Passwort hierüber zurückzusetzen.\n" + . "Wenden sie sich bitte stattdessen an\n%s" + ), + Config::get()->UNI_NAME_CLEAN, + $users[0]->email, + $GLOBALS['UNI_CONTACT'] + ); + + StudipMail::sendMessage($user->email, $subject, $mailbody); + + restoreLanguage(); if ($user) { // spam/abuse-protection // if there are more than 5 tokens present, do NOT send another mail diff --git a/app/controllers/notifications.php b/app/controllers/notifications.php index b206df1..dc31aab 100644 --- a/app/controllers/notifications.php +++ b/app/controllers/notifications.php @@ -13,9 +13,6 @@ * @since 3.0 */ -require_once 'app/controllers/authenticated_controller.php'; - - class NotificationsController extends AuthenticatedController { /** diff --git a/app/controllers/oer/endpoints.php b/app/controllers/oer/endpoints.php index 417f1e8..1ce9626 100644 --- a/app/controllers/oer/endpoints.php +++ b/app/controllers/oer/endpoints.php @@ -45,7 +45,7 @@ class Oer_EndpointsController extends StudipController $this->render_json([ 'name' => Config::get()->UNI_NAME_CLEAN, 'public_key' => $host['public_key'], - 'url' => $GLOBALS['OER_PREFERRED_URI'] ?: $GLOBALS['ABSOLUTE_URI_STUDIP']."dispatch.php/oer/endpoints/", + 'url' => ($GLOBALS['OER_PREFERRED_URI'] ?? $GLOBALS['ABSOLUTE_URI_STUDIP']) . 'dispatch.php/oer/endpoints/', 'index_server' => $host['index_server'] ]); } @@ -254,7 +254,7 @@ class Oer_EndpointsController extends StudipController 'description' => $material['description'], 'content_type' => $material['content_type'], 'front_image_content_type' => $material['front_image_content_type'], - 'url' => ($GLOBALS['OER_PREFERRED_URI'] ?: $GLOBALS['ABSOLUTE_URI_STUDIP'])."dispatch.php/oer/market/download/".$item_id, + 'url' => ($GLOBALS['OER_PREFERRED_URI'] ?? $GLOBALS['ABSOLUTE_URI_STUDIP']) . 'dispatch.php/oer/market/download/' . $item_id, 'player_url' => $material['player_url'], 'tool' => $material['tool'], 'structure' => ($material['structure'] ? $material['structure']->getArrayCopy() : null), @@ -374,7 +374,7 @@ class Oer_EndpointsController extends StudipController header("Expires: Mon, 12 Dec 2001 08:00:00 GMT"); header("Last-Modified: " . gmdate ("D, d M Y H:i:s") . " GMT"); - if ($_SERVER['HTTPS'] == "on") { + if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') { header("Pragma: public"); header("Cache-Control: private"); } else { @@ -410,7 +410,7 @@ class Oer_EndpointsController extends StudipController $this->response->add_header('Content-Length', filesize($this->material->getFrontImageFilePath())); $this->render_text(file_get_contents($this->material->getFrontImageFilePath())); } else { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } } diff --git a/app/controllers/plugin_controller.php b/app/controllers/plugin_controller.php deleted file mode 100644 index d57a90d..0000000 --- a/app/controllers/plugin_controller.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -/** - * Copyright (c) 2014 Rasmus Fuhse <fuhse@data-quest.de> - * - * 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. - */ - -class PluginController extends StudipController -{ - public function __construct($dispatcher) - { - parent::__construct($dispatcher); - - if (!isset($dispatcher->current_plugin)) { - throw new Exception('Plugin missing for plugin controller!'); - } - $this->plugin = $dispatcher->current_plugin; - - if ($this->plugin && $this->plugin->hasTranslation()) { - // Localization - $this->_ = function ($string) { - return call_user_func_array( - [$this->plugin, '_'], - func_get_args() - ); - }; - - $this->_n = function ($string0, $tring1, $n) { - return call_user_func_array( - [$this->plugin, '_n'], - func_get_args() - ); - }; - } - } - - /** - * Creates the body element id for this controller a given action. - * - * @param string $unconsumed_path Unconsumed path to extract action from - * @return string - */ - protected function getBodyElementIdForControllerAndAction($unconsumed_path) - { - $body_id = implode('-', [ - 'plugin', - strtosnakecase(get_class($this->plugin)), - parent::getBodyElementIdForControllerAndAction($unconsumed_path), - ]); - - return $body_id; - } - - /** - * Intercepts all non-resolvable method calls in order to correctly handle - * calls to _ and _n. - * - * @param string $method - * @param array $arguments - * @return mixed - */ - public function __call($method, $arguments) - { - if (isset($this->_template_variables[$method]) && is_callable($this->_template_variables[$method])) { - return call_user_func_array($this->_template_variables[$method], $arguments); - } - return parent::__call($method, $arguments); - } -} diff --git a/app/controllers/privacy.php b/app/controllers/privacy.php index ba8e6f1..d50b1f1 100644 --- a/app/controllers/privacy.php +++ b/app/controllers/privacy.php @@ -305,7 +305,7 @@ class PrivacyController extends AuthenticatedController $storage->addFileRef($fileref); } - foreach (PluginEngine::getPlugins('PrivacyPlugin') as $plugin) { + foreach (PluginEngine::getPlugins(PrivacyPlugin::class) as $plugin) { $plugin->exportUserData($storage); } diff --git a/app/controllers/profile.php b/app/controllers/profile.php index a4e7c56a..1412c9c 100644 --- a/app/controllers/profile.php +++ b/app/controllers/profile.php @@ -37,22 +37,17 @@ class ProfileController extends AuthenticatedController 'username', $this->user ? $this->user->username : null )); - // get additional informations to selected user - $this->profile = new ProfileModel( - $this->current_user ? $this->current_user->id : null, - $this->user ? $this->user->id : null - ); // set the page title depending on user selection if ( - $this->user + isset($this->user, $this->current_user) && $this->current_user->id === $this->user->id && !$this->current_user->locked ) { PageLayout::setTitle(_('Mein Profil')); UserConfig::get($this->user->id)->store('PROFILE_LAST_VISIT', time()); } elseif ( - $this->current_user->id + !empty($this->current_user->id) && ( $this->perm->have_perm('root') || ( @@ -106,11 +101,11 @@ class ProfileController extends AuthenticatedController // Additional user information $this->public_email = get_visible_email($this->current_user->user_id); - $this->motto = $this->profile->getVisibilityValue('motto'); - $this->private_nr = $this->profile->getVisibilityValue('privatnr', 'private_phone'); - $this->private_cell = $this->profile->getVisibilityValue('privatcell', 'private_cell'); - $this->privadr = $this->profile->getVisibilityValue('privadr', 'privadr'); - $this->homepage = $this->profile->getVisibilityValue('Home', 'homepage'); + $this->motto = $this->getVisibilityValue('motto'); + $this->private_nr = $this->getVisibilityValue('privatnr', 'private_phone'); + $this->private_cell = $this->getVisibilityValue('privatcell', 'private_cell'); + $this->privadr = $this->getVisibilityValue('privadr', 'privadr'); + $this->homepage = $this->getVisibilityValue('Home', 'homepage'); // skype informations $this->skype_name = ''; @@ -119,8 +114,8 @@ class ProfileController extends AuthenticatedController } // get generic datafield entries - $this->shortDatafields = $this->profile->getShortDatafields(); - $this->longDatafields = $this->profile->getLongDatafields(); + $this->shortDatafields = $this->getShortDatafields(); + $this->longDatafields = $this->getLongDatafields(); // get working station of an user (institutes) $this->institutes = $this->getInstitutInformation(); @@ -144,24 +139,20 @@ class ProfileController extends AuthenticatedController } // calendar - $this->dates = ''; - if (Config::get()->CALENDAR_ENABLE) { - if (!in_array($this->current_user->perms, ['admin', 'root'])) { - if (Visibility::verify('termine', $this->current_user->user_id)) { - $start = time(); - $end = strtotime('+1 week 23:59:59'); - - $response = $this->relay('calendar/contentbox/display/' . $this->current_user->user_id . '/' . ($end - $start)); - $this->dates = $response->body; - } - } + if ( + Config::get()->CALENDAR_ENABLE + && !in_array($this->current_user->perms, ['admin', 'root']) + && Visibility::verify('termine', $this->current_user->user_id) + ) { + $start = time(); + $end = strtotime('+1 week 23:59:59'); + + $response = $this->relay('calendar/contentbox/display/' . $this->current_user->user_id . '/' . ($end - $start)); + $this->dates = $response->body; } // include and show votes and tests if (Config::get()->VOTE_ENABLE && Visibility::verify('votes', $this->current_user->user_id)) { - $response = $this->relay('evaluation/display/' . $this->current_user->user_id); - $this->evaluations = $response->body; - $response = $this->relay('questionnaire/widget/' . $this->current_user->user_id . "/user"); $this->questionnaires = $response->body; } @@ -209,11 +200,11 @@ class ProfileController extends AuthenticatedController // Anzeige der Seminare, falls User = dozent if ($this->current_user['perms'] == 'dozent') { - $this->seminare = array_filter($this->profile->getDozentSeminars()); + $this->seminare = array_filter($this->getTeacherSeminars()); } // Hompageplugins - $homepageplugins = PluginEngine::getPlugins('HomepagePlugin'); + $homepageplugins = PluginEngine::getPlugins(HomepagePlugin::class); $render = ''; $layout = $GLOBALS['template_factory']->open('shared/content_box'); @@ -223,7 +214,7 @@ class ProfileController extends AuthenticatedController $template = $homepageplugin->getHomepageTemplate($this->current_user->user_id); // create output of the plugins if (!empty($template)) { - $render .= $template->render(null, $layout); + $render .= $template->render(layout: $layout); } $layout->clear_attributes(); } @@ -237,7 +228,7 @@ class ProfileController extends AuthenticatedController foreach ($category as $cat) { $head = $cat->name; $body = $cat->content; - $vis_text = ""; + $vis_text = ''; if ($this->user->user_id == $this->current_user->user_id) { $vis_text .= ' (' . Visibility::getStateDescription('kat_' . $cat->kategorie_id) . ')'; @@ -524,4 +515,155 @@ class ProfileController extends AuthenticatedController PageLayout::setTitle(sprintf(_('Profil von %s'), $external_user['name'])); $this->user = $external_user; } + + /** + * Collect user datafield informations + * + * @return array + */ + private function getDatafields(): array + { + $short_datafields = []; + $long_datafields = []; + foreach (DataFieldEntry::getDataFieldEntries($this->current_user->user_id, 'user') as $entry) { + if ($entry->isVisible() && $entry->getDisplayValue() + && Visibility::verify($entry->getID(), $this->current_user->user_id)) + { + if ($entry instanceof DataFieldTextareaEntry) { + $long_datafields[] = $entry; + } else { + $short_datafields[] = $entry; + } + } + } + + return [ + 'long' => $long_datafields, + 'short' => $short_datafields, + ]; + } + + /** + * Filter long datafiels from the datafields + * + * @return array + */ + private function getLongDatafields(): array + { + $datafields = $this->getDatafields(); + $array = []; + + if (!empty($datafields)) { + foreach ($datafields['long'] as $entry) { + $array[(string)$entry->getName()] = [ + 'content' => $entry->getDisplayValue(), + 'visible' => '(' . $entry->getPermsDescription() . ')', + ]; + } + } + + return $array; + } + + /** + * Filter short datafiels from the datafields + * + * @return array + */ + private function getShortDatafields(): array + { + $shortDatafields = $this->getDatafields(); + $array = []; + + if (!empty($shortDatafields)) { + foreach ($shortDatafields['short'] as $entry) { + $array[(string)$entry->getName()] = [ + 'content' => $entry->getDisplayValue(), + 'visible' => '(' . $entry->getPermsDescription() . ')', + ]; + } + } + return $array; + } + + /** + * Creates an array with all seminars + * + * @return array + */ + private function getTeacherSeminars(): array + { + $courses = []; + $semester = []; + $next_semester = Semester::findNext(); + $current_semester = Semester::findCurrent(); + $previous_semester = Semester::findPrevious(); + if ($next_semester) { + $semester[$next_semester->id] = $next_semester; + } + if ($current_semester) { + $semester[$current_semester->id] = $current_semester; + } + if ($previous_semester) { + $semester[$previous_semester->id] = $previous_semester; + } + $field = 'name'; + if (Config::get()->IMPORTANT_SEMNUMBER) { + $field = "veranstaltungsnummer,{$field}"; + } + $allcourses = new SimpleCollection( + Course::findBySQL( + "INNER JOIN seminar_user USING(Seminar_id) WHERE user_id=? AND seminar_user.status='dozent' AND seminare.visible=1", + [ + $this->current_user->id + ] + ) + ); + foreach (array_filter($semester) as $one) { + $courses[(string) $one->name] = $allcourses->filter(function ($c) use ($one) { + if (Config::get()->HIDE_STUDYGROUPS_FROM_PROFILE && $c->isStudygroup()) { + return false; + } + if (!$c->isOpenEnded()) { + return $c->isInSemester($one); + } elseif ($one->isCurrent()) { + return $c; + } + return false; + })->orderBy($field); + + if (!$courses[(string) $one->name]->count()) { + unset($courses[(string) $one->name]); + } + } + return $courses; + } + + /** + * Get the homepagevisibilities + * + * @return array + */ + private function getHomepageVisibilities(): array + { + $visibilities = get_local_visibility_by_id( + $this->current_user ? $this->current_user->id : null, + 'homepage' + ); + if (is_array(json_decode($visibilities, true))) { + return json_decode($visibilities, true); + } + return []; + } + + /** + * Returns the visibility value + */ + private function getVisibilityValue(string $param, string $visibility = ''): mixed + { + if (Visibility::verify($visibility ?: $param, $this->current_user->user_id)) { + return $this->current_user->$param; + } + return false; + } } diff --git a/app/controllers/profilemodules.php b/app/controllers/profilemodules.php index 201c4ff..c24d4f4 100644 --- a/app/controllers/profilemodules.php +++ b/app/controllers/profilemodules.php @@ -106,7 +106,7 @@ class ProfileModulesController extends AuthenticatedController $plugins = []; // Get homepage plugins from database. - foreach (PluginEngine::getPlugins('HomepagePlugin') as $plugin) { + foreach (PluginEngine::getPlugins(HomepagePlugin::class) as $plugin) { if ($plugin->isActivatableForContext($this->user)) { $plugins[$plugin->getPluginId()] = $plugin; } diff --git a/app/controllers/questionnaire.php b/app/controllers/questionnaire.php index 66a4e0b..94e4d04 100644 --- a/app/controllers/questionnaire.php +++ b/app/controllers/questionnaire.php @@ -1,7 +1,5 @@ <?php -require_once 'lib/classes/QuestionType.interface.php'; - class QuestionnaireController extends AuthenticatedController { protected $allow_nobody = true; //nobody is allowed @@ -48,11 +46,24 @@ class QuestionnaireController extends AuthenticatedController public function courseoverview_action() { $this->range_id = Context::getId(); + + if (!$this->range_id) { + throw new CheckObjectException(_('Sie haben kein Objekt gewählt.')); + } $this->range_type = Context::getType(); if (!$GLOBALS['perm']->have_studip_perm("tutor", $this->range_id)) { throw new AccessDeniedException("Only for logged in users."); } + Navigation::activateItem("/course/admin/questionnaires"); + if ($GLOBALS['perm']->have_studip_perm('admin', $this->range_id)) { + // Ensure the select widget is added last + NotificationCenter::on('SidebarWillRender', function () { + $widget = new CourseManagementSelectWidget(); + Sidebar::get()->addWidget($widget); + }); + } + $this->statusgruppen = Statusgruppen::findByRange_id($this->range_id); $this->questionnaires = Questionnaire::findBySQL( "INNER JOIN questionnaire_assignments USING (questionnaire_id) WHERE (questionnaire_assignments.range_id = ? AND questionnaire_assignments.range_type = ?) OR (questionnaire_assignments.range_id IN (?) AND questionnaire_assignments.range_type = 'statusgruppe') ORDER BY questionnaires.chdate DESC", @@ -148,7 +159,7 @@ class QuestionnaireController extends AuthenticatedController : null; $this->questionnaire['user_id'] = User::findCurrent()->id; - $questions_data = Request::getArray('questions_data'); + $questions_data = json_decode(Request::get('questions_data'), true); $questions = []; foreach ($questions_data as $index => $question_data) { $class = $question_data['questiontype']; @@ -480,7 +491,7 @@ class QuestionnaireController extends AuthenticatedController $course_assignment['user_id'] = $GLOBALS['user']->id; $course_assignment->store(); } - foreach (PluginManager::getInstance()->getPlugins("QuestionnaireAssignmentPlugin") as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(QuestionnaireAssignmentPlugin::class) as $plugin) { $plugin->storeQuestionnaireAssignments($this->questionnaire); } @@ -584,8 +595,8 @@ class QuestionnaireController extends AuthenticatedController } $this->statusgruppen_ids = []; if (in_array($this->range_type, ["course", "institute"])) { - if ($GLOBALS['perm']->have_studip_perm("tutor", $this->range_id)) { - $statusgruppen = Statusgruppen::findByRange_id(Context::get()->id); + if ($this->range_id && $GLOBALS['perm']->have_studip_perm("tutor", $this->range_id)) { + $statusgruppen = Statusgruppen::findByRange_id($this->range_id); } else { $statusgruppen = Statusgruppen::findBySQL("INNER JOIN statusgruppe_user USING (statusgruppe_id) WHERE statusgruppen.range_id = ? AND statusgruppe_user.user_id = ? ", [ Context::get()->id, @@ -634,6 +645,7 @@ class QuestionnaireController extends AuthenticatedController object_set_visit($questionnaire['questionnaire_id'], 'vote'); } if (in_array($this->range_type, ["course", "institute"]) + && $this->range_id && !$GLOBALS['perm']->have_studip_perm("tutor", $this->range_id) && !($stopped_visible || count($this->questionnaire_data))) { $this->render_nothing(); diff --git a/app/controllers/quicksearch.php b/app/controllers/quicksearch.php index 194f317..c086498 100644 --- a/app/controllers/quicksearch.php +++ b/app/controllers/quicksearch.php @@ -14,7 +14,7 @@ /** * Controller for the ajax-response of the QuickSearch class found in - * lib/classes/QuickSearch.class.php + * lib/classes/QuickSearch.php */ class QuicksearchController extends AuthenticatedController { @@ -94,7 +94,7 @@ class QuicksearchController extends AuthenticatedController if (!empty($result[3])) { $formatted['item_description'] = sprintf('%s (%s)', $result[2], $result[3]); } else { - $formatted['item_description'] = $result[2]; + $formatted['item_description'] = $result[2] ?? ''; } } else if ($this->search instanceof SearchType) { $formatted['item_name'] = $this->search->getAvatarImageTag($result[0], Avatar::SMALL, ['title' => '']) . $formatted['item_name']; diff --git a/app/controllers/quickselection.php b/app/controllers/quickselection.php index 1899e53..a53eb08 100644 --- a/app/controllers/quickselection.php +++ b/app/controllers/quickselection.php @@ -27,7 +27,7 @@ class QuickselectionController extends AuthenticatedController UserConfig::get($GLOBALS['user']->id)->store('QUICK_SELECTION', $names); - $template = PluginEngine::getPlugin('QuickSelection')->getPortalTemplate(); + $template = PluginEngine::getPlugin(QuickSelection::class)->getPortalTemplate(); $this->response->add_header('X-Dialog-Close', 1); $this->response->add_header('X-Dialog-Execute', 'STUDIP.QuickSelection.update'); diff --git a/app/controllers/resources/ajax.php b/app/controllers/resources/ajax.php index 6ff3942..cffd878 100644 --- a/app/controllers/resources/ajax.php +++ b/app/controllers/resources/ajax.php @@ -16,22 +16,22 @@ class Resources_AjaxController extends AuthenticatedController { public function toggle_marked_action($request_id) { - $request = \ResourceRequest::find($request_id); + $request = ResourceRequest::find($request_id); if (!$request) { throw new Exception('Resource request object not found!'); } - $current_user = \User::findCurrent(); + $current_user = User::findCurrent(); if ($request->isReadOnlyForUser($current_user)) { - throw new \AccessDeniedException(); + throw new AccessDeniedException(); } //Switch to the next marking state or return to the unmarked state //if the next marking state would be after the last defined //marking state. - $request->marked = ($request->marked + 1) % \ResourceRequest::MARKING_STATES; + $request->marked = ($request->marked + 1) % ResourceRequest::MARKING_STATES; $request->store(); $this->render_json($request->toArray()); @@ -39,46 +39,46 @@ class Resources_AjaxController extends AuthenticatedController public function get_resource_booking_intervals_action($booking_id) { - $booking = \ResourceBooking::find($booking_id); + $booking = ResourceBooking::find($booking_id); if (!$booking) { throw new Exception('Resource booking object not found!'); } $resource = $booking->resource->getDerivedClassInstance(); - if (!$resource->bookingPlanVisibleForUser(\User::findCurrent())) { - throw new \AccessDeniedException(); + if (!$resource->bookingPlanVisibleForUser(User::findCurrent())) { + throw new AccessDeniedException(); } //Get begin and end: - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); + $begin_str = Request::get('begin'); + $end_str = Request::get('end'); $begin = null; $end = null; if ($begin_str && $end_str) { //Try the ISO format first: YYYY-MM-DDTHH:MM:SS±ZZ:ZZ - $begin = \DateTime::createFromFormat(\DateTime::RFC3339, $begin_str); - $end = \DateTime::createFromFormat(\DateTime::RFC3339, $end_str); - if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { - $tz = new \DateTime(); + $begin = DateTime::createFromFormat(DateTime::RFC3339, $begin_str); + $end = DateTime::createFromFormat(DateTime::RFC3339, $end_str); + if (!($begin instanceof DateTime) || !($end instanceof DateTime)) { + $tz = new DateTime(); $tz = $tz->getTimezone(); //Try the ISO format without timezone: - $begin = \DateTime::createFromFormat('Y-m-d\TH:i:s', $begin_str, $tz); - $end = \DateTime::createFromFormat('Y-m-d\TH:i:s', $end_str, $tz); + $begin = DateTime::createFromFormat('Y-m-d\TH:i:s', $begin_str, $tz); + $end = DateTime::createFromFormat('Y-m-d\TH:i:s', $end_str, $tz); } } $sql = "booking_id = :booking_id "; $sql_data = ['booking_id' => $booking->id]; - if ($begin instanceof \DateTime && $end instanceof \DateTime) { + if ($begin instanceof DateTime && $end instanceof DateTime) { $sql .= "AND begin >= :begin AND end <= :end "; $sql_data['begin'] = $begin->getTimestamp(); $sql_data['end'] = $end->getTimestamp(); } - if (\Request::submitted('exclude_cancelled_intervals')) { + if (Request::submitted('exclude_cancelled_intervals')) { $sql .= "AND takes_place = '1' "; } $sql .= "ORDER BY begin ASC, end ASC"; - $intervals = \ResourceBookingInterval::findBySql($sql, $sql_data); + $intervals = ResourceBookingInterval::findBySql($sql, $sql_data); $result = []; foreach ($intervals as $interval) { @@ -90,7 +90,7 @@ class Resources_AjaxController extends AuthenticatedController public function toggle_takes_place_field_action($interval_id) { - $interval = \ResourceBookingInterval::find($interval_id); + $interval = ResourceBookingInterval::find($interval_id); if (!$interval) { throw new Exception('ResourceBookingInterval object not found!'); } @@ -103,13 +103,13 @@ class Resources_AjaxController extends AuthenticatedController $resource = $resource->getDerivedClassInstance(); - if (!$resource->userHasPermission(\User::findCurrent(), 'autor', [$interval->begin, $interval->end])) { + if (!$resource->userHasPermission(User::findCurrent(), 'autor', [$interval->begin, $interval->end])) { throw new Exception('You do not have sufficient permissions to modify the interval!'); } if ( !$interval->takes_place - && $resource->isAssigned(new \DateTime('@' . $interval->begin), new \DateTime('@' . $interval->end)) + && $resource->isAssigned(new DateTime('@' . $interval->begin), new DateTime('@' . $interval->end)) ) { throw new Exception('Already booked'); } @@ -121,13 +121,14 @@ class Resources_AjaxController extends AuthenticatedController 'takes_place' => $interval->takes_place ]); } else { - throw new Exception('Error while storing the interval!'); + $this->set_status(500); + $this->render_text('Error while storing the interval!'); } } public function get_semester_booking_plan_action($resource_id) { - $resource = \Resource::find($resource_id); + $resource = Resource::find($resource_id); if (!$resource) { throw new Exception('Resource object not found!'); } @@ -143,8 +144,8 @@ class Resources_AjaxController extends AuthenticatedController $display_requests = Request::get('display_requests'); $display_all_requests = Request::get('display_all_requests'); - $begin = new \DateTime(); - $end = new \DateTime(); + $begin = new DateTime(); + $end = new DateTime(); $semester_id = Request::get('semester_id'); @@ -190,11 +191,11 @@ class Resources_AjaxController extends AuthenticatedController 'resource_id' => $resource->id ]; if (!$display_all_requests) { - $requests_sql .= "AND user_id = :user_id "; + $requests_sql .= " AND user_id = :user_id "; $requests_sql_params['user_id'] = $current_user->id; } - $requests = \ResourceRequest::findBySql( + $requests = ResourceRequest::findBySql( $requests_sql, $requests_sql_params ); @@ -207,7 +208,7 @@ class Resources_AjaxController extends AuthenticatedController $booking->resource = $resource; $irrelevant_booking = $booking->getRepetitionType() !== 'weekly' && ( - !\Request::get('display_single_bookings') + !Request::get('display_single_bookings') || $booking->end < strtotime('today') ); if ($booking->getAssignedUserType() === 'course' && in_array($booking->assigned_course_date->metadate_id, $meta_dates)) { @@ -261,7 +262,7 @@ class Resources_AjaxController extends AuthenticatedController $relevant_request = false; foreach ($requests as $request) { - if ($request->cycle instanceof \SeminarCycleDate) { + if ($request->cycle instanceof SeminarCycleDate) { $cycle_dates = $request->cycle->getAllDates(); foreach ($cycle_dates as $cycle_date) { $relevant_request = $semester->beginn <= $cycle_date->date @@ -488,7 +489,7 @@ class Resources_AjaxController extends AuthenticatedController $clipboard = Clipboard::find($clipboard_id); if (!empty($_SESSION['selected_clipboard_id'])) { - $clipboard = \Clipboard::find($_SESSION['selected_clipboard_id']); + $clipboard = Clipboard::find($_SESSION['selected_clipboard_id']); } if (!$clipboard) { throw new Exception('Clipboard object not found!'); @@ -497,7 +498,7 @@ class Resources_AjaxController extends AuthenticatedController //Permission check: if ($clipboard->user_id !== $current_user->id) { - throw new \AccessDeniedException(); + throw new AccessDeniedException(); } $display_requests = Request::bool('display_requests'); @@ -656,4 +657,186 @@ class Resources_AjaxController extends AuthenticatedController $this->render_json($data); } + + public function move_booking_action($booking_id): void + { + $booking = ResourceBooking::find($booking_id); + if (!$booking) { + $this->notFound('Resource booking object not found!'); + return; + } + + $current_user = User::findCurrent(); + + if ($booking->isReadOnlyForUser($current_user)) { + throw new AccessDeniedException(); + } + + $resource_id = Request::get('resource_id'); + $interval_id = Request::get('interval_id'); + + $begin = $this->convertDatetime(Request::get('begin')); + $end = $this->convertDatetime(Request::get('end')); + + //Check if a specific interval has been moved: + if ($interval_id) { + $interval = ResourceBookingInterval::findOneBySql( + 'interval_id = ? AND booking_id = ?', + [$interval_id, $booking->id] + ); + if (!$interval) { + $this->notFound('Resource booking interval not found!'); + return; + } + $interval_begin = new DateTime(); + $interval_begin->setTimestamp($interval->begin); + $interval_end = new DateTime(); + $interval_end->setTimestamp($interval->end); + + //Calculate the difference from the interval time range + //to the time range from the request. That difference + //is then applied to the booking. + $begin_diff = $interval_begin->diff($begin); + $end_diff = $interval_end->diff($end); + + $new_booking_begin = new DateTime(); + $new_booking_begin->setTimestamp($booking->begin); + $new_booking_end = new DateTime(); + $new_booking_end->setTimestamp($booking->end); + + $new_booking_begin = $new_booking_begin->add($begin_diff); + $new_booking_end = $new_booking_end->add($end_diff); + //We must substract the preparation time to the begin timestamp + //to get the real begin: + $real_begin = clone $new_booking_begin; + if ($booking->preparation_time > 0) { + $real_begin->sub(new DateInterval('PT' . ($booking->preparation_time / 60 ) . 'M')); + } + $booking->begin = $real_begin->getTimestamp(); + $booking->end = $new_booking_end->getTimestamp(); + } else { + //We must substract the preparation time to the begin timestamp + //to get the real begin: + $real_begin = clone $begin; + if ($booking->preparation_time > 0) { + $real_begin->sub(new DateInterval('PT' . ($booking->preparation_time / 60 ) . 'M')); + } + $booking->begin = $real_begin->getTimestamp(); + $booking->end = $end->getTimestamp(); + } + if ($resource_id) { + //The resource-ID has changed: + //The booking was moved from one resource to another. + $booking->resource_id = $resource_id; + } + + //Update the booking_user_id field: + $booking->booking_user_id = User::findCurrent()->id; + + try { + $booking->store(); + + if (Request::bool('quiet')) { + $this->render_nothing(); + } else { + $this->render_json($booking->toRawArray()); + } + } catch (Exception $e) { + $this->set_status(500); + $this->render_text($e->getMessage()); + } + } + + public function move_request_action($request_id): void + { + $request = ResourceRequest::find($request_id); + if (!$request) { + $this->notFound('Resource request object not found!'); + return; + } + + $current_user = User::findCurrent(); + + if ($request->isReadOnlyForUser($current_user)) { + throw new AccessDeniedException(); + } + + $request->begin = $this->convertDatetime(Request::get('begin')); + $request->end = $this->convertDatetime(Request::get('end')); + + try { + $request->store(); + $this->renderObject($request); + } catch (\Exception $e) { + $this->set_status(500); + $this->render_text($e->getMessage()); + } + } + + public function semester_week_action($timestamp) + { + $semester = \Semester::findByTimestamp($timestamp); + if (!$semester) { + $this->notFound('No semester found for given timestamp'); + throw new RecordNotFoundException(); + } + + $timestamp = strtotime('today', $timestamp); + $week_begin_timestamp = strtotime('monday this week', $semester->vorles_beginn); + $end_date = $semester->vorles_ende; + + $i = 0; + $result = [ + 'semester_name' => (string)$semester->name, + 'week_number' => sprintf(_('KW %u'), date('W', $timestamp)), + 'current_day' => strftime('%x', $timestamp) + ]; + while ($week_begin_timestamp < $end_date) { + $next_week_timestamp = strtotime('+1 week', $week_begin_timestamp); + if ($week_begin_timestamp <= $timestamp && $timestamp < $next_week_timestamp) { + $result['sem_week'] = sprintf( + _('%u. Vorlesungswoche (ab %s)'), + $i + 1, + strftime('%x', $week_begin_timestamp)); + break; + } + $i += 1; + + $week_begin_timestamp = $next_week_timestamp; + } + + $this->render_json($result); + } + + private function notFound(string $message = ''): void + { + $this->set_status(404); + $this->render_text($message); + } + + private function renderObject(SimpleORMap $object): void + { + if (Request::bool('quiet')) { + $this->render_nothing(); + } else{ + $this->render_json($object->toArray()); + } + } + + /** + * Tries the ISO format first: YYYY-MM-DDTHH:MM:SS±ZZ:ZZ + */ + private function convertDatetime(?string $input): ?Datetime + { + if (!$input) { + return null; + } + + return DateTime::createFromFormat(DateTime::RFC3339, $input) + ?? DateTime::createFromFormat( + 'Y-m-d\TH:i:s', + $input, + (new DateTime())->getTimezone() + ); + } } diff --git a/app/controllers/resources/booking.php b/app/controllers/resources/booking.php index 294aeaa..a4722ed 100644 --- a/app/controllers/resources/booking.php +++ b/app/controllers/resources/booking.php @@ -242,9 +242,7 @@ class Resources_BookingController extends AuthenticatedController return true; } - $template_factory = new Flexi_TemplateFactory( - $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' - ); + $template_factory = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); $derived_resource = $booking->resource->getDerivedClassInstance(); $system_lang = $_SESSION['_language']; @@ -1354,7 +1352,7 @@ class Resources_BookingController extends AuthenticatedController $resource, $time_intervals, [1, 3], - ($this->booking->id ? [$this->booking->id] : []) + isset($this->booking->id) ? [$this->booking->id] : [] ); $reservations_to_overwrite = array_merge( $reservations_to_overwrite, diff --git a/app/controllers/resources/room_request.php b/app/controllers/resources/room_request.php index 6421f23..4c51beb 100644 --- a/app/controllers/resources/room_request.php +++ b/app/controllers/resources/room_request.php @@ -1739,8 +1739,8 @@ class Resources_RoomRequestController extends AuthenticatedController if ($save_only) { // redirect to reload all infos and showing the most current ones $this->redirect('resources/room_request/resolve/' . $request_id); - } elseif (Request::isDialog() && Context::get()) { - $this->response->add_header('X-Dialog-Execute', '{"func": "STUDIP.AdminCourses.App.loadCourse", "payload": "'.Context::get()->id.'"}'); + } elseif (Request::isDialog() && Context::get()->id) { + $this->response->add_header('X-Dialog-Execute', '{"func": "STUDIP.AdminCourses.App.loadCourse", "payload": "' . Context::get()->id . '"}'); } } diff --git a/app/controllers/room_management/planning.php b/app/controllers/room_management/planning.php index c450c64..3da7904 100644 --- a/app/controllers/room_management/planning.php +++ b/app/controllers/room_management/planning.php @@ -38,7 +38,7 @@ class RoomManagement_PlanningController extends AuthenticatedController if ($selected_clipboard_id) { $_SESSION['selected_clipboard_id'] = $selected_clipboard_id; } else { - $selected_clipboard_id = $_SESSION['selected_clipboard_id']; + $selected_clipboard_id = $_SESSION['selected_clipboard_id'] ?? null; } $this->display_all_requests = Request::get('display_all_requests'); @@ -282,7 +282,7 @@ class RoomManagement_PlanningController extends AuthenticatedController if ($selected_clipboard_id) { $_SESSION['selected_clipboard_id'] = $selected_clipboard_id; } else { - $selected_clipboard_id = $_SESSION['selected_clipboard_id']; + $selected_clipboard_id = $_SESSION['selected_clipboard_id'] ?? null; } $this->display_all_requests = Request::get('display_all_requests'); @@ -411,7 +411,7 @@ class RoomManagement_PlanningController extends AuthenticatedController } //Check if a clipboard is selected: - $selected_clipboard_id = $_SESSION['selected_clipboard_id']; + $selected_clipboard_id = $_SESSION['selected_clipboard_id'] ?? null; $rooms = []; if ($selected_clipboard_id) { $clipboard = Clipboard::find($selected_clipboard_id); @@ -1075,7 +1075,7 @@ class RoomManagement_PlanningController extends AuthenticatedController if ($selected_clipboard_id) { $_SESSION['selected_clipboard_id'] = $selected_clipboard_id; } else { - $selected_clipboard_id = $_SESSION['selected_clipboard_id']; + $selected_clipboard_id = $_SESSION['selected_clipboard_id'] ?? null; } //Get the selected date or use the current date, if none specified: @@ -1371,7 +1371,7 @@ class RoomManagement_PlanningController extends AuthenticatedController $export = Request::get('export'); if ($export == 'html') { //Load the export template: - $factory = new Flexi_TemplateFactory( + $factory = new Flexi\Factory( $GLOBALS['STUDIP_BASE_PATH'] . '/app/views/room_management/planning/' ); diff --git a/app/controllers/search/courses.php b/app/controllers/search/courses.php index 6ae9d6a..a6a4d27 100644 --- a/app/controllers/search/courses.php +++ b/app/controllers/search/courses.php @@ -40,6 +40,7 @@ class Search_CoursesController extends AuthenticatedController public function index_action() { $nodeClass = ''; + $title = _('Vorlesungsverzeichnis'); if (Request::option('type', 'semtree') === 'semtree') { Navigation::activateItem('/search/courses/semtree'); $nodeClass = StudipStudyArea::class; @@ -52,22 +53,12 @@ class Search_CoursesController extends AuthenticatedController $this->treeTitle = _('Einrichtungen'); $this->breadcrumbIcon = 'institute'; $this->editUrl = $this->url_for('rangetree/edit'); + $title = _('Einrichtungsverzeichnis'); } $this->startId = Request::option('node_id', $nodeClass . '_root'); $this->setupSidebar(); - } - - public function export_results_action() - { - $sem_browse_obj = new SemBrowse(); - $tmpfile = basename($sem_browse_obj->create_result_xls()); - if ($tmpfile) { - $this->redirect(FileManager::getDownloadURLForTemporaryFile( - $tmpfile, _('ErgebnisVeranstaltungssuche.xls'), 4)); - } else { - $this->render_nothing(); - } + PageLayout::setTitle($title); } private function setupSidebar() @@ -110,17 +101,7 @@ class Search_CoursesController extends AuthenticatedController } $sidebar->addWidget(new VueWidget('search-widget')); + $sidebar->addWidget(new VueWidget('views-widget')); $sidebar->addWidget(new VueWidget('export-widget')); - - $views = new ViewsWidget(); - $views->addLink( - _('Als Liste'), - $this->url_for('search/courses', array_merge($params, ['show_as' => 'list'])) - )->setActive($this->show_as === 'list'); - $views->addLink( - _('Als Tabelle'), - $this->url_for('search/courses', array_merge($params, ['show_as' => 'table'])) - )->setActive($this->show_as === 'table'); - $sidebar->addWidget($views); } } diff --git a/app/controllers/search/globalsearch.php b/app/controllers/search/globalsearch.php index ed4ed6e..05fcda2 100644 --- a/app/controllers/search/globalsearch.php +++ b/app/controllers/search/globalsearch.php @@ -72,37 +72,40 @@ class Search_GlobalsearchController extends AuthenticatedController } } - $semester_filter = $sidebar->addWidget(new OptionsWidget(_('Semester'))); - $semester_filter->id = 'semester_filter'; - $semester_filter->addSelect( - _('Semester'), - null, - 'semester', - $this->getSemesters(), - 'future', - ['id' => 'semester_select'] + $filter_widget = $sidebar->addWidget(new OptionsWidget(_('Filter'))); + $filter_widget->id = 'filter_widget'; + + $filter_widget->addElement( + new SelectListElement( + _('Semester'), + 'semester', + $this->getSemesters(), + 'future', + ['id' => 'semester_select'] + ), + 'semester_filter' ); - $seminar_type_filter = $sidebar->addWidget(new OptionsWidget(_('Veranstaltungstypen'))); - $seminar_type_filter->id = 'seminar_type_filter'; - $seminar_type_filter->addSelect( - _('Typ der Veranstaltung'), - null, - 'seminar_type', - $this->getSemClasses(), - '', - ['id' => 'seminar_type_select'] + $filter_widget->addElement( + new SelectListElement( + _('Typ der Veranstaltung'), + 'seminar_type', + $this->getSemClasses(), + '', + ['id' => 'seminar_type_select'] + ), + 'seminar_type_filter' ); - $institute_filter = $sidebar->addWidget(new OptionsWidget(_('Einrichtungen'))); - $institute_filter->id = 'institute_filter'; - $institute_filter->addSelect( - _('Einrichtung'), - null, - 'institute', - $this->getInstitutes(), - '', - ['id' => 'institute_select'] + $filter_widget->addElement( + new SelectListElement( + _('Einrichtung'), + 'institute', + $this->getInstitutes(), + '', + ['id' => 'institute_select'] + ), + 'institute_filter' ); } diff --git a/app/controllers/search/studiengaenge.php b/app/controllers/search/studiengaenge.php index a075ce9..e17eb2a 100644 --- a/app/controllers/search/studiengaenge.php +++ b/app/controllers/search/studiengaenge.php @@ -228,7 +228,7 @@ class Search_StudiengaengeController extends MVVController } else { $this->active_sem = Semester::find($this->sessGet('selected_semester', Semester::findCurrent()->id)); } - $this->active_sem = $this->semesters[$this->active_sem->id] ? $this->active_sem : null; + $this->active_sem = !empty($this->semesters[$this->active_sem->id]) ? $this->active_sem : null; if (!$this->active_sem && count($this->semesters)) { $active_sem = reset($this->semesters); $this->active_sem = Semester::find($active_sem['semester_id']); @@ -423,7 +423,7 @@ class Search_StudiengaengeController extends MVVController { $this->abschnitt = StgteilAbschnitt::find($abschnitt_id); if (!$this->abschnitt) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } $this->render_template('search/studiengaenge/kommentar', $this->layout); } diff --git a/app/controllers/settings/general.php b/app/controllers/settings/general.php index 734cca1..0e8ec70 100644 --- a/app/controllers/settings/general.php +++ b/app/controllers/settings/general.php @@ -44,6 +44,7 @@ class Settings_GeneralController extends Settings_SettingsController public function index_action() { $this->user_language = getUserLanguage($this->user->id); + $this->notifications_placement = User::findCurrent()->getConfiguration()->SYSTEM_NOTIFICATIONS_PLACEMENT; } /** @@ -80,6 +81,7 @@ class Settings_GeneralController extends Settings_SettingsController } else { PersonalNotifications::deactivateAudioFeedback($this->user->id); } + $this->config->store('SYSTEM_NOTIFICATIONS_PLACEMENT', Request::get('system_notifications_placement')); PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.')); $this->redirect('settings/general'); diff --git a/app/controllers/settings/settings.php b/app/controllers/settings/settings.php index 0346c24..9cd00c0 100644 --- a/app/controllers/settings/settings.php +++ b/app/controllers/settings/settings.php @@ -46,11 +46,11 @@ abstract class Settings_SettingsController extends AuthenticatedController $exception = new AccessDeniedException(_('Sie dürfen dieses Profil nicht bearbeiten')); $exception->setDetails([ _("Wahrscheinlich ist Ihre Session abgelaufen. Bitte " - ."nutzen Sie in diesem Fall den untenstehenden Link, " + ."nutzen Sie in diesem Fall den folgenden Link, " ."um zurück zur Anmeldung zu gelangen.\n\n" ."Eine andere Ursache kann der Versuch des Zugriffs " ."auf Userdaten, die Sie nicht bearbeiten dürfen, sein. " - ."Nutzen Sie den untenstehenden Link, um zurück auf " + ."Nutzen Sie den folgenden Link, um zurück auf " ."die Startseite zu gelangen."), ]); throw $exception; @@ -121,7 +121,7 @@ abstract class Settings_SettingsController extends AuthenticatedController public function get_default_template($action) { $class = get_class($this); - $controller_name = Trails_Inflector::underscore(mb_substr($class, 0, -10)); + $controller_name = Trails\Inflector::underscore(mb_substr($class, 0, -10)); return file_exists($this->dispatcher->trails_root . '/views/' . $controller_name . '.php') ? $controller_name : $controller_name . '/' . $action; diff --git a/app/controllers/shared/contacts.php b/app/controllers/shared/contacts.php index 98670f4..820d2ab 100644 --- a/app/controllers/shared/contacts.php +++ b/app/controllers/shared/contacts.php @@ -114,7 +114,7 @@ class Shared_ContactsController extends MVVController if ($this->contact_id) { $contact_range = MvvContactRange::findOneBySQL('contact_id=?', [$this->contact_id]); if (!$contact_range) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } $this->relations = $contact_range->getRelations($this->filter); $this->origin = 'index'; @@ -155,7 +155,7 @@ class Shared_ContactsController extends MVVController { $this->contact_range = MvvContactRange::findOneBySQL('contact_id = ?', [$contact_id]); if (!$this->contact_range) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } $this->relations = $this->contact_range->getRelations($this->filter); @@ -725,7 +725,7 @@ class Shared_ContactsController extends MVVController $this->mvvcontact_id = $user_id; $this->selected_sem_end = $this->filter['end_sem.ende']; - $this->selected_inst = $this->filter['mvv_studiengang.institut_id']; + $this->selected_inst = $this->filter['mvv_studiengang.institut_id'] ?? null; if (Request::submitted('store')) { $selected = Request::getArray('ranges'); @@ -871,7 +871,7 @@ class Shared_ContactsController extends MVVController )); $filter = [ 'mvv_modul.stat' => $stat, - 'mvv_modul_inst.institut_id' => $this->filter['mvv_modul_inst.institut_id'], + 'mvv_modul_inst.institut_id' => $this->filter['mvv_modul_inst.institut_id'] ?? '', 'start_sem.beginn' => $this->filter['start_sem.beginn'], 'end_sem.ende' => $this->filter['end_sem.ende'] ]; diff --git a/app/controllers/shared/download.php b/app/controllers/shared/download.php index 92d15d9..f94bc86 100644 --- a/app/controllers/shared/download.php +++ b/app/controllers/shared/download.php @@ -158,7 +158,7 @@ class Shared_DownloadController extends AuthenticatedController } $path = $GLOBALS['STUDIP_BASE_PATH'] . '/app/views/shared/modul/'; - $factory = new Flexi_TemplateFactory($path); + $factory = new Flexi\Factory($path); $template = $factory->open('_modul'); $template->_ = function ($string) { return $this->_($string); }; @@ -166,7 +166,7 @@ class Shared_DownloadController extends AuthenticatedController $template->display_language = $display_language; $content = $template->render(); - $factory = new \Flexi_TemplateFactory($path); + $factory = new Flexi\Factory($path); $type = 1; if (count($modul->modulteile) == 1) { $modulteil = $modul->modulteile->first(); diff --git a/app/controllers/shared/log_event.php b/app/controllers/shared/log_event.php index 0355e45..3d8af7e 100644 --- a/app/controllers/shared/log_event.php +++ b/app/controllers/shared/log_event.php @@ -122,12 +122,12 @@ class Shared_LogEventController extends MVVController $search_action .= " AND `log_actions`.`name` LIKE CONCAT('%_'," . DBManager::get()->quote($log_action) . ")"; } - $statement = DBManager::get()->prepare("SELECT *, `log_actions`.`name` + $statement = DBManager::get()->prepare("SELECT * FROM `log_events` LEFT JOIN `log_actions` ON `log_events`.`action_id` = `log_actions`.`action_id` WHERE `info` = ? " . $search_action . " - ORDER BY `log_events`.`mkdate` DESC"); + ORDER BY `log_events`.`event_id` DESC"); $statement->execute([$mvv_field]); $res = $statement->fetchOne(); if ($res) { diff --git a/app/controllers/siteinfo.php b/app/controllers/siteinfo.php index ab81660..7bc95cb 100644 --- a/app/controllers/siteinfo.php +++ b/app/controllers/siteinfo.php @@ -37,7 +37,7 @@ class SiteinfoController extends StudipController } else { $action = 'show'; if ($this->page_is_draft || ($this->page_disabled_nobody && $GLOBALS['user']->id === 'nobody')) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } } $this->add_navigation($action); diff --git a/app/controllers/start.php b/app/controllers/start.php index 53a87dc..8231095 100644 --- a/app/controllers/start.php +++ b/app/controllers/start.php @@ -53,10 +53,13 @@ class StartController extends AuthenticatedController } } + $this->widget_layout = $this->get_template_factory()->open('start/_widget.php'); + $sidebar = Sidebar::get(); $nav = $sidebar->addWidget(new NavigationWidget()); $nav->setTitle(_('Sprungmarken')); + $nav->setId('navigation-layer-3'); foreach (array_merge(...$this->columns) as $widget) { $nav->addLink( $widget->getPluginName(), @@ -132,7 +135,7 @@ class StartController extends AuthenticatedController */ private function getAvailableWidgets($user_id) { - $all_widgets = PluginEngine::getPlugins('PortalPlugin'); + $all_widgets = PluginEngine::getPlugins(PortalPlugin::class); $user_widgets = WidgetUser::getWidgets($user_id); $used_widgets = array_merge(...$user_widgets); $available = []; @@ -190,7 +193,7 @@ class StartController extends AuthenticatedController PageLayout::setTitle(sprintf(_('Standard-Startseite für "%s" bearbeiten'), ucfirst($permission))); - $this->widgets = PluginEngine::getPlugins('PortalPlugin'); + $this->widgets = PluginEngine::getPlugins(PortalPlugin::class); $this->initial_widgets = WidgetDefault::getWidgets($permission); $this->permission = $permission; } diff --git a/app/controllers/studiengaenge/abschluesse.php b/app/controllers/studiengaenge/abschluesse.php index 8ec055b..60bc091 100644 --- a/app/controllers/studiengaenge/abschluesse.php +++ b/app/controllers/studiengaenge/abschluesse.php @@ -50,7 +50,7 @@ class Studiengaenge_AbschluesseController extends Studiengaenge_StudiengaengeCon $perm_institutes = MvvPerm::getOwnInstitutes(); $abschluss = Abschluss::find($abschluss_id); if (!$abschluss) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } $this->abschluss_id = $abschluss->id; if (count($perm_institutes)) { @@ -59,7 +59,7 @@ class Studiengaenge_AbschluesseController extends Studiengaenge_StudiengaengeCon $perm_institutes ); if (!count($institutes_abschluss)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $this->studiengaenge = SimpleORMapCollection::createFromArray( Studiengang::findByAbschluss_id($this->abschluss_id) @@ -91,4 +91,4 @@ class Studiengaenge_AbschluesseController extends Studiengaenge_StudiengaengeCon $this->perform_relayed('index'); } } -}
\ No newline at end of file +} diff --git a/app/controllers/studiengaenge/fachbereiche.php b/app/controllers/studiengaenge/fachbereiche.php index 970c8d9..35f997b 100644 --- a/app/controllers/studiengaenge/fachbereiche.php +++ b/app/controllers/studiengaenge/fachbereiche.php @@ -51,7 +51,7 @@ class Studiengaenge_FachbereicheController extends Studiengaenge_StudiengaengeCo $this->fachbereich_id = $fachbereich_id; if (count($perm_institutes)) { if (!in_array($this->fachbereich_id, $perm_institutes)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } } diff --git a/app/controllers/studiengaenge/fachbereichestgteile.php b/app/controllers/studiengaenge/fachbereichestgteile.php index c8037dc..73ad2aa 100644 --- a/app/controllers/studiengaenge/fachbereichestgteile.php +++ b/app/controllers/studiengaenge/fachbereichestgteile.php @@ -67,7 +67,7 @@ class Studiengaenge_FachbereichestgteileController extends Studiengaenge_Studien $this->fachbereich = $fachbereich; $this->perform_relayed('stgteil'); } else { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } } } diff --git a/app/controllers/studiengaenge/faecher.php b/app/controllers/studiengaenge/faecher.php index c9546a3..9990f7d 100644 --- a/app/controllers/studiengaenge/faecher.php +++ b/app/controllers/studiengaenge/faecher.php @@ -83,7 +83,7 @@ class Studiengaenge_FaecherController extends Studiengaenge_StudiengangteileCont $this->stgteil = StudiengangTeil::get(); $this->stgteil->assignFach($fach->getId()); } else { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } $this->perform_relayed('stgteil'); } diff --git a/app/controllers/studiengaenge/kategorien.php b/app/controllers/studiengaenge/kategorien.php index 03d28fc..8f6a98d 100644 --- a/app/controllers/studiengaenge/kategorien.php +++ b/app/controllers/studiengaenge/kategorien.php @@ -73,7 +73,7 @@ class Studiengaenge_KategorienController extends Studiengaenge_StudiengaengeCont if (count($perm_institutes)) { if (!in_array($studiengang->institut_id, $perm_institutes)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } } @@ -98,4 +98,4 @@ class Studiengaenge_KategorienController extends Studiengaenge_StudiengaengeCont $this->perform_relayed('index'); } } -}
\ No newline at end of file +} diff --git a/app/controllers/studiengaenge/shared_version.php b/app/controllers/studiengaenge/shared_version.php index 696fac0..a6265b0 100644 --- a/app/controllers/studiengaenge/shared_version.php +++ b/app/controllers/studiengaenge/shared_version.php @@ -11,18 +11,18 @@ abstract class SharedVersionController extends MVVController { $this->stgteil = StudiengangTeil::find($stgteil_id); if (!$this->stgteil) { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } if (!MvvPerm::haveFieldPermVersionen($this->stgteil, MvvPerm::PERM_READ)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } if (!isset($this->version)) { $this->version = StgteilVersion::find($version_id); if (!$this->version) { if (!MvvPerm::haveFieldPermVersionen($this->stgteil, MvvPerm::PERM_CREATE)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $this->version = new StgteilVersion(); } @@ -51,7 +51,7 @@ abstract class SharedVersionController extends MVVController if (Request::submitted('store')) { CSRFProtection::verifyUnsafeRequest(); if (!MvvPerm::haveFieldPermVersionen($this->stgteil)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $stored = false; $this->version->stgteil_id = $this->stgteil->getId(); @@ -220,7 +220,7 @@ abstract class SharedVersionController extends MVVController { $version = StgteilVersion::find($version_id); if (!$version) { - throw new Trails_Exception(404, _('Unbekannte Version')); + throw new Trails\Exception(404, _('Unbekannte Version')); } if (Request::isPost()) { CSRFProtection::verifyUnsafeRequest(); @@ -267,16 +267,16 @@ abstract class SharedVersionController extends MVVController $perm = MvvPerm::get($this->version); if (!$perm->haveFieldPerm('abschnitte', MvvPerm::PERM_READ)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } if ($this->abschnitt->isNew() && !$perm->haveFieldPerm('abschnitte', MvvPerm::PERM_CREATE)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } if (Request::submitted('store')) { CSRFProtection::verifyUnsafeRequest(); if (!$perm->haveFieldPerm('abschnitte', MvvPerm::PERM_WRITE)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $this->abschnitt->version_id = $this->version->getId(); $this->abschnitt->name = Request::i18n('name')->trim(); @@ -494,7 +494,7 @@ abstract class SharedVersionController extends MVVController $abschnitt = StgteilAbschnitt::find($abschnitt_id); if ($abschnitt) { if (!MvvPerm::haveFieldPermModul_zuordnungen($abschnitt, MvvPerm::PERM_CREATE)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $modul = Modul::find($modul_id); if (!$modul) { @@ -614,7 +614,7 @@ abstract class SharedVersionController extends MVVController { $version = StgteilVersion::find($version_id); if (!$version) { - throw new Trails_Exception(404, _('Unbekannte Version')); + throw new Trails\Exception(404, _('Unbekannte Version')); } else { if (Request::isPost()) { CSRFProtection::verifyUnsafeRequest(); @@ -754,7 +754,7 @@ abstract class SharedVersionController extends MVVController $this->redirect($this->action_url('abschnitte/' . $version_id)); } } else { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } } if (Request::isXhr()) { diff --git a/app/controllers/studiengaenge/studiengaenge.php b/app/controllers/studiengaenge/studiengaenge.php index d053f48..8465665 100644 --- a/app/controllers/studiengaenge/studiengaenge.php +++ b/app/controllers/studiengaenge/studiengaenge.php @@ -541,7 +541,7 @@ class Studiengaenge_StudiengaengeController extends MVVController if (Request::isPost()) { CSRFProtection::verifyRequest(); if (!MvvPerm::haveFieldPermStudiengangteile($studiengang, MvvPerm::PERM_CREATE)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $stgteil_name = $this->stg_stgteil->stgteil_name; $stgbez_name = $this->stg_stgteil->stgbez_name; @@ -588,7 +588,7 @@ class Studiengaenge_StudiengaengeController extends MVVController if (Request::isPost()) { CSRFProtection::verifyUnsafeRequest(); if (!MvvPerm::haveFieldPermStudiengangteile($studiengang, MvvPerm::PERM_CREATE)) { - throw new Trails_Exception(403); + throw new Trails\Exception(403); } $stgteil_name = $stg_stgteil->stgteil_name; $stgbez_name = $stg_stgteil->stgbez_name; diff --git a/app/controllers/studiengaenge/studiengangteile.php b/app/controllers/studiengaenge/studiengangteile.php index 0fddd44..bc1449f 100644 --- a/app/controllers/studiengaenge/studiengangteile.php +++ b/app/controllers/studiengaenge/studiengangteile.php @@ -144,7 +144,7 @@ class Studiengaenge_StudiengangteileController extends SharedVersionController $this->stgteil->contact_assignments = $stgteil_orig->contact_assignments; } else { - throw new Trails_Exception(404); + throw new Trails\Exception(404); } $this->perform_relayed('stgteil'); } diff --git a/app/controllers/studiengaenge/versionen.php b/app/controllers/studiengaenge/versionen.php index 9b60b3d..41625bf 100644 --- a/app/controllers/studiengaenge/versionen.php +++ b/app/controllers/studiengaenge/versionen.php @@ -52,7 +52,7 @@ class Studiengaenge_VersionenController extends SharedVersionController $this->redirect($this->action_url('index/' . $this->chooser_filter['stgteile'])); return; default : - throw new Trails_Exception(400); + throw new Trails\Exception(400); } $this->name = $list; if (!empty($this->lists[$list]['elements'])) { @@ -218,7 +218,7 @@ class Studiengaenge_VersionenController extends SharedVersionController if ($stgteil_id) { $this->stgteil = StudiengangTeil::find($stgteil_id); if (!$this->stgteil) { - throw new Trails_Exception(404, _('Unbekannter Studiengangteil')); + throw new Trails\Exception(404, _('Unbekannter Studiengangteil')); } $this->initPageParams(); diff --git a/app/controllers/studip_controller.php b/app/controllers/studip_controller.php deleted file mode 100644 index 9e238a1..0000000 --- a/app/controllers/studip_controller.php +++ /dev/null @@ -1,875 +0,0 @@ -<?php -/* - * studip_controller.php - studip controller base class - * Copyright (c) 2009 Elmar Ludwig - * - * 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. - */ - -use PhpOffice\PhpSpreadsheet\Spreadsheet; -use PhpOffice\PhpSpreadsheet\Writer\Csv; -use PhpOffice\PhpSpreadsheet\Writer\Xlsx; - -require_once 'studip_controller_properties_trait.php'; -require_once 'studip_response.php'; - -/** - * @property StudipResponse $response - */ -abstract class StudipController extends Trails_Controller -{ - use StudipControllerPropertiesTrait; - - protected $with_session = false; //do we need to have a session for this controller - protected $allow_nobody = true; //should 'nobody' allowed for this controller or redirected to login? - protected $_autobind = false; - - /** - * @return false|void - */ - public function before_filter(&$action, &$args) - { - $this->current_action = $action; - // allow only "word" characters in arguments - $this->validate_args($args); - - parent::before_filter($action, $args); - - if ($this->with_session) { - # open session - page_open([ - 'sess' => 'Seminar_Session', - 'auth' => $this->allow_nobody ? 'Seminar_Default_Auth' : 'Seminar_Auth', - 'perm' => 'Seminar_Perm', - 'user' => 'Seminar_User' - ]); - - // show login-screen, if authentication is "nobody" - $GLOBALS['auth']->login_if((Request::get('again') || !$this->allow_nobody) && $GLOBALS['user']->id == 'nobody'); - - // Setup flash instance - $this->flash = Trails_Flash::instance(); - - // set up user session - include 'lib/seminar_open.php'; - } - - // Set generic attribute that indicates whether the request was sent - // via ajax or not - $this->via_ajax = Request::isXhr(); - - # Set base layout - # - # If your controller needs another layout, overwrite your controller's - # before filter: - # - # class YourController extends AuthenticatedController { - # function before_filter(&$action, &$args) { - # parent::before_filter($action, $args); - # $this->set_layout("your_layout"); - # } - # } - # - # or unset layout by sending: - # - # $this->set_layout(NULL) - # - $layout_file = Request::isXhr() - ? 'layouts/dialog.php' - : 'layouts/base.php'; - $layout = $GLOBALS['template_factory']->open($layout_file); - $this->set_layout($layout); - - $this->set_content_type('text/html;charset=utf-8'); - } - - /** - * Extended method to inject extended response object. - */ - public function erase_response() - { - parent::erase_response(); - - $this->response = new StudipResponse(); - } - - /** - * Hooked perform method in order to inject body element id creation. - * - * In order to avoid clashes, these body element id will be joined - * with a minus sign. Otherwise the controller "x" with action - * "y_z" would be given the same id as the controller "x/y" with - * the action "z", namely "x_y_z". With the minus sign this will - * result in the ids "x-y_z" and "x_y-z". - * - * Plugins will always have a leading 'plugin-' and the decamelized - * plugin name in front of the id. - * - * @param String $unconsumed_path Path segment containing action and - * optionally arguments or format - * @return Trails_Response from parent controller - */ - public function perform($unconsumed_path) - { - // Set body element id if it has not already been set - if (!PageLayout::hasBodyElementId()) { - $body_id = $this->getBodyElementIdForControllerAndAction($unconsumed_path); - PageLayout::setBodyElementId($body_id); - } - - return parent::perform($unconsumed_path); - } - - /** - * Callback function being called after an action is executed. - * - * @param string Name of the action to perform. - * @param array An array of arguments to the action. - * - * @return void - */ - public function after_filter($action, $args) - { - parent::after_filter($action, $args); - - if (Request::isXhr() && !isset($this->response->headers['X-Title']) && PageLayout::hasTitle()) { - $this->response->add_header('X-Title', rawurlencode(PageLayout::getTitle())); - } - if (Request::isXhr() && !isset($this->response->headers['X-WikiLink']) && PageLayout::getHelpKeyword()) { - $this->response->add_header('X-WikiLink', format_help_url(PageLayout::getHelpKeyword())); - } - - if ($this->with_session) { - page_close(); - } - } - - /** - * Validate arguments based on a list of given types. The types are: - * 'int', 'float', 'option' and 'string'. If the list of types is NULL - * or shorter than the argument list, 'option' is assumed for all - * remaining arguments. 'option' differs from Request::option() in - * that it also accepts the charaters '-' and ',' in addition to all - * word characters. - * - * Since Stud.IP 4.0 it is also possible to directly inject - * SimpleORMap objects. If types is NULL, the signature of the called - * action is analyzed and any type hint that matches a sorm class - * will be used to create an object using the argument as the id - * that is passed to the object's constructor. - * - * If $_autobind is set to true, the created object is also assigned - * to the controller so that it is available in a view. - * - * @param array $args an array of arguments to the action - * @param array $types list of argument types (optional) - */ - public function validate_args(&$args, $types = null) - { - $class_infos = []; - - if ($types === null) { - $types = []; - } - - if ($this->has_action($this->current_action)) { - $reflection = new ReflectionMethod($this, $this->current_action . '_action'); - $parameters = $reflection->getParameters(); - foreach ($parameters as $i => $parameter) { - $class_type = $parameter->getType(); - - if ( - !$class_type - || !class_exists($class_type->getName()) - || !is_a($class_type->getName(), SimpleORMap::class, true) - ) { - continue; - } - - $types[$i] = 'sorm'; - $class_infos[$i] = [ - 'model' => $class_type->getName(), - 'var' => $parameter->getName(), - 'optional' => $parameter->isOptional(), - ]; - - if ($parameter->isOptional() && !isset($args[$i])) { - $args[$i] = $parameter->getDefaultValue(); - } - } - } - - foreach ($args as $i => &$arg) { - $type = $types[$i] ?? 'option'; - switch ($type) { - case 'int': - $arg = (int) $arg; - break; - - case 'float': - $arg = (float) strtr($arg, ',', '.'); - break; - - case 'option': - if (preg_match('/[^\\w,-]/', $arg)) { - throw new Trails_Exception(400); - } - break; - - case 'sorm': - $info = $class_infos[$i]; - - $id = null; - if ($arg != -1) { - $id = $arg; - } - if (mb_strpos($id, SimpleORMap::ID_SEPARATOR) !== false) { - $id = explode(SimpleORMap::ID_SEPARATOR, $id); - } - - $reflection = new ReflectionClass($info['model']); - - $sorm = $reflection->newInstance($id); - if (!$info['optional'] && $sorm->isNew()) { - throw new Trails_Exception( - 404, - "Parameter {$info['var']} could not be resolved with value {$arg}" - ); - } - - $arg = $sorm; - if ($this->_autobind) { - $this->{$info['var']} = $arg; - } - break; - - case 'string': - break; - - default: - throw new Trails_Exception(500, 'Unknown type "' . $type . '"'); - } - } - - reset($args); - } - - /** - * Returns a URL to a specified route to your Trails application. - * without first parameter the current action is used - * if route begins with a / then the current controller ist prepended - * if second parameter is an array it is passed to URLHeper - * - * @param string a string containing a controller and optionally an action - * @param string[] optional arguments - * - * @return string a URL to this route - */ - public function url_for($to = ''/* , ... */) - { - $args = func_get_args(); - - // Try to create route if none given - if ($to === '') { - $args[0] = isset($this->parent_controller) - ? $this->parent_controller->current_action - : $this->current_action; - return $this->action_url(...$args); - } - - // Create url for a specific action - // TODO: This seems odd. You kinda specify an absolute path - // to receive a relative url. Meh... - // - // @deprecated Do not use this, please! - if ($to[0] === '/') { - $args[0] = substr($to, 1); - return $this->action_url(...$args); - } - - // Check for absolute URL - if ($this->isURL($to)) { - throw new InvalidArgumentException(__METHOD__ . ' cannot be used with absolute URLs'); - } - - // Extract fragment (if any) - if (strpos($to, '#') !== false) { - list($args[0], $fragment) = explode('#', $to); - } - - // Extract parameters (if any) - $params = []; - if (is_array(end($args))) { - $params = array_pop($args); - } - - // Map any sorm objects to their ids - $args = array_map(function ($arg) { - if (is_object($arg) && $arg instanceof SimpleORMap) { - return $arg->isNew() ? -1 : $arg->id; - } - return $arg; - }, $args); - - $url = parent::url_for(...$args); - - if (isset($fragment)) { - $url .= '#' . $fragment; - } - return URLHelper::getURL($url, $params); - } - - /** - * Returns an escaped URL to a specified route to your Trails application. - * without first parameter the current action is used - * if route begins with a / then the current controller ist prepended - * if second parameter is an array it is passed to URLHeper - * - * @param string a string containing a controller and optionally an action - * @param strings optional arguments - * - * @return string a URL to this route - */ - public function link_for($to = ''/* , ... */) - { - return htmlReady($this->url_for(...func_get_args())); - } - - /** - * Redirects the user another page. Accepts multiple parameters just like - * url_for(). - * - * @param string $to - * @see StudipController::url_for() - */ - public function redirect($to) - { - $to = $this->adjustToArguments(...func_get_args()); - - parent::redirect($to); - } - - /** - * Relocate the user to another location. This is a specialized version - * of redirect that differs in two points: - * - * - relocate() will force the browser to leave the current dialog while - * redirect would refresh the dialog's contents - * - relocate() accepts all the parameters that url_for() accepts so it's - * no longer neccessary to chain url_for() and redirect() - * - * @param String $to Location to redirect to - */ - public function relocate($to) - { - $to = $this->adjustToArguments(...func_get_args()); - - if (Request::isDialog()) { - $this->response->add_header('X-Location', encodeURI($to)); - $this->render_nothing(); - } else { - parent::redirect($to); - } - } - - /** - * Returns a URL to a specified route to your Trails application, unless - * the parameter is already a valid URL (which is returned unchanged). - * - * If no absolute url or more than one argument is given, url_for() is - * used. - */ - private function adjustToArguments(...$args): string - { - if (count($args) > 1 && $this->isURL($args[0])) { - throw new InvalidArgumentException('Method may not be used with a URL and multiple parameters'); - } - - if (count($args) === 1 && $this->isURL($args[0])) { - return $args[0]; - } - - return $this->url_for(...$args); - } - - /** - * Returns whether the given parameter is a valid url. - * - * @param string $to - * @return bool - */ - private function isURL(string $to): bool - { - return preg_match('#^(/|\w+://)#', $to); - } - - /** - * Exception handler called when the performance of an action raises an - * exception. - * - * @param object the thrown exception - */ - public function rescue($exception) - { - throw $exception; - } - - /** - * render given data as json, data is converted to utf-8 - * - * @param mixed $data - */ - public function render_json($data) - { - $json = json_encode($data); - - $this->set_content_type('application/json;charset=utf-8'); - $this->response->add_header('Content-Length', strlen($json)); - $this->render_text($json); - } - - /** - * Render given data as csv, data is assumed to be utf-8. - * The first row of data may contain column titles. - * - * @param array $data data as two dimensional array - * @param string $filename download file name (optional) - * @param string $delimiter field delimiter char (optional) - * @param string $enclosure field enclosure char (optional) - */ - public function render_csv($data, $filename = null, $delimiter = ';', $enclosure = '"') - { - $this->set_content_type('text/csv; charset=UTF-8'); - - $output = fopen('php://temp', 'rw'); - fputs($output, "\xEF\xBB\xBF"); - - foreach ($data as $row) { - fputcsv($output, $row, $delimiter, $enclosure); - } - - rewind($output); - $csv_data = stream_get_contents($output); - fclose($output); - - if (isset($filename)) { - $this->response->add_header('Content-Disposition', 'attachment; ' . encode_header_parameter('filename', $filename)); - } - - $this->response->add_header('Content-Length', strlen($csv_data)); - - $this->render_text($csv_data); - } - - /** - * Renders a pdf file given by a TCPDF/ExportPDF object. - * - * @param TCPDF $pdf TCPDF object to render - * @param string $filename Filename - * @param bool $inline Should the pdf be displayed inline (default: no) - */ - protected function render_pdf(TCPDF $pdf, $filename, $inline = false) - { - $temp_file = $GLOBALS['TMP_PATH'] . '/' . md5(uniqid('pdf-file', true)); - $pdf->Output($temp_file, 'F'); - - $disposition = $inline ? 'inline' : 'attachment'; - - $this->render_temporary_file($temp_file, $filename, 'application/pdf', $disposition); - } - - /** - * Renders a file - * @param string $file Path of the file to render - * @param string $filename Name of the file displayed to user - * (will equal $file when missing) - * @param string $content_type Optional content type (will be determined if missing) - * @param string $content_disposition Either attachment (default) or inline - * @param Closure $callback Optional callback when download has finished - * @param int $chunk_size Optional size of chunks to send (default: 256k) - */ - public function render_file( - $file, - $filename = null, - $content_type = null, - $content_disposition = 'attachment', - Closure $callback = null, - $chunk_size = 262144 - ) { - if (!file_exists($file)) { - throw new Trails_Exception(404); - } - - if (!is_readable($file)) { - throw new Trails_Exception(500); - } - - if ($content_type === null) { - $content_type = get_mime_type($filename ?: $file); - } - - if (!in_array($content_type, get_mime_types())) { - $content_type = 'application/octet-stream'; - } - - if ($content_type === 'application/octet-stream') { - $content_disposition = 'attachment'; - } - - $this->set_content_type($content_type); - $this->response->add_header( - 'Content-Disposition', - "{$content_disposition}; " . encode_header_parameter( - 'filename', - FileManager::cleanFileName($filename ?: basename($file)) - ) - ); - $this->response->add_header('Content-Length', filesize($file)); - $this->response->add_header('Content-Transfer-Encoding', 'binary'); - $this->response->add_header('Pragma', 'public'); - $this->render_text(function () use ($file, $chunk_size, $callback) { - $fp = fopen($file, 'rb'); - - while (!feof($fp)) { - yield fgets($fp, $chunk_size); - } - - fclose($fp); - - if ($callback) { - $callback($file); - } - }); - } - - /** - * Renders a temporary file which will be deleted after transmission. - * This is just a convenience method so you don't have to write the delete - * callback. - * - * @param string $file Path of the file to render - * @param string $filename Name of the file displayed to user - * (will equal $file when missing) - * @param string $content_type Optional content type (will be determined if missing) - * @param string $content_disposition Either attachment (default) or inline - * @param Closure $callback Optional callback when download has finished - * @param int $chunk_size Optional size of chunks to send (default: 256k) - */ - public function render_temporary_file( - $file, - $filename = null, - $content_type = null, - $content_disposition = 'attachment', - Closure $callback = null, - $chunk_size = 262144 - - ) { - $delete_callback = function ($file) use ($callback) { - unlink($file); - - if ($callback) { - $callback($file); - } - }; - - $this->render_file( - $file, - $filename, - $content_type, - $content_disposition, - $delete_callback, - $chunk_size - ); - } - - public function render_form(\Studip\Forms\Form $form) - { - $this->render_text($form->render()); - } - - /** - * relays current request to another controller and returns the response - * the other controller is given all assigned properties, additional parameters are passed - * through - * - * @param string $to_uri a trails route - * @return Trails_Response - */ - public function relay($to_uri/* , ... */) - { - $args = func_get_args(); - $uri = array_shift($args); - [$controller_path, $unconsumed] = '' === $uri ? $this->dispatcher->default_route() : $this->dispatcher->parse($uri); - - $controller = $this->dispatcher->load_controller($controller_path); - $assigns = $this->get_assigned_variables(); - unset($assigns['controller']); - foreach ($assigns as $k => $v) { - $controller->$k = $v; - } - $controller->layout = null; - $controller->parent_controller = $this; - array_unshift($args, $unconsumed); - return $controller->perform_relayed(...$args); - } - - /** - * Relays current request and performs redirect if neccessary. - * - * @param string $to_uri a trails route - * @return Trails_Response - * - * @see StudipController::relay() - */ - public function relayWithRedirect(...$args): Trails_Response - { - $response = $this->relay(...$args); - - // If the relayed action should perform a redirect, do so - if (isset($response->headers['Location'])) { - header("Location: {$response->headers['Location']}"); - page_close(); - die; - } - - return $response; - } - - /** - * perform a given action/parameter string from an relayed request - * before_filter and after_filter methods are not called - * - * @see perform - * @param string $unconsumed - * @return Trails_Response - */ - public function perform_relayed($unconsumed/* , ... */) - { - $args = func_get_args(); - $unconsumed = array_shift($args); - - [$action, $extracted_args, $format] = $this->extract_action_and_args($unconsumed); - $this->format = isset($format) ? $format : 'html'; - $this->current_action = $action; - $args = array_merge($extracted_args, $args); - $callable = $this->map_action($action); - - if (is_callable($callable)) { - $callable(...$args); - } else { - $this->does_not_understand($action, $args); - } - - if (!$this->performed) { - $this->render_action($action); - } - return $this->response; - } - - /** - * Renders a given template and returns the resulting string. - * - * @param string $template Name of the template file - * @param mixed $layout Optional layout - * @return string - */ - public function render_template_as_string($template, $layout = null) - { - $template = $this->get_template_factory()->open($template); - $template->set_layout($layout); - $template->set_attributes($this->get_assigned_variables()); - return $template->render(); - } - - /** - * Magic methods that intercepts all unknown method calls. - * If a method is called that matches an action on this controller, - * an url to that action is generated. - * - * Basically, this: - * - * <code>$controller->url_for('foo/bar/baz/' . $param)</code> - * - * is equal to calling this on the Foo_BarController: - * - * <code>$controller->baz($param)</code> - * - * @param String $method Called method name - * @param array $argumetns Provided arguments - * @return url to the requested action - * @throws Trails_UnknownAction if no action matches the method - */ - public function __call($method, $arguments) - { - $function = 'action_link'; - if (mb_strpos($method, 'Link') === mb_strlen($method) - 4) { - $method = mb_substr($method, 0, -4); - } elseif (mb_strpos($method, 'URL') === mb_strlen($method) - 3) { - $function = 'action_url'; - $method = mb_substr($method, 0, -3); - } - - if (!$this->has_action($method)) { - throw new Trails_UnknownAction("Unknown action '{$method}'"); - } - - array_unshift($arguments, $method); - return call_user_func_array([$this, $function], $arguments); - } - - /** - * Returns whether this controller has the specificed action. - * - * @param string $action Name of the action - * @return true if action is defined, false otherwise - */ - public function has_action($action) - { - return method_exists($this, $action . '_action') - || ($this->parent_controller - && $this->parent_controller->has_action($action)); - } - - /** - * Generates the url for an action on this controller without the - * neccessity to provide the full "path" to the action (since it - * is implicitely known). - * - * Basically, this: - * - * <code>$controller->url_for('foo/bar/baz/' . $param)</code> - * - * is equal to calling this on the Foo_BarController: - * - * <code>$controller->action_url('baz/' . $param)</code> - * - * @param string $action Name of the action - * @return string url to the requested action - */ - public function action_url($action) - { - $arguments = func_get_args(); - $arguments[0] = $this->controller_path() . '/' . $arguments[0]; - - return $this->url_for(...$arguments); - } - - /** - * Generates the link for an action on this controller without the - * neccessity to provide the full "path" to the action (since it - * is implicitely known). - * - * Basically, this: - * - * <code>$controller->link_for('foo/bar/baz/' . $param)</code> - * - * is equal to calling this on the Foo_BarController: - * - * <code>$controller->action_link('baz/' . $param)</code> - * - * @param string $action Name of the action - * @return string to the requested action - */ - public function action_link($action) - { - return htmlReady($this->action_url(...func_get_args())); - } - - /** - * Returns the url path to this controller. - * - * @return string url path to this controller - */ - protected function controller_path() - { - $class = get_class($this->parent_controller ?? $this); - $controller = mb_substr($class, 0, -mb_strlen('Controller')); - $controller = strtosnakecase($controller); - return preg_replace('/_{2,}/', '/', $controller); - } - - - /** - * Validate the datetime according to specific format. - * - * @param string $datetime the datetime which should be validate - * @param string $format the format that the datetime should have by default H:i for time - * - * @return bool result of validation - */ - public function validate_datetime($datetime, $format = 'H:i') - { - $dt = DateTime::createFromFormat($format, $datetime); - return $dt && $dt->format($format) == date('H:i',strtotime($datetime)); - } - - /** - * Export xlsx and csv files via PhpSpreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - */ - public function render_spreadsheet( - array $header, - array $data, - string $format, - string $filename, - ?string $filepath = null - ): void { - $render_to_browser = false; - if ($filepath == null) { - $render_to_browser = true; - $filepath = tempnam($GLOBALS['TMP_PATH'], 'spreadsheet'); - } - $spreadsheet = new Spreadsheet(); - $activeWorksheet = $spreadsheet->getActiveSheet(); - $activeWorksheet->fromArray($header); - $activeWorksheet->fromArray($data, null, 'A2'); - - if ($format === 'xlsx') { - $writer = new Xlsx($spreadsheet); - } elseif ($format === 'csv') { - $writer = new Csv($spreadsheet); - } else { - throw new Exception("Format {$format} is not supported"); - } - - $writer->save($filepath); - - if ($render_to_browser) { - $this->response->add_header('Cache-Control', 'cache, must-revalidate'); - $this->render_temporary_file( - $filepath, - $filename, - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - ); - } - } - - /** - * Creates the body element id for this controller a given action. - * - * @param string $unconsumed_path Unconsumed path to extract action from - * @return string - */ - protected function getBodyElementIdForControllerAndAction($unconsumed_path) - { - // Extract action from unconsumed path segment - [$action] = $this->extract_action_and_args($unconsumed_path); - - // Extract controller name from class name - $controller = preg_replace('/Controller$/', '', get_class($this)); - $controller = Trails_Inflector::underscore($controller); - - // Build main parts of the body element id - $body_id_parts = explode('/', $controller); - $body_id_parts[] = $action; - - // Create and set body element id - $body_id = implode('-', $body_id_parts); - - return $body_id; - } -} diff --git a/app/controllers/studip_controller_properties_trait.php b/app/controllers/studip_controller_properties_trait.php deleted file mode 100644 index 4e906fa..0000000 --- a/app/controllers/studip_controller_properties_trait.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * This trait manages all variable assignments to the controller and templates. - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @license GPL2 or any later version - * @since Stud.IP 5.2 - */ -trait StudipControllerPropertiesTrait -{ - /** - * Stores the assigned variables. - * @var array - */ - protected $_template_variables = []; - - /** - * Returns whether a variable is set. - * - * @param string $offset - * @return bool - */ - public function __isset(string $offset): bool - { - return isset($this->_template_variables[$offset]); - } - - /** - * Stores a variable. - * - * @param string $offset - * @param mixed $value - */ - public function __set(string $offset, $value): void - { - $this->_template_variables[$offset] = $value; - } - - /** - * Returns a previously set variable. - * - * @param string $offset - * @return mixed - */ - public function &__get(string $offset) - { - if (!isset($this->_template_variables[$offset])) { - $this->_template_variables[$offset] = null; - } - return $this->_template_variables[$offset]; - } - - /** - * Unsets a previously set variable - * - * @param string $offset - */ - public function __unset(string $offset): void - { - unset($this->_template_variables[$offset]); - } - - public function get_assigned_variables(): array - { - $variables = $this->_template_variables; - $variables['controller'] = $this; - return $variables; - } -} diff --git a/app/controllers/studip_response.php b/app/controllers/studip_response.php deleted file mode 100644 index 1c15326..0000000 --- a/app/controllers/studip_response.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -class StudipResponse extends Trails_Response -{ - /** - * Outputs this response to the client using "echo" and "header". - * - * This extension allows the body to be a callable and handles generators - * by outputting the chunks yielded by the generator. - */ - public function output() - { - if (isset($this->status)) { - $this->send_header( - "{$_SERVER['SERVER_PROTOCOL']} {$this->status} {$this->reason}", - true, - $this->status - ); - } - - // Send headers - foreach ($this->headers as $k => $v) { - $this->send_header("{$k}: {$v}"); - } - - // Determine output - if (is_callable($this->body)) { - $output = call_user_func($this->body); - } else { - $output = $this->body; - } - - if ($output instanceof Generator) { - // Clear output buffer - while (ob_get_level()) { - ob_end_clean(); - } - - // Ensure generator will run to the end - $abort = ignore_user_abort(true); - - // Output chunks yielded by generator - foreach ($output as $chunk) { - if (!connection_aborted()) { - echo $chunk; - flush(); - } - } - - // Reset user abort to previous state - ignore_user_abort($abort); - } else { - echo $output; - } - } -} diff --git a/app/controllers/vote.php b/app/controllers/vote.php deleted file mode 100644 index 19f92a2..0000000 --- a/app/controllers/vote.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -# Lifter010: TODO -/** - * vote.php - Votecontroller controller - * - * 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. - */ - -class VoteController extends AuthenticatedController { - - public function display_action($range_id) { - - // Bind some params - URLHelper::bindLinkParam('show_expired', $null1); - URLHelper::bindLinkParam('preview', $null2); - URLHelper::bindLinkParam('revealNames', $null3); - URLHelper::bindLinkParam('sort', $null4); - - // Bind range_id - $this->range_id = $range_id; - - $this->nobody = !$GLOBALS['user']->id || $GLOBALS['user']->id == 'nobody'; - - /* - * Insert vote - */ - if ($vote = Request::get('vote')) { - $vote = new Vote($vote); - if (!$this->nobody && $vote && $vote->isRunning() && (!$vote->userVoted() || $vote->changeable)) { - try { - $vote->insertVote(Request::getArray('vote_answers'), $GLOBALS['user']->id); - } catch (Exception $exc) { - $GLOBALS['vote_message'][$vote->id] = MessageBox::error($exc->getMessage()); - } - } - } - - // Check if we need administration icons - $this->admin = $range_id == $GLOBALS['user']->id || $GLOBALS['perm']->have_studip_perm('tutor', $range_id); - - - // Load evaluations - if (!$this->nobody) { - $eval_db = new EvaluationDB(); - $this->evaluations = StudipEvaluation::findMany($eval_db->getEvaluationIDs($range_id, EVAL_STATE_ACTIVE)); - } else { - $this->evaluations = []; - } - $show_votes[] = 'active'; - // Check if we got expired - if (Request::get('show_expired')) { - $show_votes[] = 'stopvis'; - if ($this->admin) { - $this->evaluations = array_merge($this->evaluations, StudipEvaluation::findMany($eval_db->getEvaluationIDs($range_id, EVAL_STATE_STOPPED))); - $show_votes[] = 'stopinvis'; - } - } - - $this->votes = Vote::findBySQL('range_id = ? AND state IN (?) ORDER BY mkdate desc', [$range_id,$show_votes]); - $this->visit(); - - } - - function visit() - { - if ($GLOBALS['user']->id && $GLOBALS['user']->id != 'nobody' && Request::option('contentbox_open') && in_array(Request::option('contentbox_type'), words('vote eval'))) { - object_set_visit(Request::option('contentbox_open'), Request::option('contentbox_type')); - } - } - - function visit_action() - { - $this->visit(); - $this->render_nothing(); - } - - /** - * Determines if a vote should show its result - * - * @param Vote $vote the vote to check - * @return boolean true if result should be shown - */ - public function showResult($vote) { - if (Request::submitted('change') && $vote->changeable) { - return false; - } - return $vote->userVoted() || in_array($vote->id, Request::getArray('preview')); - } - -} diff --git a/app/routes/Activity.php b/app/routes/Activity.php deleted file mode 100644 index fadca0f..0000000 --- a/app/routes/Activity.php +++ /dev/null @@ -1,168 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author Till Glöggler <tgloeggl@uos.de> - * @author André Klaßen <klassen@elan-ev.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition user_id ^[a-f0-9]{1,32}$ - */ -class Activity extends \RESTAPI\RouteMap -{ - /** - * List activities for an user - * - * @get /user/:user_id/activitystream - * - * @param string $user_id the user to get the activities for - * - * @return array the activities as array('collection' => array(...), 'pagination' => array()) - */ - public function getActivities($user_id) - { - // only root can retrieve arbitrary streams - if (!$GLOBALS['perm']->have_perm('root') && $GLOBALS['user']->id != $user_id) { - $this->error(401); - } - - // failsafe einbauen - falls es keine älteren Aktivitäten mehr im System gibt, Abbruch! - - $oldest_activity = \Studip\Activity\Activity::getOldestActivity(); - $max_age = $oldest_activity ? $oldest_activity->mkdate : time(); - - - $contexts = []; - - $user = \User::find($user_id); - - // create system context - $system_context = new \Studip\Activity\SystemContext($user); - $contexts[] = $system_context; - - $contexts[] = new \Studip\Activity\UserContext($user, $user); - $user->contacts->each(function($another_user) use (&$contexts, $user) { - $contexts[] = new \Studip\Activity\UserContext($another_user, $user); - }); - - if (!in_array($user->perms, ['admin','root'])) { - // create courses and institutes context - foreach (\Course::findMany($user->course_memberships->pluck('seminar_id')) as $course) { - $contexts[] = new \Studip\Activity\CourseContext($course, $user); - } - foreach (\Institute::findMany($user->institute_memberships->pluck('institut_id')) as $institute) { - $contexts[] = new \Studip\Activity\InstituteContext($institute, $user); - } - } - - - // add filters - $filter = new \Studip\Activity\Filter(); - - $start = \Request::int('start', strtotime('-1 days')); - $end = \Request::int('end', time()); - - - $scrollfrom = \Request::int('scrollfrom', false); - $filtertype = \Request::get('filtertype', ''); - - $objectType = \Request::get('object_type'); - $filter->setObjectType($objectType); - - $objectId = \Request::get('object_id'); - $filter->setObjectId($objectId); - - $context = \Request::get('context_type'); - $filter->setContext($context); - - $contextId = \Request::get('context_id'); - $filter->setContextId($contextId); - - if (!empty($filtertype)) { - $filter->setType(json_decode($filtertype)); - } - - if ($scrollfrom) { - // shorten "watch-window" by one second to prevent duplication of activities - $scrollfrom -= 1; - - if ($scrollfrom > $max_age){ - $end = $scrollfrom; - $start = strtotime('-1 day', $end); - $data = []; - - $backtrack = 1; - - while (empty($data)) { - $filter->setStartDate($start); - $filter->setEndDate($end); - - $data = $this->getStreamData($contexts, $filter); - - if ($start < $max_age) { - break; - } - - // move "watch-window" back one day at a time - $end = $start - 1; - $start = strtotime('-'. $backtrack . ' days', $start); - - // enforce maximum "watch-window", currently 2 weeks - $backtrack = min (14, $backtrack + 1); - } - } else { - $data = false; - } - } else { - - $filter->setStartDate($start); - $filter->setEndDate($end); - $data = $this->getStreamData($contexts, $filter); - - } - - // set etag for preventing resending the same stuff over and over again - $this->etag(md5(serialize($data))); - - return $data; - } - - /** - * private helper function to get stream data for given contexts and filter - * - * @param $contexts - * @param $filter - * @return array - */ - - private function getStreamData($contexts, $filter) - { - $stream = new \Studip\Activity\Stream($contexts, $filter); - $data = $stream->toArray(); - - foreach ($data as $key => $act) { - $actor = [ - 'type' => $data[$key]['actor_type'], - 'id' => $data[$key]['actor_id'] - ]; - - if ($data[$key]['actor_type'] == 'user') { - $a_user = \User::findFull($data[$key]['actor_id']); - $actor['details'] = User::getMiniUser($this, $a_user ?: new \User()); - } elseif ($data[$key]['actor_type'] === 'anonymous') { - $actor['details'] = [ - 'name' => _('Anonym'), - ]; - } - - unset($data[$key]['actor_type']); - unset($data[$key]['actor_id']); - - $data[$key]['actor'] = $actor; - } - - return $data; - - } -} diff --git a/app/routes/Blubber.php b/app/routes/Blubber.php deleted file mode 100644 index 1445088..0000000 --- a/app/routes/Blubber.php +++ /dev/null @@ -1,321 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition course_id ^[a-f0-9]{1,32}$ - * @condition stream_id ^(global|[a-f0-9]{1,32})$ - * @condition user_id ^[a-f0-9]{1,32}$ - * @condition blubber_id ^[a-f0-9]{1,32}$ - */ -class Blubber extends \RESTAPI\RouteMap -{ - - /** - * Get content and some comments for a blubber-thread or for the "global" thread all "public" threads. - * - * @get /blubber/threads/:thread_id - * @param string $thread_id id of the blubber thread or "global" if you want public threads (not comments). Remind the global thread is a virtual thread with a special behaviour. - * @return array the blubber as array - */ - public function getThreadData($thread_id) - { - if (!$GLOBALS['perm']->have_perm('autor')) { - $this->error(401); - } - $GLOBALS['user']->cfg->store('BLUBBER_DEFAULT_THREAD', $thread_id); - - $thread = new \BlubberThread($thread_id); - $thread = \BlubberThread::upgradeThread($thread); - if (!$thread->isReadable()) { - $this->error(401); - } - - $json = $thread->getJSONData(50, null, \Request::get("search")); - $thread->markAsRead(); - - $this->etag(md5(serialize($json))); - - return $json; - } - - /** - * Get threads - * - * @get /blubber/threads - * @return array the stream as array - */ - public function getMyThreads() - { - $threads_data = [ - 'threads' => [], - 'more_down' => 0, - ]; - $limit = \Request::int('limit', 50); - - $threads = \BlubberThread::findMyGlobalThreads( - $limit + 1, - null, - \Request::int('timestamp'), - null, - \Request::get("search") ?: null - ); - if (count($threads) > $limit) { - array_pop($threads); - $threads_data['more_down'] = 1; - } - foreach ($threads as $thread) { - $threads_data['threads'][] = [ - 'thread_id' => $thread->getId(), - 'avatar' => $thread->getAvatar(), - 'name' => $thread->getName(), - 'timestamp' => (int) $thread->getLatestActivity(), - ]; - } - return $threads_data; - } - - /** - * Write a comment to a thread - * - * @post /blubber/threads/:thread_id/comments - * @param string $thread_id id of the blubber thread - * @return array the comment as array - */ - public function postComment($thread_id) - { - if (!$GLOBALS['perm']->have_perm('autor')) { - $this->error(401); - } - - if (!trim($this->data['content'])) { - $this->error(406); - } - - $thread = \BlubberThread::find($thread_id); - if (!$thread->isCommentable()) { - $this->error(401); - } - - $comment = new \BlubberComment(); - $comment['thread_id'] = $thread_id; - $comment['content'] = $this->data['content']; - $comment['user_id'] = $GLOBALS['user']->id; - $comment['external_contact'] = 0; - $comment->store(); - - $thread->setLastVisit(); - - return $comment->getJSONData(); - } - - /** - * Write a comment to a thread - * - * @put /blubber/threads/:thread_id/comments/:comment_id - * - * @param string $thread_id id of the blubber thread - * @param string $comment id of the comment - * - * @return array the comment as array - */ - public function editComment($thread_id, $comment_id) - { - $comment = \BlubberComment::find($comment_id); - if (!$comment->isWritable()) { - $this->error(401); - } - $old_content = $comment['content']; - $comment['content'] = $this->data['content']; - - if ($comment['user_id'] !== $GLOBALS['user']->id) { - $messaging = new \messaging(); - $message = sprintf( - _("%s hat als Moderator gerade Ihren Beitrag in Blubber editiert.\n\nDie alte Version des Beitrags lautete:\n\n%s\n\nDie neue lautet:\n\n%s\n"), - get_fullname(), $old_content, $comment['content'] - ); - - $message .= "\n\n"; - - $message .= '[' . _('Link zu diesem Beitrag') . ']'; - $message .= \URLHelper::getURL( - "{$GLOBALS['ABSOLUTE_URI_STUDIP']}dispatch.php/blubber/index/{$comment->thread_id}", - [], - true - ); - - $messaging->insert_message( - $message, - get_username($comment['user_id']), - $GLOBALS['user']->id, - null, null, null, null, - _("Änderungen an Ihrem Blubber.") - ); - } - - if (!trim($this->data['content'])) { - $data = $comment->getJSONData(); - $comment->delete(); - } else { - $comment->store(); - $data = $comment->getJSONData(); - } - return $data; - } - - /** - * Write a comment to a thread - * - * @get /blubber/threads/:thread_id/comments - * - * @param string $thread_id id of the blubber thread - * - * @return array the comments as array - */ - public function getComments($thread_id) - { - if (!$GLOBALS['perm']->have_perm('autor')) { - $this->error(401); - } - - $thread = new \BlubberThread($thread_id); - if (!$thread->isReadable()) { - $this->error(401); - } - - $modifier = \Request::get('modifier'); - if ($modifier === 'olderthan') { - $limit = \Request::int('limit', 50); - - $query = "SELECT blubber_comments.* - FROM blubber_comments - WHERE blubber_comments.thread_id = :thread_id - AND blubber_comments.mkdate <= :timestamp - ORDER BY mkdate DESC - LIMIT :limit"; - $result = \DBManager::get()->fetchAll($query, [ - 'thread_id' => $thread_id, - 'timestamp' => \Request::int('timestamp', time()), - 'limit' => $limit + 1, - ]); - - $output = ['comments' => []]; - - if (count($result) > $limit) { - array_pop($result); - $output['more_up'] = 1; - } else { - $output['more_up'] = 0; - } - foreach ($result as $data) { - $comment = \BlubberComment::buildExisting($data); - $output['comments'][] = $comment->getJSONData(); - } - return $output; - } - - if ($modifier === 'newerthan') { - $limit = \Request::int('limit', 50); - - $query = "SELECT blubber_comments.* - FROM blubber_comments - WHERE blubber_comments.thread_id = :thread_id - AND blubber_comments.mkdate >= :timestamp - ORDER BY mkdate - LIMIT :limit"; - $comments = \DBManager::get()->fetchAll($query, [ - 'thread_id' => $thread_id, - 'timestamp' => \Request::int('timestamp', time()), - 'limit' => $limit + 1, - ], function ($comment) { - return \BlubberComment::buildExisting($comment)->getJSONData(); - }); - - $output = ['comments' => $comments]; - - if (count($comments) > $limit) { - array_pop($output['comments']); - $output['more_down'] = 1; - } else { - $output['more_down'] = 0; - } - - return $output; - } - - $query = "SELECT blubber_comments.* - FROM blubber_comments - WHERE blubber_comments.thread_id = :thread_id "; - $parameters = ['thread_id' => $thread_id]; - - if (\Request::get('search')) { - $query .= " AND blubber_comments.content LIKE :search "; - $parameters['search'] = '%'.\Request::get('search').'%'; - } - $query .= " ORDER BY mkdate ASC "; - - $output['comments'] = \DBManager::get()->fetchAll($query, $parameters, function ($comment) { - return \BlubberComment::buildExisting($comment)->getJSONData(); - }); - $output['more_up'] = 0; - $output['more_down'] = 0; - - return $output; - } - - /** - * Does the current user follow the thread? - * - * @get /blubber/threads/:thread_id/follow - */ - public function threadIsFollowed($thread_id) - { - return $this->requireThread($thread_id)->isFollowedByUser(); - } - - /** - * User follows a thread. - * - * @post /blubber/threads/:thread_id/follow - * - * @param string $thread_id id of the blubber thread - */ - public function followThread($thread_id) - { - $this->requireThread($thread_id)->addFollowingByUser(); - } - - /** - * User unfollows a thread. - * - * @delete /blubber/threads/:thread_id/follow - * - * @param string $thread_id id of the blubber thread - */ - public function unfollowThread($thread_id) - { - $this->requireThread($thread_id)->removeFollowingByUser(); - } - - /** - * Returns a blubber thread and checks permissions. - * - * @param string $thread_id Id of the blubber thread - * @return \BlubberThread - */ - private function requireThread($thread_id) - { - if (!$GLOBALS['perm']->have_perm('autor')) { - $this->error(401); - } - - $thread = new \BlubberThread($thread_id); - if (!$thread->isReadable()) { - $this->error(401); - } - - return \BlubberThread::upgradeThread($thread); - } -} diff --git a/app/routes/Clipboard.php b/app/routes/Clipboard.php deleted file mode 100644 index dfe22e0..0000000 --- a/app/routes/Clipboard.php +++ /dev/null @@ -1,193 +0,0 @@ -<?php -namespace RESTAPI\Routes; - - -/** - * This file contains the REST class for the clipboard system. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @since 4.5 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class Clipboard extends \RESTAPI\RouteMap -{ - /** - * Adds a new clipboard. - * - * @post /clipboard/add - */ - public function addClipboard() - { - $name = \Request::get('name'); - - if (!$name) { - $this->halt(400, _('Es wurde kein Name angegeben!')); - } - - $clipboard = new \Clipboard(); - $clipboard->user_id = $GLOBALS['user']->id; - $clipboard->name = $name; - if (!$clipboard->store()) { - $this->halt(500, _('Fehler beim Speichern des Merkzettels!')); - } - - $result = $clipboard->toRawArray(); - //A special treatment for the widget_id parameter: - //It is passed through: - $widget_id = \Request::get('widget_id'); - if ($widget_id) { - $result['widget_id'] = $widget_id; - } - - return $result; - } - - - /** - * Edits a clipboard. - * - * @put /clipboard/:clipboard_id - */ - public function editCliboard($clipboard_id = null) - { - $clipboard = \Clipboard::find($clipboard_id); - if (!$clipboard) { - $this->notFound(_('Ungültige Merkzettel-ID!')); - } - - if ($clipboard->user_id != $GLOBALS['user']->id) { - //Thou shalt not delete clipboards - //which don't belong to you! - throw new \AccessDeniedException(); - } - - $name = $this->data['name']; - if (!$name) { - $this->halt(400, _('Es wurde kein Name angegeben!')); - } - - $clipboard->name = $name; - - if ($clipboard->isDirty()) { - $success = $clipboard->store(); - } else { - $success = true; - } - - if (!$success) { - $this->halt(500, _('Fehler beim Bearbeiten des Merkzettels!')); - } - - $result = $clipboard->toRawArray(); - - //A special treatment for the widget_id parameter: - //It is passed through: - $widget_id = \Request::get('widget_id'); - if ($widget_id) { - $result['widget_id'] = $widget_id; - } - - return $result; - } - - - /** - * Deletes a clipboard. - * - * @delete /clipboard/:clipboard_id - */ - public function deleteClipboard($clipboard_id = null) - { - $clipboard = \Clipboard::find($clipboard_id); - if (!$clipboard) { - $this->notFound(_('Ungültige Merkzettel-ID!')); - } - - if ($clipboard->user_id !== $GLOBALS['user']->id) { - //Thou shalt not delete items of clipboards - //which don't belong to you! - throw new \AccessDeniedException(); - } - - if (!$clipboard->delete()) { - $this->halt(500, _('Fehler beim Löschen des Merkzettels!')); - } - - return ""; - } - - - /** - * Adds an item to a clipboard. - * - * @post /clipboard/:clipboard_id/item - */ - public function addClipboardItem($clipboard_id = null) - { - $clipboard = \Clipboard::find($clipboard_id); - if (!$clipboard) { - $this->notFound(_('Ungültige Merkzettel-ID!')); - } - - if ($clipboard->user_id != $GLOBALS['user']->id) { - //Thou shalt not add items to clipboards - //which don't belong to you! - throw new \AccessDeniedException(); - } - - $range_id = \Request::get('range_id'); - $range_type = \Request::get('range_type'); - $widget_id = \Request::get('widget_id'); - - if (!is_a($range_type, $clipboard->allowed_item_class, true)) { - $this->halt( - 400, - sprintf( - _('Die Klasse %s ist in dieser Merkzettel-Klasse nicht erlaubt!'), - $range_type - ) - ); - } - - try { - $item = $clipboard->addItem($range_id, $range_type); - - $result = $item->toRawArray(); - $result['name'] = $item->__toString(); - if ($widget_id) { - $result['widget_id'] = $widget_id; - } - return $result; - } catch (\Exception $e) { - $this->halt(500, $e->getMessage()); - } - } - - - /** - * Removes an item (selected by its range-ID) from a clipboard. - * - * @delete /clipboard/:clipboard_id/item/:range_id - */ - public function removeClipboardItem($clipboard_id = null, $range_id = null) - { - $clipboard = \Clipboard::find($clipboard_id); - if (!$clipboard) { - $this->notFound(_('Ungültige Merkzettel-ID!')); - } - - if ($clipboard->user_id != $GLOBALS['user']->id) { - //Thou shalt not delete items of clipboards - //which don't belong to you! - throw new \AccessDeniedException(); - } - - if ($clipboard->removeItem($range_id)) { - return ['range_id' => $range_id]; - } else { - $this->halt(500, _('Fehler beim Löschen des Eintrags!')); - } - } -} diff --git a/app/routes/Contacts.php b/app/routes/Contacts.php deleted file mode 100644 index d7fd010..0000000 --- a/app/routes/Contacts.php +++ /dev/null @@ -1,302 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition user_id ^[a-f0-9]{1,32}$ - * @condition friend_id ^[a-f0-9]{1,32}$ - * @condition group_id ^[a-f0-9]{1,32}$ - */ -class Contacts extends \RESTAPI\RouteMap -{ - - public static function before() - { - require_once 'User.php'; - require_once 'lib/statusgruppe.inc.php'; - } - - /** - * Lists all contacts of a user - * - * @get /user/:user_id/contacts - */ - public function getUserContacts($user_id) - { - if ($GLOBALS['user']->id !== $user_id) { - $this->error(401); - } - - // quite degenerated as long as we can only see our own contacts - $user = $this->requireUser($user_id); - - $total = count($user->contacts); - $contacts = $user->contacts->limit($this->offset, $this->limit); - - $contacts_json = $this->contactsToJSON($contacts); - $this->etag(md5(serialize($contacts_json))); - - return $this->paginated($contacts_json, - $total, compact('user_id')); - } - - /** - * Adds/Updates a contact to user's list of contacts - * - * @put /user/:user_id/contacts/:friend_id - */ - public function addUserContact($user_id, $buddy_user_id) - { - if ($GLOBALS['user']->id !== $user_id) { - $this->error(401); - } - - $user = $this->requireUser($user_id); - $friend = $this->requireUser($buddy_user_id); - - // prevent duplicates - if ($user->isFriendOf($friend)) { - $this->error(409, sprintf('User "%s" is already a contact', htmlReady($friend->id))); - } - - $user->contacts[] = $friend; - $user->store(); - - $this->status(201); - } - - /** - * Deletes a contact - * - * @delete /user/:user_id/contacts/:friend_id - */ - public function removeUserContact($user_id, $buddy_user_id) - { - if ($GLOBALS['user']->id !== $user_id) { - $this->error(401); - } - - $user = $this->requireUser($user_id); - $friend = $this->requireUser($buddy_user_id); - - if (!$user->isFriendOf($friend)) { - $this->notFound("Contact not found"); - } - - $user->contacts->unsetByPK($friend->id); - $user->store(); - - $this->status(204); - } - - - /** - * List all contact groups of a user - * - * @get /user/:user_id/contact_groups - */ - public function getUserContactGroups($user_id) - { - if ($GLOBALS['user']->id !== $user_id) { - $this->error(401); - } - - $contact_groups = \SimpleCollection::createFromArray( - \Statusgruppen::findByRange_id($GLOBALS['user']->id)) - ->orderBy('name ASC'); - - $total = count($contact_groups); - $contact_groups = $contact_groups->limit($this->offset, $this->limit); - - $contact_groups_json = $this->contactGroupsToJSON($contact_groups); - $this->etag(md5(serialize($contact_groups_json))); - - return $this->paginated($contact_groups_json, - $total, compact('user_id')); - } - - /** - * Create a new contact group for a user. - * - * @post /user/:user_id/contact_groups - */ - public function createContactGroup($user_id) - { - if ($GLOBALS['user']->id !== $user_id) { - $this->error(401); - } - - if (!isset($this->data['name']) || !mb_strlen($name = trim($this->data['name']))) { - $this->error(400, 'Contact group name required.'); - } - - $group = new \Statusgruppen(); - $group->range_id = $GLOBALS['user']->id; - $group->name = $name; - $group->size = 0; - $group->selfassign = 0; - $group->calendar_group = 0; - $group->store(); - $this->redirect('contact_group/' . $group->id, 201, 'ok'); - } - - /** - * Show a single contact group - * - * @get /contact_group/:group_id - */ - public function showContactGroup($group_id) - { - $group = $this->requireContactGroup($group_id); - $contact_group_json = $this->contactGroupToJSON($group); - $this->etag(md5(serialize($contact_group_json))); - return $contact_group_json; - } - - /** - * Remove a contact group - * - * @delete /contact_group/:group_id - */ - public function destroyContactGroup($group_id) - { - $group = $this->requireContactGroup($group_id); - - $group->remove(); - - $this->status(204); - } - - /** - * List all members of a contact group - * - * @get /contact_group/:group_id/members - */ - public function indexOfContactGroupMembers($group_id) - { - $group = $this->requireContactGroup($group_id); - $contacts = $group->members->limit($this->offset, $this->limit); - - $json = []; - foreach ($contacts as $contact) { - $url = $this->urlf('/contact_group/%s/members/%s', [$group_id, $contact->user_id]); - $json[$url] = User::getMiniUser($this, $contact->user); - } - - $this->etag(md5(serialize($json))); - - return $this->paginated($json, count($group->members), compact('group_id')); - } - - /** - * Add a user to a contact group - * - * @put /contact_group/:group_id/members/:user_id - */ - public function addToContactGroup($group_id, $user_id) - { - $group = $this->requireContactGroup($group_id); - $user = $this->requireUser($user_id); - - // prevent duplicates - $exists = $group->members->findBy('user_id', $user_id)->first(); - if ($exists) { - $this->halt(204); - } - - $new_contact = [ - 'owner_id' => $GLOBALS['user']->id, - 'user_id' => $user->id]; - - $new_contact['group_assignments'][] = ['statusgruppe_id' => $group->id, - 'user_id' => $user->id]; - - $success = (bool)\Contact::import($new_contact)->store(); - - - if (!$success) { - $this->error(500); - } - - $this->status(201); - } - - /** - * Remove a user from a contact group - * - * @delete /contact_group/:group_id/members/:user_id - */ - public function removeFromContactGroup($group_id, $user_id) - { - $group = $this->requireContactGroup($group_id); - $membership = $group->members->findBy('user_id', $user_id)->first(); - if (!$membership) { - $this->notFound(); - } - - $membership->delete(); - - $this->status(204); - } - - - /**************************************************/ - /* PRIVATE HELPER METHODS */ - /**************************************************/ - - private function requireUser($user_id) - { - $user = \User::find($user_id); - // TODO: checks visibility using the global perm object! - if (!$user || !get_visibility_by_id($user_id)) { - $this->notFound(sprintf("Could not find user with id: %s", htmlReady($user_id))); - } - - return $user; - } - - private function requireContactGroup($group_id) - { - $group = \Statusgruppen::find($group_id); - if (!$group) { - $this->notFound(); - } - - if ($group->range_id !== $GLOBALS['user']->id) { - $this->error(401); - } - return $group; - } - - private function contactsToJSON($contacts) { - $result = []; - foreach ($contacts as $contact) { - $result[] = User::getMiniUser($this, $contact); - } - return $result; - } - - private function contactGroupsToJSON($contact_groups) - { - $result = []; - foreach ($contact_groups as $cg) { - $url = $this->urlf('/contact_group/%s', [htmlReady($cg->id)]); - $result[$url] = $this->contactGroupToJSON($cg); - } - return $result; - } - - private function contactGroupToJSON($group) - { - $json = [ - 'id' => $group->id, - 'name' => (string) $group->name, - 'contacts' => $this->urlf('/contact_group/%s/members', [htmlReady($group->id)]), - 'contacts_count' => sizeof($group->members) - ]; - return $json; - } -} diff --git a/app/routes/Course.php b/app/routes/Course.php deleted file mode 100644 index d1fad96..0000000 --- a/app/routes/Course.php +++ /dev/null @@ -1,242 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition course_id ^[a-f0-9]{1,32}$ - * @condition user_id ^[a-f0-9]{1,32}$ - */ -class Course extends \RESTAPI\RouteMap -{ - - public function before() - { - require_once 'User.php'; - } - - /** - * Lists all courses of a user including the semesters in which - * that course is active. - * Optionally filtered by a URL parameter 'semester'. - * - * @get /user/:user_id/courses - */ - public function getUserCourses($user_id) - { - if (($GLOBALS['user']->id !== $user_id) && !$GLOBALS['perm']->have_perm("root")) { - $this->error(401); - } - - // setting up semester to filter by - $semester = null; - $semester_id = \Request::get('semester'); - if ($semester_id) { - $semester = \Semester::find($semester_id); - if (!$semester) { - $this->error(400, "Semester not found."); - } - } - - $memberships = $this->findMembershipsByUserId($user_id, $semester); - - $total = count($memberships); - $memberships = $memberships->limit($this->offset, $this->limit); - $memberships_json = $this->membershipsToJSON($memberships); - $this->etag(md5(serialize($memberships_json))); - return $this->paginated( - $memberships_json, - $total, - compact('user_id'), - ['semester' => $semester_id] - ); - } - - /** - * Show a single course - * - * @get /course/:course_id - */ - public function getCourse($course_id) - { - if (!$course = \Course::find($course_id)) { - $this->notFound("Course not found"); - } - - $course = $this->requireCourse($course_id); - $this->lastmodified($course->chdate); - $course_json = $this->courseToJSON($course); - $this->etag(md5(serialize($course_json))); - return $course_json; - } - - /** - * List all members of a course. - * Optionally filtered by a URL parameter 'status'. - * - * @get /course/:course_id/members - */ - public function getMembers($course_id) - { - $status_filter = \Request::get('status'); - if ($status_filter && !in_array($status_filter, words("user autor tutor dozent"))) { - $this->error(400, "Status may be one of: user, autor, tutor, dozent"); - } - - $course = $this->requireCourse($course_id); - $members = $course->members; - if ($status_filter) { - $members = $members->findBy('status', $status_filter); - } - - $total = count($members); - $members = $members->limit($this->offset, $this->limit); - $members_json = $this->membersToJSON($course, $members); - $this->etag(md5(serialize($members_json))); - return $this->paginated( - $members_json, - $total, - compact('course_id'), - ['status' => $status_filter] - ); - } - - /** - * Get the root file folder of a course. - * - * @get /course/:course_id/top_folder - */ - public function getTopFolder($course_id) - { - $top_folder = \Folder::findTopFolder( - $this->requireCourse($course_id)->id, - 'course' - ); - - if (!$top_folder) { - $this->notFound("No folder found for course with id {$course_id}!"); - } - - return (new FileSystem())->getFolder($top_folder->id); - } - - /**************************************************/ - /* PRIVATE HELPER METHODS */ - /**************************************************/ - - private function findMembershipsByUserId($user_id, $semester) - { - $memberships = \SimpleORMapCollection::createFromArray( - \CourseMember::findBySQL('user_id = ? ORDER BY mkdate ASC', [$user_id]) - ); - - // filter by semester - if ($semester) { - - $memberships = $memberships->filter(function ($m) use ($semester) { - return $m->course->isInSemester($semester); - }); - } - - return $memberships; - } - - private function membershipsToJSON($memberships) - { - $json = []; - - foreach ($memberships as $membership) { - $course_json = $this->courseToJSON($course = $membership->course); - - $json[$this->urlf("/course/%s", [$course->id])] = $course_json; - } - return $json; - } - - private function courseToJSON($course) - { - $json = []; - - $json['course_id'] = $course->id; - $json['number'] = $course->VeranstaltungsNummer; - $json['title'] = (string) $course->Name; - $json['subtitle'] = (string) $course->Untertitel; - $json['type'] = $course->status; - $json['description'] = (string) $course->Beschreibung; - $json['location'] = (string) $course->Ort; - - // lecturers - foreach ($course->getMembersWithStatus('dozent') as $lecturer) { - $url = $this->urlf('/user/%s', [htmlReady($lecturer->user_id)]); - $json['lecturers'][$url] = User::getMiniUser($this, $lecturer->user); - } - - // other members - foreach (words("user autor tutor dozent") as $status) { - $json['members'][$status] = $this->urlf('/course/%s/members?status=%s', [$course->id, $status]); - $json['members'][$status . '_count'] = $course->countMembersWithStatus($status); - } - - foreach (words("start_semester end_semester") as $key) { - $json[$key] = $course->$key ? $this->urlf('/semester/%s', [htmlReady($course->$key->id)]) : null; - } - - $activated = array_map('get_class', $course->getActivatedTools()); - - $json['modules'] = []; - foreach (['forum' => 'forum_categories', - 'documents' => 'top_folder', - 'wiki' => 'wiki'] as $module => $uri) - { - if (in_array('Core' . ucfirst($module), $activated)) { - $json['modules'][$module] = $this->urlf('/course/%s/%s', [htmlReady($course->id), $uri]); - } - } - - // Add group if current user is member of the group - $json['group'] = null; - - $member = \CourseMember::find([$course->id, $GLOBALS['user']->id]); - if ($member) { - $json['group'] = (int) $member->gruppe; - } - - - return $json; - } - - private function requireCourse($id) - { - if (!$course = \Course::find($id)) { - $this->notFound("Course not found"); - } - - //This route is used in the room management system. - //Therefore, we need not only to check if the user is in the course, - //but also, if the user is a global resource admin. In the latter case, - //access shall also be granted. - if (!$GLOBALS['perm']->have_studip_perm('user', $id, $GLOBALS['user']->id) - && !\ResourceManager::userHasGlobalPermission(\User::findCurrent(), 'admin')) { - $this->error(401); - } - - return $course; - } - - private function membersToJSON($course, $members) - { - $json = []; - - foreach ($members as $member) { - $url = $this->urlf('/user/%s', [$member->user_id]); - $avatar = \Avatar::getAvatar($member->user_id); - $json[$url] = [ - 'member' => User::getMiniUser($this, $member->user), - 'status' => $member->status - ]; - } - return $json; - } -} diff --git a/app/routes/Discovery.php b/app/routes/Discovery.php deleted file mode 100644 index c83f524..0000000 --- a/app/routes/Discovery.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class Discovery extends \RESTAPI\RouteMap -{ - /** - * Schnittstellenbeschreibung - * - * @get /discovery - */ - public function getDiscovery() - { - $routes = $this->router->getRoutes(true); - foreach ($routes as $uri_template => $methods) { - foreach ($methods as $method => $route) { - $routes[$uri_template][$method] = $route['description']; - } - } - return $routes; - } -} diff --git a/app/routes/Events.php b/app/routes/Events.php deleted file mode 100644 index 368d615..0000000 --- a/app/routes/Events.php +++ /dev/null @@ -1,186 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -use Config; -use Resource; -use Room; -use Seminar; -use Issue; - - -/** - * @author André Klaßen <andre.klassen@elan-ev.de> - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition course_id ^[a-f0-9]{1,32}$ - * @condition user_id ^[a-f0-9]{1,32}$ - * @condition semester_id ^[a-f0-9]{1,32}$ - */ -class Events extends \RESTAPI\RouteMap -{ - - /** - * returns all upcoming events within the next two weeks for a given user - * - * @get /user/:user_id/events - */ - public function getEvents($user_id) - { - if ($user_id !== $GLOBALS['user']->id) { - $this->error(401); - } - - $start = new \DateTime(); - $end = clone $start; - $end = $end->add(new \DateInterval('P2W')); - - $list = array_merge( - \CalendarCourseDate::getEvents($start, $end, $user_id), - \CalendarCourseExDate::getEvents($start, $end, $user_id) - ); - - $json = []; - $events = array_slice($list, $this->offset, $this->limit); ; - foreach ($events as $event) { - - $course_uri = $this->urlf('/course/%s', [htmlReady($event->range_id)]); - - $json[] = [ - 'event_id' => $event->id, - 'course' => $course_uri, - 'start' => $event->date, - 'end' => $event->end_time, - 'title' => $event->getTitle(), - 'description' => $event->getDescription() ?: '', - 'categories' => $event->getTypeName(), - 'room' => $event->getRoomName(), - 'canceled' => $event instanceof \CourseExDate || holiday($event->date), - ]; - } - - $this->etag(md5(serialize($json))); - - return $this->paginated($json, count($list), compact('user_id')); - } - - /** - * returns an iCAL Export of all events for a given user - * - * @get /user/:user_id/events.ics - */ - public function getEventsICAL($user_id) - { - if ($user_id !== $GLOBALS['user']->id) { - $this->error(401); - } - $end = new \DateTime(); - $end->setTimestamp(\CalendarDate::NEVER_ENDING); - $start = new \DateTime(); - $start->modify('-4 week'); - $ical_export = new \ICalendarExport(); - $ical = $ical_export->exportCalendarDates($user_id, $start, $end) - . $ical_export->exportCourseDates($user_id, $start, $end) - . $ical_export->exportCourseExDates($user_id, $start, $end); - $content = $ical_export->writeHeader() . $ical . $ical_export->writeFooter(); - - $this->contentType('text/calendar'); - $this->headers([ - 'Content-Length' => strlen($content), - 'Content-Disposition' => 'attachment; ' . encode_header_parameter('filename', 'studip.ics'), - ]); - $this->halt(200, $this->response->headers, function () use ($content) { - echo $content; - }); - } - - - /** - * returns events for a given course - * - * @get /course/:course_id/events - */ - public function getEventsForCourse($course_id) - { - if (!$GLOBALS['perm']->have_studip_perm('user', $course_id, $GLOBALS['user']->id)) { - $this->error(401); - } - - $seminar = new Seminar($course_id); - $dates = getAllSortedSingleDates($seminar); - $total = sizeof($dates); - - $events = []; - foreach (array_slice($dates, $this->offset, $this->limit) as $date) { - - // get issue titles - $issue_titles = []; - if (is_array($issues = $date->getIssueIDs())) { - foreach ($issues as $is) { - $issue = new Issue(['issue_id' => $is]); - $issue_titles[] = $issue->getTitle(); - } - } - - $room = self::getRoomForSingleDate($date); - $events[] = [ - 'event_id' => $date->getSingleDateID(), - 'start' => $date->getStartTime(), - 'end' => $date->getEndTime(), - 'title' => $date->toString(), - 'description' => implode(', ', $issue_titles), - 'categories' => $date->getTypeName() ?: '', - 'room' => $room ?: '', - 'deleted' => $date->isExTermin(), - 'canceled' => $date->isHoliday() ?: false, - ]; - } - - $this->etag(md5(serialize($events))); - - return $this->paginated($events, $total, compact('course_id')); - } - - private static function getRoomForSingleDate($val) { - - /* css-Klasse auswählen, sowie Template-Feld für den Raum mit Text füllen */ - if (Config::get()->RESOURCES_ENABLE) { - - if ($val->getResourceID()) { - $resObj = Resource::find($val->getResourceID()); - if ($resObj) { - $room_object = $resObj->getDerivedClassInstance(); - if ($room_object instanceof Room) { - $room = _("Raum: "); - $room .= $room_object->getActionURL('booking_plan'); - } - } - } else { - $room = _("keine Raumangabe"); - - if ($val->isExTermin()) { - if ($name = $val->isHoliday()) { - $room = '('.$name.')'; - } else { - $room = '('._('fällt aus').')'; - } - } - - else { - if ($val->getFreeRoomText()) { - $room = '('.htmlReady($val->getFreeRoomText()).')'; - } - } - } - } else { - $room = ''; - if ($val->getFreeRoomText()) { - $room = '('.htmlReady($val->getFreeRoomText()).')'; - } - } - - return html_entity_decode(strip_tags($room)); - } - -} diff --git a/app/routes/Feedback.php b/app/routes/Feedback.php deleted file mode 100644 index 9a28347..0000000 --- a/app/routes/Feedback.php +++ /dev/null @@ -1,271 +0,0 @@ -<?php - -namespace RESTAPI\Routes; - -/** - * @author Nils Gehrke <nils.gehrke@uni-goettingen.de> - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition feedback_id ^\d*$ - * @condition course_id ^[a-f0-9]{32}$ - * - */ -class Feedback extends \RESTAPI\RouteMap -{ - /** - * Create feedback element for a range - * - * @post /feedback/range/:range_id/:range_type - * - */ - public function createFeedbackElement($range_id, $range_type) - { - $course_id = $range_type::find($range_id)->getRangeCourseId(); - if (!\Feedback::hasRangeAccess($range_id, $range_type) || !\Feedback::hasCreatePerm($course_id)) { - $this->error(403); - } - $feedback = \FeedbackElement::build([ - 'range_id' => $range_id, - 'range_type' => $range_type, - 'user_id' => $GLOBALS['user']->id, - 'course_id' => $course_id, - 'question' => $this->data['question'], - 'description' => $this->data['description'], - 'results_visible' => intval($this->data['results_visible']), - 'commentable' => intval($this->data['commentable']), - 'mode' => $this->data['mode'] - ]); - $feedback->store(); - return $feedback->toArray(); - } - - /** - * Get a feedback element - * - * @get /feedback/:feedback_id - * - */ - public function getFeedbackElement($feedback_id) - { - if (!$feedback = \FeedbackElement::find($feedback_id)) { - $this->error(404); - } - if (!\Feedback::hasRangeAccess($feedback->range_id, $feedback->range_type)) { - $this->error(403); - } - return $feedback->toArray(); - } - - - /** - * Get all entries of a feedback element - * - * @get /feedback/:feedback_id/entries - * - */ - public function getFeedbackEntries($feedback_id) - { - if (!$feedback = \FeedbackElement::find($feedback_id)) { - $this->error(404); - } - if (!\Feedback::hasRangeAccess($feedback->range_id, $feedback->range_type)) { - $this->error(403); - } - if ($feedback->results_visible == 1 && !$feedback->isFeedbackable()) { - foreach($feedback->entries as $entry) { - $result['entries'][] = $entry->toArray(); - } - } elseif (!$feedback->isFeedbackable()) { - $result['entries'][] = $feedback->getOwnEntry()->toArray(); - } else { - $result = []; - } - - return $result; - } - - /** - * Edit a feedback element - * - * @put /feedback/:feedback_id - * - */ - public function editFeedbackElement($feedback_id) - { - if (!$feedback = \FeedbackElement::find($feedback_id)) { - $this->error(404); - } - $course_id = $feedback->course_id; - if (!\Feedback::hasRangeAccess($feedback->range_id, $feedback->range_type) || !\Feedback::hasAdminPerm($course_id)) { - $this->error(403); - } - $feedback->question = $this->data['question'] !== null ? $this->data['question'] : $feedback->question; - $feedback->description = $this->data['description'] !== null ? $this->data['description'] : $feedback->description; - $feedback->results_visible = $this->data['results_visible'] !== null ? - intval($this->data['results_visible']) : $feedback->results_visible; - $feedback->store(); - return $feedback->toArray(); - } - - /** - * Delete a feedback element - * - * @delete /feedback/:feedback_id - * - */ - public function deleteFeedbackElement($feedback_id) - { - if (!$feedback = \FeedbackElement::find($feedback_id)) { - $this->error(404); - } - $course_id = $feedback->course_id; - if (!\Feedback::hasRangeAccess($feedback->range_id, $feedback->range_type) || !\Feedback::hasAdminPerm($course_id)) { - $this->error(403); - } - $feedback->delete(); - $this->halt(200); - } - - /** - * List all feedback elements for a range - * - * @get /feedback/range/:range_id/:range_type - * - * @param string $range_id - * @param string $range_type - */ - public function getFeedbackElementsForRange($range_id, $range_type) - { - if (!\Feedback::hasRangeAccess($range_id, $range_type)) { - $this->error(403, 'You may not access the given range object.'); - } - $feedback_elements = \FeedbackElement::findBySQL('range_id = ? AND range_type = ? ORDER BY mkdate DESC', [$range_id, $range_type]); - foreach($feedback_elements as $feedback) { - $result['feedback_elements'][] = $feedback->toArray(); - } - return $result; - } - - /** - * List all feedback elements of a course - * - * @get /course/:course_id/feedback - * - */ - public function getFeedbackElementsForCourse($course_id) - { - if (!\Feedback::hasAdminPerm($course_id)) { - $this->error(403, 'You may not list all feedback elements of the course. Only feedback admins can.'); - } - $feedback_elements = \FeedbackElement::findBySQL('course_id = ? ORDER BY mkdate DESC', [$course_id]); - foreach($feedback_elements as $feedback) { - $result['feedback_elements'][] = $feedback->toArray(); - } - return $result; - } - - /** - * add an entry for a feedback element - * - * @post /feedback/:feedback_id/entry - * - */ - public function addFeedbackEntry($feedback_id) - { - if (!$feedback = \FeedbackElement::find($feedback_id)) { - $this->error(404); - } - if (!$feedback->isFeedbackable()) { - $this->error(403, 'You may not add an entry here. Maybe you have already given feedback or you are the author of the feedback element.'); - } - $entry = \FeedbackEntry::build([ - 'feedback_id' => $feedback->id, - 'user_id' => $GLOBALS['user']->id - ]); - - $entry->rating = $this->getRating( - $feedback->mode, - (int) $this->data['rating'] - ); - - if ($feedback->commentable) { - $entry->comment = $this->data['comment']; - } - - $entry->store(); - return $entry->toArray(); - } - - /** - * edit an entry of a feedback element - * - * @put /feedback/entry/:entry_id - * - */ - public function editFeedbackEntry($entry_id) - { - $entry = \FeedbackEntry::find($entry_id); - - if (!$entry) { - $this->notFound(); - } - - if (!$entry->isEditable()) { - $this->error(403); - } - - $entry->rating = $this->getRating( - $entry->feedback->mode, - (int) $this->data['rating'] - ); - - if ($entry->feedback->commentable) { - $entry->comment = $this->data['comment'] ?? $entry->comment; - } - - $entry->store(); - return $entry->toArray(); - } - - /** - * delete an entry of a feedback element - * - * @delete /feedback/entry/:entry_id - * - */ - public function deleteFeedbackEntry($entry_id) - { - if (!$entry = \FeedbackEntry::find($entry_id)) { - $this->error(404); - } - if ($entry->delete()){ - $this->halt(200); - } - } - - /** - * @param int $mode - * @param int $rating - * @return int - */ - private function getRating(int $mode, int $rating): int - { - if ($mode === 0) { - return 0; - } - - if ($rating === 0) { - return 1; - } - - if ($mode === 1) { - return min(5, $rating); - } - - if ($mode === 2) { - return min(10, $rating); - } - - throw new \InvalidArgumentException("Invalid mode {$mode}"); - } -} diff --git a/app/routes/FileSystem.php b/app/routes/FileSystem.php deleted file mode 100644 index 9abd713..0000000 --- a/app/routes/FileSystem.php +++ /dev/null @@ -1,684 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * This class implements REST routes for the new Stud.IP file system. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @license GNU General Public License Version 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * Partially based upon the Files.php source code from Jan-Hendrik Willms - * (tleilax+studip@gmail.com) and mluzena@uos.de which is also - * licensed under the terms of the GNU General Public License Version 2 - * or later. - */ - -class FileSystem extends \RESTAPI\RouteMap -{ - // FILE REFERENCE AND FILE ROUTES: - - /** - * Get a file reference object (metadata) - * @get /file/:file_ref_id - */ - public function getFileRef($file_ref_id) - { - return $this->filerefToJSON( - $this->requireFileRef($file_ref_id), - (bool) \Request::int('extended') - ); - } - - /** - * Get the data of a file by the ID of an associated FileRef object - * - * @get /file/:file_ref_id/download - */ - public function getFileRefData($file_ref_id) - { - $file_ref = $this->requireFileRef($file_ref_id); - - // check if the current user has the permissions to read this file reference: - $user = \User::findCurrent(); - if (!$file_ref->folder->getTypedFolder()->isFileDownloadable($file_ref_id, $user->id)) { - $this->error(403, "You may not download the file reference with the id {$file_ref_id}"); - } - - // check if file exists: - if (!$file_ref->file) { - $this->error(500, 'File reference has no associated file object!'); - } - - $data_path = $file_ref->file->getPath(); - if (!file_exists($data_path)) { - $this->error(500, "File was not found in the operating system's file system!"); - } - - $this->lastModified($file_ref->file->chdate); - $this->sendFile($data_path, ['filename' => $file_ref->name]); - } - - /** - * Update file data using a FileReference to it. - * - * @post /file/:file_ref_id/update - */ - public function updateFileData($file_ref_id) - { - // We only update the first file: - $uploaded_file = array_shift($this->data['_FILES']); - - // FileManager::updateFileRef handles the whole file upload - // and does all the necessary security checks: - $result = \FileManager::updateFileRef( - $this->requireFileRef($file_ref_id), - \User::findCurrent(), - $uploaded_file, - true, - false - ); - - if (!$result instanceof \FileRef) { - $this->error(500, 'Error while updating a file reference: ' . implode(' ', $result)); - } - - return $this->filerefToJSON($result); - } - - /** - * Edit a file reference. - * - * @put /file/:file_ref_id - */ - public function editFileRef($file_ref_id) - { - $result = \FileManager::editFileRef( - $this->requireFileRef($file_ref_id), - \User::findCurrent(), - $this->data['name'], - $this->data['description'], - $this->data['content_term_of_use_id'], - $this->data['license'] - ); - - if (!$result instanceof \FileRef) { - $this->error(500, 'Error while editing a file reference: ' . implode(' ', $result)); - } - - return $this->filerefToJSON($result); - } - - /** - * Copies a file reference. - * - * @post /file/:file_ref_id/copy/:destination_folder_id - */ - public function copyFileRef($file_ref_id, $destination_folder_id) - { - $result = \FileManager::copyFile( - $this->requireFileRef($file_ref_id)->getFileType(), - $this->requireFolder($destination_folder_id)->getTypedFolder(), - \User::findCurrent() - ); - - if (!($result instanceof \FileType)) { - $this->error(500, 'Error while copying a file reference: ' . implode(' ', $result)); - } - - return $this->filerefToJSON($result->getFileRef()); - } - - /** - * Moves a file reference. - * - * @post /file/:file_ref_id/move/:destination_folder_id - */ - public function moveFileRef($file_ref_id, $destination_folder_id) - { - $result = \FileManager::moveFile( - $this->requireFileRef($file_ref_id)->getFileType(), - $this->requireFolder($destination_folder_id)->getTypedFolder(), - \User::findCurrent() - ); - - if (!($result instanceof \FileType)) { - $this->error(500, 'Error while moving a file reference: ' . implode(' ', $result)); - } - - return $this->filerefToJSON($result->getFileRef()); - } - - /** - * Deletes a file reference. - * - * @delete /file/:file_ref_id - */ - public function deleteFileRef($file_ref_id) - { - $result = \FileManager::deleteFileRef( - $this->requireFileRef($file_ref_id), - \User::findCurrent() - ); - - if (!$result instanceof \FileRef) { - $this->error(500, 'Error while deleting a file reference: ' . implode(' ', $result)); - } - - $this->halt(200); - } - - /** - * Upload file to given folder. - * file data has to be attached as multipart/form-data - * - * @post /file/:folder_id - */ - public function uploadFile($folder_id) - { - $typed_folder = $this->requireFolder($folder_id)->getTypedFolder(); - if (isset($this->data['_FILES'])) { - $file_data = array_map(function ($a) { - return is_array($a) ? $a : [$a]; - }, array_shift($this->data['_FILES'])); - } - if (is_array($file_data)) { - $validated_files = \FileManager::handleFileUpload( - $file_data, - $typed_folder, - $this->requireUser()->id - ); - - if (count($validated_files['error']) > 0) { - $this->error(500, 'Error while uploading files: ' . implode(' ', $validated_files['error'])); - } - - $uploaded_files = \SimpleCollection::createFromArray($validated_files['files']); - $default_license = \ContentTermsOfUse::findDefault(); - $uploaded_files->setValue('content_terms_of_use_id', $default_license->id); - $uploaded_files->store(); - if (count($uploaded_files) === 1) { - $result = $this->filerefToJSON($uploaded_files->first()); - } else { - $result = $uploaded_files->map(function ($f) { - return $this->filerefToJSON($f); - }); - } - $this->halt(201, [], $result); - } else { - $this->error(400, 'No files found in request.'); - } - } - - // FOLDER ROUTES: - - /** - * Returns a list of defined folder types, separated by range type. - * @get /studip/file_system/folder_types - */ - public function getDefinedFolderTypes() - { - return \FileManager::getFolderTypes(); - } - - /** - * Get a folder object with its file references, subdirectories and the permissions for the user who has made the API call. - * @get /folder/:folder_id - */ - public function getFolder($folder_id) - { - return $this->folderToJSON( - $this->requireFolder($folder_id), - true - ); - } - - /** - * Creates a new folder inside of another folder and returns the new object on success. - * @post /folder/:parent_folder_id/new_folder - */ - public function createNewFolder($parent_folder_id) - { - $user = \User::findCurrent(); - $parent = $this->requireTypedFolder($parent_folder_id); - - if (!$parent->isWritable($user->id)) { - $this->error(403, 'You are not permitted to create a subfolder in the parent folder!'); - } - - $result = \FileManager::createSubFolder( - $parent, - $user, - 'StandardFolder', //to be extended - $this->data['name'], - $this->data['description'] - ); - - if (!$result instanceof \FolderType) { - $this->error(500, 'Error while creating a folder: ' . implode(' ', $result)); - } - - return $this->folderToJSON( - $this->requireFolder($result->getId()) - ); - } - - /** - * Get a list with all FileRef objects of a folder. - * @get /folder/:folder_id/files - */ - public function getFileRefsOfFolder($folder_id) - { - $folder = $this->requireFolder($folder_id); - - $query = "folder_id = :folder_id ORDER BY name ASC"; - $parameters[':folder_id'] = $folder->id; - - if ($this->limit || $this->offset) { - $query .= " LIMIT :limit OFFSET :offset"; - $parameters[':limit'] = $this->limit; - $parameters[':offset'] = $this->offset; - } - - $file_refs = \FileRef::findAndMapBySql(function (\FileRef $ref) { - return $this->filerefToJSON($ref); - }, $query, $parameters); - - return $this->paginated( - $file_refs, - \FileRef::countByFolder_id($folder->id), - ['folder_id' => $folder->id] - ); - } - - - /** - * Get a list with all FileRef objects of a folder. - * @get /folder/:folder_id/subfolders - */ - public function getSubfoldersOfFolder($folder_id) - { - $user = $this->requireUser(); - $folder = $this->requireFolder($folder_id); - - $query = "parent_id = :parent_id ORDER BY name ASC"; - $parameters = [':parent_id' => $folder->id]; - - if ($this->limit || $this->offset) { - $query .= " LIMIT :limit OFFSET :offset"; - $parameters[':limit'] = $this->limit; - $parameters[':offset'] = $this->offset; - } - - $subfolders = \Folder::findAndMapBySql(function (\Folder $subfolder) use ($user) { - $type = $subfolder->getTypedFolder(); - if (!$type || !$type->isVisible($user->id)) { - return false; - } - return $this->folderToJSON($subfolder); - }, $query, $parameters); - - return $this->paginated( - array_filter($subfolders), - \Folder::countByParent_id($folder_id), - ['folder_id' => $folder_id] - ); - } - - /** - * Get a list with permissions the current user has for a folder. - * @get /folder/:folder_id/permissions - */ - public function getFolderPermissions($folder_id) - { - $user = $this->requireUser(); - $folder = $this->requireFolder($folder_id); - - // read permissions of the user and return them: - return array_merge([ - 'folder_id' => $folder->id, - 'user_id' => $user->id, - ], $this->folderPermissionsToJSON($folder)); - } - - /** - * Allows editing the name or the description (or both) of a folder. - * - * @put /folder/:folder_id - */ - public function editFolder($folder_id) - { - if (isset($this->data['name']) && !$this->data['name']) { - $this->error(400, "The name for the folder with the id {$folder_id} must not be empty!"); - } - - $user = $this->requireUser(); - $typed_folder = $this->requireTypedFolder($folder_id); - - if (!$typed_folder->isEditable($user->id)) { - $this->error(403, "You may not edit the folder with id {$folder_id}!"); - } - - if (!$typed_folder instanceof \StandardFolder) { - $this->error(501, "Editing is only allowed for folders of type StandardFolder for now!"); - } - - if ($this->data['name']) { - $typed_folder->name = $this->data['name']; - } - if (isset($this->data['description'])) { - $typed_folder->description = $this->data['description'] ?: ''; - } - - if (!$typed_folder->store()) { - $this->error(500, "Could not store folder with id {$folder_id}!"); - } - - return $this->folderToJSON( - $this->requireFolder($folder_id) - ); - } - - /** - * Copies a folder into another folder. - * - * @post /folder/:folder_id/copy/:destination_folder_id - */ - public function copyFolder($folder_id, $destination_folder_id) - { - $result = \FileManager::copyFolder( - $this->requireTypedFolder($folder_id), - $this->requireTypedFolder($destination_folder_id), - \User::findCurrent() - ); - - if (!$result instanceof \FolderType) { - $this->error(500, 'Error while copying a folder: ' . implode(' ', $result)); - } - - return $this->folderToJSON( - $this->requireFolder($result->getId()) - ); - } - - - /** - * Move a folder into another folder. - * @post /folder/:folder_id/move/:destination_folder_id - */ - public function moveFolder($folder_id, $destination_folder_id) - { - $result = \FileManager::moveFolder( - $this->requireTypedFolder($folder_id), - $this->requireTypedFolder($destination_folder_id), - \User::findCurrent() - ); - - if (!$result instanceof \FolderType) { - $this->error(500, 'Error while moving a folder: ' . implode(' ', $result)); - } - - return $this->folderToJSON( - $this->requireFolder($folder_id) - ); - } - - - /** - * Deletes a folder. - * - * @delete /folder/:folder_id - */ - public function deleteFolder($folder_id) - { - $result = \FileManager::deleteFolder( - $this->requireTypedFolder($folder_id), - \User::findCurrent() - ); - - if (!$result instanceof \FolderType) { - $this->error(500, 'Error while deleting a folder: ' . implode(' ', $result)); - } - - $this->halt(200); - } - - // RELATED OBJECT ROUTES: - - /** - * Get a collection of all ContentTermsOfUse objects - * - * @get /studip/content_terms_of_use_list - */ - public function getContentTermsOfUseList() - { - $objects = \ContentTermsOfUse::findBySql( - '1 ORDER BY name ASC LIMIT :limit OFFSET :offset', - ['limit' => $this->limit, 'offset' => $this->offset] - ); - - return $this->paginated( - array_map([$this, 'termsOfUseToJSON'], $objects), - \ContentTermsOfUse::countBySql('1') - ); - } - - // UTILITY METHODS - - /** - * Requires a valid user object. - * @return \User object - */ - private function requireUser() - { - return \User::findCurrent(); - } - - /** - * Requires a valid file reference object - * @param mixed $id_or_object Either a file reference id or object - * @return \FileRef object - */ - private function requireFileRef($id_or_object) - { - if ($id_or_object instanceof \FileRef) { - $file_ref = $id_or_object; - } else { - //check if the file_id references a file reference object: - $file_ref = \FileRef::find($id_or_object); - if (!$file_ref) { - $this->notFound("File reference with id {$id_or_object} not found!"); - } - } - - // check if the file reference is placed inside a folder. - // (must be present to check for permissions) - if (!$file_ref->folder) { - $this->error(500, "File reference with id {$file_ref->id} has no folder!"); - } - - $typed_folder = $file_ref->folder->getTypedFolder(); - if (!$typed_folder) { - $this->error(500, "The folder of file reference with id {$file_ref->id} has no folder type!"); - } - - //check if the current user has the permissions to read this file reference: - if (!$typed_folder->isReadable($this->requireUser()->id)) { - $this->error(403, "You are not permitted to read the file reference with id {$file_ref->id}!"); - } - - return $file_ref; - } - - /** - * Converts a file reference object to JSON. - * @param \FileRef $ref File reference object - * @param boolean $extended Extended output? (includes folder, owner and terms of use) - * @return array representation for json encoding - */ - private function filerefToJSON(\FileRef $ref, $extended = false) - { - $user = $this->requireUser(); - $typed_folder = $ref->folder->getTypedFolder(); - $filetype = $ref->getFileType(); - - $result = array_merge($ref->toRawArray(), [ - 'size' => (int) $ref->file->size, - 'mime_type' => $ref->file->mime_type, - 'storage' => $ref->file->filetype === "URLFile" ? "url" : "disk", - - 'is_readable' => $typed_folder->isReadable($user->id), - 'is_downloadable' => $filetype->isDownloadable($user->id), - 'is_editable' => $filetype->isEditable($user->id), - 'is_writable' => $filetype->isWritable($user->id), - ]); - - $result['downloads'] = (int) $result['downloads']; - $result['mkdate'] = (int) $result['mkdate']; - $result['chdate'] = (int) $result['chdate']; - - if ($result['storage'] === 'url') { - $result['url'] = $ref->getFileType()->getDownloadURL(); - } - - if ($extended) { - //folder does exist (since we checked for its existence above) - $result['folder'] = $this->folderToJSON($ref->folder); - - if ($ref->owner) { - $result['owner'] = User::getMiniUser($this, $ref->owner); - } - - //$result['license'] = $file_ref->license; //to be activated when licenses are defined - - if ($ref->terms_of_use) { - $result['terms_of_use'] = $this->termsOfUseToJSON($ref->terms_of_use); - } - } - - return $result; - } - - /** - * Requires a valid folder object - * @param mixed $id_or_object Either a folder id or object - * @return Folder object - */ - private function requireFolder($id_or_object) - { - if ($id_or_object instanceof \Folder) { - $folder = $id_or_object; - } else { - $folder = \Folder::find($id_or_object); - if (!$folder) { - $this->notFound("Folder with id {$id_or_object} not found!"); - } - } - - $typed_folder = $folder->getTypedFolder(); - if (!$typed_folder) { - $this->error(500, "Cannot find folder type of folder with id {$folder->id}!"); - return; - } - - if (!$typed_folder->isReadable($this->requireUser()->id)) { - $this->error(403, "You are not allowed to read the contents of the folder with the id {$folder->id}!"); - } - - return $folder; - } - - /** - * Requires a valid typed folder object - * @param mixed $id_or_object Either a folder id or object - * @return FolderType instance - */ - private function requireTypedFolder($id_or_object) - { - return $this->requireFolder($id_or_object)->getTypedFolder(); - } - - /** - * Converts a given folder to JSON. - * @param Folder $folder Folder object - * @param boolean $extended Extended output? (includes subfolders and file references) - * @return array representation for json encoding - */ - private function folderToJSON(\Folder $folder, $extended = false) - { - $result = $this->folderPermissionsToJSON($folder); - - if ($result['is_readable']) { - $result = array_merge($folder->toRawArray(), $result); - - $result['mkdate'] = (int) $result['mkdate']; - $result['chdate'] = (int) $result['chdate']; - - //The field "data_content" must be handled differently - //than the other fields since it contains JSON data. - $data_content = json_decode($folder->data_content); - $result['data_content'] = $data_content; - - if ($extended) { - $user = $this->requireUser(); - - $result['subfolders'] = []; - foreach ($folder->subfolders as $subfolder) { - if (!$subfolder->getTypedFolder()->isVisible($user->id)) { - continue; - } - $result['subfolders'][] = $this->folderToJSON($subfolder); - } - - $result['file_refs'] = []; - foreach ($folder->getTypedFolder()->getFiles() as $file) { - if (method_exists($file,"getFileRef")) { - $result['file_refs'][] = $this->filerefToJSON( - $file->getFileRef() - ); - } - } - } - } - - return $result; - } - - /** - * Converts permissions of a folder to JSON. - * @param Folder $folder Folder object - * @param User $user User object to check permissions against - * @return array representation for json encoding - */ - private function folderPermissionsToJSON(\Folder $folder) - { - $user = $this->requireUser(); - $type = $folder = $folder->getTypedFolder(); - if (!$type) { - $this->error(500, 'Folder type not found!'); - } - - return [ - 'is_visible' => $type->isVisible($user->id), - 'is_readable' => $type->isReadable($user->id), - 'is_writable' => $type->isWritable($user->id), - ]; - } - - /** - * Converts a terms of use object to JSON. - * @param ContentTermsOfUse $object Object - * @return array representation for json encoding - */ - private function termsOfUseToJSON(\ContentTermsOfUse $object) - { - $result = $object->toRawArray(); - - $result['is_default'] = (bool) $result['is_default']; - - $result['mkdate'] = (int) $result['mkdate']; - $result['chdate'] = (int) $result['chdate']; - - return $result; - } -} diff --git a/app/routes/Forum.php b/app/routes/Forum.php deleted file mode 100644 index 35aad91..0000000 --- a/app/routes/Forum.php +++ /dev/null @@ -1,419 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition course_id ^[a-f0-9]{1,32}$ - */ -class Forum extends \RESTAPI\RouteMap -{ - /** - * List all categories of a forum - * - * @get /course/:course_id/forum_categories - */ - public function getForumCategories($course_id) - { - if (!\ForumPerm::has('view', $course_id)) { - $this->error(401); - } - - $categories = \ForumCat::findBySeminar_id($course_id, 'ORDER BY pos ASC'); - $total = sizeof($categories); - $categories = array_splice($categories, (int)$this->offset, (int)$this->limit ?: 10); - - $json = []; - foreach ($categories as $cat) { - $json_cat = $cat->toArray(); - $uri = $this->urlf('/forum_category/%s', [htmlReady($json_cat['category_id'])]); - $json_cat['course_id'] = $json_cat['seminar_id']; - $json[$uri] = $this->categoryToJson($json_cat); - } - - $this->etag(md5(serialize($json))); - - return $this->paginated($json, $total, compact('course_id')); - } - - /** - * Create a new category - * - * @post /course/:course_id/forum_categories - */ - public function createForumCategory($course_id) - { - if (!\ForumPerm::has("add_category", $course_id)) { - $this->error(401); - } - - if (!isset($this->data['name']) || !mb_strlen($name = trim($this->data['name']))) { - $this->error(400, 'Category name required.'); - } - - $category_id = \ForumCat::add($course_id, $name); - if (!$category_id) { - $this->error(500, 'Error creating the forum category.'); - } - - $this->redirect('forum_category/' . $category_id, 201, 'ok'); - } - - /** - * Read a category - * - * @get /forum_category/:category_id - */ - public function getForumCategory($category_id) - { - $category = $this->findCategory($category_id); - $cid = $category['course_id']; - - if (!\ForumPerm::has('view', $cid)) { - $this->error(401); - } - - $category_json = $this->categoryToJson($category); - $this->etag(md5(serialize($category_json))); - return $category_json; - } - - /** - * Update a category - * - * @put /forum_category/:category_id - */ - public function updateForumCategory($category_id) - { - $category = $this->findCategory($category_id); - - if (!\ForumPerm::has("edit_category", $category['course_id'])) { - $this->error(401); - } - - if (!isset($this->data['name']) || !mb_strlen($name = trim($this->data['name']))) { - $this->error(400, 'Category name required.'); - } - - \ForumCat::setName($category_id, $this->data['name']); - - $this->status(204); - } - - /** - * Delete a category - * - * @delete /forum_category/:category_id - */ - public function deleteForumCategory($category_id) - { - $category = $this->findCategory($category_id); - $cid = $category['course_id']; - - if (!\ForumPerm::has("remove_category", $cid)) { - $this->error(401); - } - - \ForumCat::remove($category_id, $cid); - - $this->status(204); - } - - /** - * Show entries of a category - * - * @get /forum_category/:category_id/areas - */ - public function getCategoryEntries($category_id) - { - $category = $this->findCategory($category_id); - - if (!\ForumPerm::has('view', $category['course_id'])) { - $this->error(401); - } - - $areas = $this->getAreas($category_id, $this->offset, $this->limit); - - $this->etag(md5(serialize($areas))); - return $this->paginated($areas, $this->countAreas($category_id), compact('category_id')); - } - - - - /** - * Add a new forum entry to an existing one - * - * @post /forum_category/:category_id/areas - */ - public function appendForumEntry($category_id) - { - $category = $this->findCategory($category_id); - $cid = $category['course_id']; - - if (!\ForumPerm::has('add_area', $cid)) { - $this->error(401); - } - - if (!isset($this->data['subject']) || !mb_strlen($subject = trim($this->data['subject']))) { - $this->error(400, 'Subject required.'); - } - - if (!isset($this->data['content'])) { - $this->error(400, 'Content required.'); - } - $content = trim($this->data['content']); - - $anonymous = isset($this->data['anonymous']) ? intval($this->data['anonymous']) : 0; - - $entry_id = $this->createEntry($cid, $cid, $subject, $content, $anonymous); - - \ForumCat::addArea($category_id, $entry_id); - - $this->redirect('forum_entry/' . $entry_id, 201, "ok"); - } - - /** - * Get a forum entry - * - * @get /forum_entry/:entry_id - */ - public function getForumEntry($entry_id) - { - $entry = \ForumEntry::getConstraints($entry_id); - $cid = $entry['seminar_id']; - - if (!\ForumPerm::has('view', $cid)) { - $this->error(401); - } - - $entry = $this->findEntry($entry_id); - $this->lastmodified($entry->chdate); - $this->etag(md5(serialize($entry))); - return $entry; - } - - /** - * Add a new forum entry to an existing one - * - * @post /forum_entry/:entry_id - */ - public function addForumEntry($parent_id) - { - $parent = \ForumEntry::getConstraints($parent_id); - $cid = $parent['seminar_id']; - - $perm = self::isArea($parent) ? 'add_area' : 'add_entry'; - - if (!\ForumPerm::has($perm, $cid)) { - $this->error(401); - } - - $subject = (string) trim($this->data['subject']); - $content = (string) trim($this->data['content']); - - // areas and threads need a subject, postings do not - if ($parent['depth'] < 3 && !$subject) { - $this->error(400, 'Subject required.'); - } - - // all entries besides the area need content - if ($parent['depth'] > 1 && !$content) { - $this->error(400, 'Content required.'); - } - - if ($parent['depth'] >= 3 && $subject) { - $this->error(400, 'Must not have subject here.'); - } - - $anonymous = isset($this->data['anonymous']) ? (int) $this->data['anonymous'] : 0; - - $entry_id = $this->createEntry($parent_id, $cid, $subject, $content, $anonymous); - - $this->redirect('forum_entry/' . $entry_id, 201, "ok"); - } - - /** - * Update an existing one forum entry - * - * @put /forum_entry/:entry_id - */ - public function updateForumEntry($entry_id) - { - $entry = \ForumEntry::getConstraints($entry_id); - $cid = $entry['seminar_id']; - - $perm = self::isArea($entry) ? 'edit_area' : 'edit_entry'; - - if (!\ForumPerm::hasEditPerms($entry_id) || !\ForumPerm::has($perm, $cid)) { - $this->error(401); - } - - $subject = (string) trim($this->data['subject']); - $content = (string) trim($this->data['content']); - - // areas and threads need a subject, postings do not - if ($entry['depth'] < 3 && !$subject) { - $this->error(400, 'Subject required.'); - } - - // all entries besides the area need content - if ($entry['depth'] > 1 && !$content) { - $this->error(400, 'Content required.'); - } - - if ($entry['depth'] >= 3 && $subject) { - $this->error(400, 'Must not have subject here.'); - } - - \ForumEntry::update($entry_id, $subject, $content); - - $this->status(204); - } - - /** - * Delete an entry - * - * @delete /forum_entry/:entry_id - */ - public function deleteForumEntry($entry_id) - { - $entry = \ForumEntry::getConstraints($entry_id); - $cid = $entry['seminar_id']; - - if (!\ForumPerm::hasEditPerms($entry_id) || !\ForumPerm::has('remove_entry', $cid)) { - $this->error(401); - } - - \ForumEntry::delete($entry_id); - - $this->status(204); - } - - /********************* - * * - * PRIVATE FUNCTIONS * - * * - *********************/ - - - private function findEntry($entry_id) - { - $raw = \ForumEntry::getConstraints($entry_id); - if ($raw === false) { - $this->notFound(); - } - - $entry = $this->convertEntry($raw); - - $children = \ForumEntry::getEntries($entry_id, \ForumEntry::WITHOUT_CHILDS, '', 'ASC', 0, false); - - if (isset($children['list'][$entry_id])) { - unset($children['list'][$entry_id]); - } - - $entry['children'] = []; - foreach (array_values($children['list']) as $childentry) { - $entry['children'][] = $this->convertEntry($childentry); - } - - return $entry; - } - - public function convertEntry($raw) - { - $entry = []; - foreach(words("topic_id mkdate chdate anonymous depth") as $key) { - $entry[$key] = $raw[$key]; - } - - $hide_user = $entry['anonymous'] && $raw['user_id'] !== $GLOBALS['user']->id; - - $entry['subject'] = $raw['name']; - $entry['user'] = $hide_user ? null : $this->urlf('/user/%s', [$raw['user_id']]); - $entry['course'] = $this->urlf('/course/%s', [$raw['seminar_id']]); - $entry['content_html'] = \ForumEntry::getContentAsHtml($raw['content']); - $entry['content'] = \ForumEntry::killEdit($raw['content']); - - return $entry; - } - - - private static function isArea($entry) - { - return 1 === $entry['depth']; - } - - private function createEntry($parent_id, $course_id, $subject, $content, $anonymous) - { - $topic_id = self::generateID(); - - $data = [ - 'topic_id' => $topic_id, - 'seminar_id' => $course_id, - 'user_id' => $GLOBALS['user']->id, - 'name' => $subject, - 'content' => $content, - 'author' => $GLOBALS['user']->getFullName(), - 'author_host' => $_SERVER['REMOTE_ADDR'], - 'anonymous' => (int) $anonymous - ]; - \ForumEntry::insert($data, $parent_id); - - return $topic_id; - } - - private function findCategory($category_id) - { - $result = []; - - if ($cat = \ForumCat::get($category_id)) { - $result = $cat; - $result['course_id'] = $cat['seminar_id']; - $result['name'] = $cat['entry_name']; - } else { - $this->error(404); - } - - return $result; - } - - private function categoryToJson($category) - { - $json = $category; - - $json['course'] = $this->urlf('/course/%s', [htmlReady($json['course_id'])]); - unset($json['course_id']); - - $json['areas'] = $this->urlf('/forum_category/%s/areas', [$json['category_id']]); - $json['areas_count'] = $this->countAreas($json['category_id']); - - return $json; - } - - private function countAreas($category_id) - { - return sizeof(\ForumCat::getAreas($category_id)); - } - - private function getAreas($category_id, $offset = 0, $limit = 10) - { - $offset = (int) $offset; - $limit = (int) $limit; - - $areas = []; - - foreach (\ForumCat::getAreas($category_id, $offset, $limit) as $area) { - $url = $this->urlf('/forum_entry/%s', [htmlReady($area['topic_id'])]); - $areas[$url] = $this->convertEntry($area); - } - - return $areas; - } - - private static function generateID() - { - return md5(uniqid(rand())); - } -} diff --git a/app/routes/Messages.php b/app/routes/Messages.php deleted file mode 100644 index db9cb2e..0000000 --- a/app/routes/Messages.php +++ /dev/null @@ -1,301 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition message_id ^[a-f0-9]{1,32}$ - * @condition user_id ^[a-f0-9]{1,32}$ - * @condition box ^(inbox|outbox)$ - */ -class Messages extends \RESTAPI\RouteMap -{ - /** - * Liefert die Anzahl der vorhandenen Nachrichten des autorisierten Nutzers - * zurück. Der Parameter bestimmt je nach Wert, auf welchen Bereich - * (Posteingang bzw. Postausgang) zugegriffen werden soll. - * Die Rückgabe beinhaltet jeweils die Anzahl aller Nachrichten sowie die - * Anzahl der ungelesenen Nachrichten. - * - * @head /user/:user_id/:box - */ - public function indexOfMessages($user_id, $box) - { - if ($user_id !== self::currentUser()) { - $this->error(401); - } - - $count = $this->countMessages($user_id, $box); - - $this->headers([ - 'X-Messages-Total' => $count['total'], - 'X-Messages-Unread' => $count['unread'], - ]); - - return null; - } - - /** - * Liefert die vorhandenen Nachrichten des autorisierten Nutzers zurück. - * - * @get /user/:user_id/:box - */ - public function getMessages($user_id, $box) - { - if ($user_id !== self::currentUser()) { - $this->error(401); - } - - $ids = $this->getMessageIds($user_id, $box); - $total = count($ids); - - $ids = array_slice($ids, $this->offset, $this->limit); - - $messages = []; - if (count($ids) > 0) { - \Message::findEachMany(function ($message) use (&$messages) { - $url = $this->urlf('/message/%s', $message->id); - $messages[$url] = $this->messageToJSON($message); - }, $ids, 'ORDER BY mkdate DESC'); - } - - return $this->paginated($messages, $total, compact('user_id', 'box')); - } - - /** - * Liefert die Daten der angegebenen Nachricht zurück. - * - * @get /message/:message_id - */ - public function showMessage($message_id) - { - $message = $this->requireMessage($message_id); - $message_json = $this->messageToJSON($message); - $this->etag(md5(serialize($message_json))); - return $message_json; - } - - - /** - * Get the root file folder of a message. The root file folder contains all - * files that were appended to the message. - * - * @get /message/:message_id/file_folder - */ - public function getTopFolder($message_id) - { - //first we check if the user exists: - $message = \Message::find($message_id); - - $user = \User::findCurrent(); - - if (!$user) { - $this->halt(404, 'User not found!'); - } - - if(!$message->permissionToRead($user->id)) { - $this->halt(403, 'You are not allowed to read this message or its appended files!'); - } - - //we can get the top folder: - $top_folder = \Folder::findTopFolder($message->id, 'message'); - - if($top_folder) { - $file_system_api = new FileSystem(); - return $file_system_api->getFolder($top_folder->id); - } else { - $this->halt(404, 'Folder not found!'); - } - } - - - /** - * Schreibt eine neue Nachricht. - * - * @post /messages - */ - public function createMessage() - { - if (!mb_strlen($subject = trim($this->data['subject'] ?: ''))) { - $this->error(400, 'No subject provided'); - } - - if (!mb_strlen($message = trim($this->data['message'] ?: ''))) { - $this->error(400, 'No message provided'); - } - - $recipients = (array) ($this->data['recipients'] ?: null); - if (!sizeof($recipients)) { - $this->error(400, 'No recipient(s) provided'); - } - - $usernames = array_map(function ($id) { $user = \User::find($id); return @$user['username']; }, $recipients); - - if (sizeof($usernames) !== sizeof(array_filter($usernames))) { - $this->error(400, "Some recipients do not exist."); - } - - $message = \Message::send($GLOBALS['user']->id, $usernames, $subject, $message); - if (!$message) { - $this->error(500, 'Could not create message'); - } - - $this->redirect('message/' . $message->id, 201, "ok"); - } - - - /** - * Eine Nachricht als (un)gelesen markieren. - * - * @put /message/:message_id - */ - public function updateMessage($message_id) - { - - $message = $this->requireMessage($message_id); - $user_id = $this->currentUser(); - - if (isset($this->data['unread'])) { - if ($this->data['unread']) { - $message->markAsUnread($user_id); - } else { - $message->markAsRead($user_id); - } - } - - $this->halt(204); - } - - /** - * Löscht eine Nachricht. - * - * @delete /message/:message_id - */ - public function destroyMessage($message_id) - { - $message = $this->requireMessage($message_id); - - $msgin = new \messaging(); - if (!$msgin->delete_message($message_id, self::currentUser(), true)) { - $this->error(500); - } - - $this->status(204); - } - - /**************************************************/ - /* PRIVATE HELPER METHODS */ - /**************************************************/ - - private static function currentUser() - { - return $GLOBALS['user']->id; - } - - private function requireMessage($message_id) - { - if (!$message = \Message::find($message_id)) { - $this->notFound("Message not found"); - } - - $current_user = self::currentUser(); - $message_user = $message->originator->user_id === $current_user - ? $message->originator - : $message->receivers->findOneBy('user_id', $current_user); - - if (!$message_user) { - $this->error(401); - } - - if ($message_user->deleted) { - $this->notFound("Message not found"); - } - - return $message; - } - - private function messageToJSON($message) - { - $user_id = self::currentUser(); - - $my_mu = $message->receivers->filter(function ($mu) use ($user_id) { - return $mu->user_id === $user_id; - }); - if ($message->originator->user_id === $user_id) { - $my_mu[] = $message->originator; - } - - $my_roles = [ - 'snd' => $message->autor_id === $user_id, - 'rec' => in_array('rec', $my_mu->pluck('snd_rec')), - ]; - - $json = $message->toArray(words('message_id subject message mkdate priority')); - - // formatted message - $json['message_html'] = formatReady($json['message']) ?: ''; - - // Tags - $json['tags'] = $message->getTags($user_id); - - // sender - $sender = $message->getSender(); - $json['sender'] = $this->urlf('/user/%s', [$message->author->id]); - - // recipients - if ($my_roles['snd']) { - $json['recipients'] = []; - foreach ($message->getRecipients() as $r) { - $json['recipients'][] = $this->urlf('/user/%s', [$r->user_id]); - } - } else { - $json['recipients'] = [$this->urlf('/user/%s', [$user_id])]; - } - - // attachments - if ($message->attachment_folder && count($message->attachment_folder->file_refs) > 0) { - $json['attachments'] = []; - foreach ($message->attachment_folder->file_refs as $ref) { - $json['attachments'][] = $this->urlf('/file/%s', [$ref->id]); - } - } - - // unread only if in inbox - if ($my_roles['rec']) { - foreach ($my_mu as $mu) { - if ($mu->snd_rec === 'rec') { - $json['unread'] = !$mu->readed; - break; - } - } - } - - return $json; - } - - private function countMessages($user_id, $box) - { - $condition = 'user_id = ? AND snd_rec = ? AND deleted = 0'; - $params = [$user_id, $box === 'inbox' ? 'rec' : 'snd']; - - $total = \MessageUser::countBySQL($condition, $params); - $unread = \MessageUser::countBySQL( - $condition . ' AND readed = 0', - $params - ); - - return compact('total', 'unread'); - } - - private function getMessageIds($user_id, $box) - { - return \MessageUser::findAndMapBySQL(function ($row) { - return $row->message_id; - }, 'user_id = ? AND snd_rec = ? AND deleted = 0 ORDER BY mkdate DESC', [ - $user_id, $box === 'inbox' ? 'rec' : 'snd' - ]); - } - -} diff --git a/app/routes/News.php b/app/routes/News.php deleted file mode 100644 index c9b258b..0000000 --- a/app/routes/News.php +++ /dev/null @@ -1,375 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition news_id ^[0-9a-f]{1,32}$ - * @condition course_id ^[0-9a-f]{1,32}$ - * @condition user_id ^[0-9a-f]{1,32}$ - * @condition comment_id ^[0-9a-f]{1,32}$ - */ -class News extends \RESTAPI\RouteMap -{ - public static function before() - { - require_once 'lib/models/StudipNews.class.php'; - } - - /** - * Globale News auslesen - * - * @get /studip/news - */ - public function getGlobalNews() - { - list($json, $total) = $this->getRangedNews('studip'); - - $this->etag(md5(serialize($json))); - return $this->paginated($json, $total); - } - - /** - * News einer Veranstaltung auslesen - * - * @get /course/:course_id/news - */ - public function getCourseNews($course_id) - { - list($json, $total) = $this->getRangedNews($course_id); - - $this->etag(md5(serialize($json))); - return $this->paginated($json, $total, compact('course_id')); - } - - /** - * News eines Nutzers auslesen - * - * @get /user/:user_id/news - */ - public function getUserNews($user_id) - { - list($json, $total) = $this->getRangedNews($user_id); - - $this->etag(md5(serialize($json))); - return $this->paginated($json, $total, compact('user_id')); - } - - - /** - * News auslesen - * - * @get /news/:news_id - */ - public function getNews($news_id) - { - $news = $this->requireNews($news_id); - $news_json = $this->newsToJson($news); - - $this->lastmodified($news->chdate); - $this->expires($news->expire); - $this->etag(md5(serialize($news_json))); - - return $news_json; - } - - /** - * News löschen - * - * @delete /news/:news_id - */ - public function destroyNews($news_id) - { - $news = $this->requireNews($news_id); - - if (!$news->havePermission('delete', '', $GLOBALS['user']->id)) { - $this->error(401); - } - - $news->delete(); - $this->status(204); - } - - - /** - * News updaten - * - * @put /news/:news_id - */ - public function updateNews($news_id) - { - $news = $this->requireNews($news_id); - if (!$news->havePermission('edit', '', $GLOBALS['user']->id)) { - $this->error(401); - } - - if (isset($this->data['topic'])) { - if (!mb_strlen(trim($topic = $this->data['topic']))) { - $this->error(400, 'Topic must not be empty.'); - } - $news->topic = $topic; - } - - if (isset($this->data['body'])) { - if (!mb_strlen(trim($body = $this->data['body']))) { - $this->error(400, 'Body must not be empty.'); - } - $news->body = $body; - } - - if (isset($this->data['expire'])) { - $news->expire = (int) $this->data['expire']; - } - - if (isset($this->data['allow_comments'])) { - $news->allow_comments = (int) $this->data['allow_comments']; - } - - $news->chdate_uid = $GLOBALS['user']->id; - - if (!$news->store()) { - $this->error(500, 'Could not update news'); - - } - $this->status(204); - } - - /** - * News anlegen - * - * @post /course/:course_id/news - * @post /user/:user_id/news - * @post /studip/news - */ - public function createNews($range_id = 'studip') - { - - if (!\StudipNews::haveRangePermission('edit', $range_id, $GLOBALS['user']->id)) { - $this->error(401, "Not authorized to create a news here."); - } - - $news = new \StudipNews(); - $news->setData([ - 'user_id' => $GLOBALS['user']->id, - 'author' => $GLOBALS['user']->getFullName(), - 'topic' => trim(@$this->data['topic']), - 'body' => trim(@$this->data['body']), - 'date' => time(), - 'expire' => isset($this->data['expire']) ? intval($this->data['expire']) : 2 * 7 * 24 * 60 * 60, - 'allow_comments' => isset($this->data['allow_comments']) ? intval($this->data['allow_comments']) : 0 - ]); - $news->addRange($range_id); - - if ($errors = $this->validateNews($news)) { - $this->error(400, compact('errors')); - } - - if (!$news->store()) { - $this->error(500); - } - - $news->storeRanges(); - - $this->redirect('news/' . $news->id, 201, "ok"); - } - - /** - * News-Comments auslesen - * - * @get /news/:news_id/comments - */ - public function getNewsComments($news_id) - { - $comments = $this->requireNews($news_id)->comments->orderBy("mkdate asc"); - - $total = count($comments); - $json = []; - foreach ($comments->limit($this->offset, $this->limit) as $comment) { - $tmp = $comment->toArray("comment_id object_id user_id content mkdate chdate"); - $tmp['content_html'] = htmlReady($comment->content); - $json[$this->urlf('/comment/%s', [htmlReady($comment->id)])] = $tmp; - } - - $this->etag(md5(serialize($json))); - - return $this->paginated($json, $total, compact('news_id')); - } - - /** - * News-Comment auslesen - * - * @get /comment/:comment_id - */ - public function getComment($comment_id) - { - $comment = $this->requireComment($comment_id); - $comment_json = $this->commentToJson($comment); - - $this->lastmodified($comment->chdate); - $this->etag(md5(serialize($comment_json))); - - return $comment_json; - } - - /** - * News-Comment anlegen - * - * @post /news/:news_id/comments - */ - public function appendComment($news_id) - { - $news = $this->requireNews($news_id); - - if (!$news->allow_comments) { - $this->error(409, 'Comments are not allowed'); - } - - if (!isset($this->data['content']) || !mb_strlen($content = trim($this->data['content']))) { - $this->error(400, 'Content required.'); - } - - $comment = new \StudipComment(); - $comment->setData( - [ - 'object_id' => $news_id, - 'user_id' => $GLOBALS['user']->id, - 'content' => $content - ]); - - if (!$comment->store()) { - $this->halt(500, 'Could not create comment.'); - } - - $this->redirect('comment/' . $comment->id, 201, "ok"); - } - - /** - * News-Comment löschen - * - * @delete /comment/:comment_id - */ - public function destroyComment($comment_id) - { - $comment = $this->requireComment($comment_id); - - if (!$comment->delete()) { - $this->error(500, 'Comment could not be deleted.'); - } - - $this->halt(204); - } - - - /**************************************************/ - /* PRIVATE HELPER METHODS */ - /**************************************************/ - - private function getRangedNews($range_id) - { - - $news = \StudipNews::getNewsByRange($range_id, true, true); - - if (!self::checkRangePermission($range_id, $GLOBALS['user']->id)) { - $this->error(401); - } - - $total = count($news); - $news = array_slice($news, $this->offset, $this->limit); - - $json = []; - foreach ($news as $n) { - $json[$this->urlf('/news/%s', [$n->id])] = $this->newsToJson($n); - } - - return [$json, $total]; - } - - private function validateNews($news) - { - $errors = []; - - $retain = $_SESSION['messages']; - $_SESSION['messages'] = []; - - if (!$news->validate()) { - foreach ($_SESSION['messages'] as $message_box) { - $errors[] = $message_box->message; - } - } - - $_SESSION['messages'] = $retain; - return $errors; - } - - private static function checkRangePermission($range_id, $user_id) - { - return \StudipNews::haveRangePermission('view', $range_id, $user_id); - } - - - private function requireNews($id) - { - if (!$news = \StudipNews::find($id)) { - $this->notFound("News not found"); - } - - if (!$news->havePermission('view', '', $GLOBALS['user']->id)) { - $this->error(401); - } - - return $news; - } - - private function newsToJson($news) - { - $json = $news->toArray(words("news_id topic body date user_id expire allow_comments chdate chdate_uid mkdate")); - - $json['topic'] = (string) $news->topic; - $json['body_html'] = formatReady((string) $news->body); - $json['chdate_uid'] = trim($json['chdate_uid']); - - if ($news->allow_comments) { - $json['comments'] = $this->urlf('/news/%s/comments', [$news->id]); - $json['comments_count'] = sizeof($news->comments); - } - - $json['ranges'] = []; - foreach ($news->news_ranges as $range) { - if (self::checkRangePermission($range->range_id, $GLOBALS['user']->id)) { - switch ($range->type) { - case 'global': $url = $this->url('/studip/news'); break; - case 'sem': $url = $this->urlf('/course/%s/news', [$range->range_id]); break; - case 'user': $url = $this->urlf('/user/%s/news', [$range->range_id]); break; - case 'inst': $url = $this->urlf('/TODO/%s/news', [$range->range_id]); break; - case 'fak': $url = $this->urlf('/TODO/%s/news', [$range->range_id]); break; - } - - $json['ranges'][] = $url; - } - } - return $json; - } - - private function requireComment($id) - { - if (!$comment = \StudipComment::find($id)) { - $this->notFound("Comment not found"); - } - if (!$comment->news->havePermission('view', '', $GLOBALS['user']->id)) { - $this->error(401); - } - - return $comment; - } - - private function commentToJson($comment) - { - $json = $comment->toArray(words("comment_id mkdate chdate content")); - $json['content_html'] = formatReady($json['content']); - $json['author'] = $this->urlf('/user/%s', [$comment->user_id]); - $json['news'] = $this->urlf('/news/%s', [$comment->object_id]); - return $json; - } -} diff --git a/app/routes/ResourceBooking.php b/app/routes/ResourceBooking.php deleted file mode 100644 index a5d027f..0000000 --- a/app/routes/ResourceBooking.php +++ /dev/null @@ -1,192 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * This file contains the REST class for the - * room and resource management system. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @since 4.5 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class ResourceBooking extends \RESTAPI\RouteMap -{ - - /** - * Helper method that either returns the specified data - * or simply an empty string in case that no request result - * is requested. - */ - protected function sendReturnData($data) - { - if (\Request::submitted('quiet')) { - //Return nothing. - return ''; - } - - //Return data. - return $data; - } - - - /** - * Moves a resource booking, if permitted. - * - * @post /resources/booking/:booking_id/move - */ - public function move($booking_id) - { - $booking = \ResourceBooking::find($booking_id); - if (!$booking) { - $this->notFound('Resource booking object not found!'); - } - - $current_user = \User::findCurrent(); - - if ($booking->isReadOnlyForUser($current_user)) { - throw new \AccessDeniedException(); - } - - $resource_id = \Request::get('resource_id'); - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); - $interval_id = \Request::get('interval_id'); - - //Try the ISO format first: YYYY-MM-DDTHH:MM:SS±ZZ:ZZ - $begin = \DateTime::createFromFormat(\DateTime::RFC3339, $begin_str); - $end = \DateTime::createFromFormat(\DateTime::RFC3339, $end_str); - if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { - $tz = new \DateTime(); - $tz = $tz->getTimezone(); - //Try the ISO format without timezone: - $begin = \DateTime::createFromFormat('Y-m-d\TH:i:s', $begin_str, $tz); - $end = \DateTime::createFromFormat('Y-m-d\TH:i:s', $end_str, $tz); - } - - //Check if a specific interval has been moved: - if ($interval_id) { - $interval = \ResourceBookingInterval::findOneBySql( - 'interval_id = :interval_id AND booking_id = :booking_id', - [ - 'interval_id' => $interval_id, - 'booking_id' => $booking->id - ] - ); - if (!$interval) { - $this->notFound('Resource booking interval not found!'); - } - $interval_begin = new \DateTime(); - $interval_begin->setTimestamp($interval->begin); - $interval_end = new \DateTime(); - $interval_end->setTimestamp($interval->end); - - //Calculate the difference from the interval time range - //to the time range from the request. That difference - //is then applied to the booking. - $begin_diff = $interval_begin->diff($begin); - $end_diff = $interval_end->diff($end); - - $new_booking_begin = new \DateTime(); - $new_booking_begin->setTimestamp($booking->begin); - $new_booking_end = new \DateTime(); - $new_booking_end->setTimestamp($booking->end); - - $new_booking_begin = $new_booking_begin->add($begin_diff); - $new_booking_end = $new_booking_end->add($end_diff); - //We must substract the preparation time to the begin timestamp - //to get the real begin: - $real_begin = clone $new_booking_begin; - if ($booking->preparation_time > 0) { - $real_begin->sub(new \DateInterval('PT' . ($booking->preparation_time / 60 ) . 'M')); - } - $booking->begin = $real_begin->getTimestamp(); - $booking->end = $new_booking_end->getTimestamp(); - } else { - //We must substract the preparation time to the begin timestamp - //to get the real begin: - $real_begin = clone $begin; - if ($booking->preparation_time > 0) { - $real_begin->sub(new \DateInterval('PT' . ($booking->preparation_time / 60 ) . 'M')); - } - $booking->begin = $real_begin->getTimestamp(); - $booking->end = $end->getTimestamp(); - } - if ($resource_id) { - //The resource-ID has changed: - //The booking was moved from one resource to another. - $booking->resource_id = $resource_id; - } - - //Update the booking_user_id field: - $booking->booking_user_id = \User::findCurrent()->id; - - try { - $booking->store(); - return $this->sendReturnData($booking->toRawArray()); - } catch (\Exception $e) { - $this->halt(500, $e->getMessage()); - } - } - - - /** - * Retrieves the intervals of the resource booking. - * These can be filtered by a time range. - * - * @get /resources/booking/:booking_id/intervals - */ - public function getIntervals($booking_id) - { - $booking = \ResourceBooking::find($booking_id); - if (!$booking) { - $this->notFound('Resource booking object not found!'); - } - - $current_user = \User::findCurrent(); - - $resource = $booking->resource->getDerivedClassInstance(); - if (!$resource->bookingPlanVisibleForUser($current_user)) { - throw new \AccessDeniedException(); - } - - //Get begin and end: - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); - $begin = null; - $end = null; - if ($begin_str && $end_str) { - //Try the ISO format first: YYYY-MM-DDTHH:MM:SS±ZZ:ZZ - $begin = \DateTime::createFromFormat(\DateTime::RFC3339, $begin_str); - $end = \DateTime::createFromFormat(\DateTime::RFC3339, $end_str); - if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { - $tz = new \DateTime(); - $tz = $tz->getTimezone(); - //Try the ISO format without timezone: - $begin = \DateTime::createFromFormat('Y-m-d\TH:i:s', $begin_str, $tz); - $end = \DateTime::createFromFormat('Y-m-d\TH:i:s', $end_str, $tz); - } - } - - $sql = "booking_id = :booking_id "; - $sql_data = ['booking_id' => $booking->id]; - if (($begin instanceof \DateTime) && ($end instanceof \DateTime)) { - $sql .= "AND begin >= :begin AND end <= :end "; - $sql_data['begin'] = $begin->getTimestamp(); - $sql_data['end'] = $end->getTimestamp(); - } - if (\Request::submitted('exclude_cancelled_intervals')) { - $sql .= "AND takes_place = '1' "; - } - $sql .= "ORDER BY begin ASC, end ASC"; - $intervals = \ResourceBookingInterval::findBySql($sql, $sql_data); - - $result = []; - foreach ($intervals as $interval) { - $result[] = $interval->toRawArray(); - } - - return $result; - } -} diff --git a/app/routes/ResourceCategories.php b/app/routes/ResourceCategories.php deleted file mode 100644 index bdd3d15..0000000 --- a/app/routes/ResourceCategories.php +++ /dev/null @@ -1,349 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * This file contains API routes related to ResourceCategory objects. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @since 4.5 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class ResourceCategories extends \RESTAPI\RouteMap -{ - /** - * Validate access to each route. - */ - public function before() - { - if (!\ResourceManager::userHasGlobalPermission(\User::findCurrent(), 'admin')) { - throw new \AccessDeniedException(); - } - } - - /** - * Returns all defined resource categories. - * - * @get /resources/categories - */ - public function getAllResourceCategories() - { - return \ResourceCategory::findAndMapBySql( - function (\ResourceCategory $category) { - return $category->toRawArray(); - }, - 'TRUE ORDER BY name ASC' - ); - } - - - /** - * Get a resource category object. - * - * @get /resources/category/:category_id - */ - public function getResourceCategory($category_id) - { - $category = \ResourceCategory::find($category_id); - if (!$category) { - $this->notFound('ResourceCategory object not found!'); - } - - return $category->toRawArray(); - } - - - /** - * Creates a resource category object. - * - * @post /resources/new_category - */ - public function addResourceCategory() - { - $name = \Request::get('name'); - $description = \Request::get('description'); - $class_name = \Request::get('class_name'); - $iconnr = \Request::int('iconnr'); - - $properties_name = \Request::getArray('properties_name'); - $properties_type = \Request::getArray('properties_type'); - $properties_requestable = \Request::getArray('properties_requestable'); - $properties_protected = \Request::getArray('properties_protected'); - - $set_properties = []; - foreach ($properties_name as $key => $property_name) { - $set_properties[] = [ - 'name' => $property_name, - 'type' => $properties_type[$key], - 'requestable' => $properties_requestable[$key], - 'protected' => $properties_protected[$key] - ]; - } - - //validation: - if (!$name) { - $this->halt( - 400, - _('Der Name der Kategorie ist leer!') - ); - } - - if (!is_a($class_name, 'Resource', true)) { - $this->halt( - 400, - _('Es wurde keine gültige Ressourcen-Datenklasse ausgewählt!') - ); - } - - switch ($class_name) { - case 'Location': - $category = \ResourceManager::createLocationCategory( - $name, - $description - ); - break; - case 'Building': - $category = \ResourceManager::createBuildingCategory( - $name, - $description - ); - break; - case 'Room': - $category = \ResourceManager::createRoomCategory( - $name, - $description - ); - break; - default: - $category = \ResourceManager::createCategory( - $name, - $description, - $class_name, - false, - $iconnr - ); - } - - if ($category->store() === false) { - $this->halt( - 500, - _('Fehler beim Speichern der Kategorie!') - ); - } - - //After we have stored the category we must store - //the properties or create them, if necessary: - - foreach ($set_properties as $set_property) { - $category->addProperty( - $set_property['name'], - $set_property['type'], - $set_property['requestable'], - $set_property['protected'] - ); - } - - return $category->toRawArray(); - } - - /** - * Modifies a resource category. - * - * @put /resources/category/:category_id - */ - public function editResourceCategory($category_id) - { - $category = \ResourceCategory::find($category_id); - if (!$category) { - $this->notFound('ResourceCategory object not found!'); - } - - if ($category->system) { - $this->halt(403, 'System categories must not be modified!'); - return; - } - - $name = $this->data['name']; - $description = $this->data['description']; - $iconnr = intval($this->data['iconnr']); - - //validation: - if ($name) { - $category->name = $name; - } - if ($description) { - $category->description = $description; - } - if ($iconnr) { - $category->iconnr = $iconnr; - } - - if ($category->store() === false) { - $this->halt( - 500, - 'Error while saving the category!' - ); - } - - return $category->toRawArray(); - } - - - /** - * Deletes a resource category. - * - * @delete /resources/category/:category_id - */ - public function deleteResourceCategory($category_id) - { - $category = \ResourceCategory::find($category_id); - if (!$category) { - $this->notFound('ResourceCategory object not found!'); - } - - if ($category->system) { - $this->halt(403,'System resource categories must not be deleted!'); - return; - } - - if ($category->delete()) { - return 'OK'; - } else { - $this->halt( - 500, - 'Error while deleting the resource category!' - ); - } - } - - - /** - * Get all resource category property objects for a resource category. - * - * @get /resources/category/:category_id/properties - */ - public function getResourceCategoryProperties($category_id) - { - $category = \ResourceCategory::find($category_id); - if (!$category) { - $this->notFound('ResourceCategory object not found!'); - } - - $result = []; - $properties = \ResourceCategoryProperty::findBySql( - 'INNER JOIN resource_property_definitions rpd - USING (property_id) - WHERE category_id = :category_id ORDER BY rpd.name ASC', - [ - 'category_id' => $category->id - ] - ); - - if ($properties) { - foreach ($properties as $property) { - $data = $property->toRawArray(); - $data['name'] = $property->definition->name; - $data['type'] = $property->definition->type; - $result[] = $data; - } - } - - return $result; - } - - - /** - * Returns all resources which belong to the specified category. - * The result set can be limited by the parameters 'offset' and 'limit'. - * If the parameter 'with_full_name' is set to 1, the resources full name - * as provided by its responsible class, is added to the result set. - * - * @get /resources/category/:category_id/resources - */ - public function getResourceCategoryResources($category_id) - { - $category = \ResourceCategory::find($category_id); - if (!$category) { - $this->notFound('ResourceCategory object not found!'); - } - - $offset = \Request::int('offset'); - $limit = \Request::int('limit'); - $with_full_name = \Request::get('with_full_name'); - - $result = []; - - $sql = 'category_id = :category_id ORDER BY name ASC '; - $sql_array = ['category_id' => $category->id]; - - if ($limit > 0) { - $sql .= 'limit :limit '; - $sql_array['limit'] = $limit; - if ($offset > 0) { - $sql .= 'offset :offset '; - $sql_array['offset'] = $offset; - } - } - - $resources = \Resource::findBySql($sql, $sql_array); - - if ($resources) { - foreach ($resources as $r) { - if ($with_full_name) { - $r = $r->getDerivedClassInstance(); - $data = $r->toRawArray(); - $data['full_name'] = $r->getFullName(); - $result[] = $data; - } else { - $result[] = $r->toRawArray(); - } - } - } - - return $result; - } - - - /** - * Creates a resource. - * - * @post /resources/category/:category_id/create_resource - */ - public function createResource($category_id) - { - $category = \ResourceCategory::find($category_id); - if (!$category) { - $this->notFound('ResourceCategory object not found!'); - } - - - $name = \Request::get('name'); - $description = \Request::get('description'); - $parent_id = \Request::get('parent_id'); - $properties = \Request::getArray('properties'); - - if (!$name) { - $this->halt( - 400, - 'The parameter \'name\' is not set!' - ); - } - - try { - $resource = $category->createResource( - $name, - $description, - $parent_id, - $properties - ); - - return $resource; - } catch (\Exception $e) { - $this->halt( - 400, - $e->getMessage() - ); - } - } -} diff --git a/app/routes/ResourcePermissions.php b/app/routes/ResourcePermissions.php deleted file mode 100644 index be5c647..0000000 --- a/app/routes/ResourcePermissions.php +++ /dev/null @@ -1,585 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * This file contains API routes related to ResourcePermission - * and ResourceTemporaryPermission objects. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @since 4.5 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class ResourcePermissions extends \RESTAPI\RouteMap -{ - - //Methods for permanent permissions: - - - /** - * Get the permission levels of users for the specified resource. - * - * @param levels: Limit the result set to the specified permission levels. - * Allowed permission levels: user, autor, tutor, admin. - * The permission levels have to be comma separated like in the - * following example: "autor,tutor,admin". - * - * @get /resources/permissions/:resource_id - */ - public function getResourcePermissions($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'admin')) { - throw new \AccessDeniedException(); - } - - $levels_str = \Request::get('levels'); - $levels = []; - if ($levels_str) { - $levels = explode(',', $levels_str); - } - - $sql = 'resource_id = :resource_id '; - $sql_array = [ - 'resource_id' => $resource->id - ]; - - if ($levels) { - $sql .= 'AND perms IN ( :levels ) '; - $sql_array['levels'] = $levels; - } - - $permissions = \ResourcePermission::findBySql($sql, $sql_array); - - $result = []; - if ($permissions) { - foreach ($permissions as $permission) { - $result[] = $permission->toRawArray(); - } - } - - return $result; - } - - - /** - * Returns the permissions a specific user has on a specified resource. - * - * @get /resources/permissions/:resource_id/:user:_id - */ - public function getPermission($resource_id, $user_id) - { - if ($resource_id !== 'global') { - if (!\Resource::exists($resource_id)) { - $this->halt( - 404, - 'Resource not found!' - ); - } - } - - $user = \User::find($user_id); - if (!$user) { - $this->halt( - 400, - 'No user was provided!' - ); - } - - $current_user = \User::findCurrent(); - - if (!\ResourceManager::userHasGlobalPermission($current_user, 'admin')) { - if ($resource_id !== 'global') { - $resource = \Resource::find($resource_id); - $resource = $resource->getDerivedClassInstance(); - if (!$resource->userHasPermission($current_user, 'admin')) { - $this->halt(403); - } - } else { - //$resource_id == 'global': One must be admin - //to perform this action! - $this->halt(403); - } - } - - $permission = \ResourcePermission::findOneBySql( - "resource_id = :resource_id AND user_id = :user_id", - [ - 'resource_id' => $resource_id, - 'user_id' => $user->id - ] - ); - - if ($permission) { - return $permission->toRawArray(); - } else { - //The user already had no global permissions! - return NULL; - } - } - - - /** - * @post /resources/permissions/:resource_id/:user_id - */ - public function setPermission($resource_id, $user_id) - { - if ($resource_id !== 'global') { - if (!\Resource::exists($resource_id)) { - $this->halt( - 404, - 'Resource not found!' - ); - return; - } - } - - $user = \User::find($user_id); - if (!$user) { - $this->halt( - 400, - 'No user was provided!' - ); - } - - $current_user = \User::findCurrent(); - - if (!\ResourceManager::userHasGlobalPermission($current_user, 'admin')) { - if ($resource_id !== 'global') { - $resource = \Resource::find($resource_id); - $resource = $resource->getDerivedClassInstance(); - if (!$resource->userHasPermission($current_user, 'admin')) { - $this->halt(403); - } - } else { - //$resource_id == 'global': One must be admin - //to perform this action! - $this->halt(403); - } - } - - //Verify permission level: - $perms = \Request::get('perms'); - - if (!in_array($perms, ['user', 'autor', 'tutor', 'admin'])) { - $this->halt( - 400, - 'Invalid permission level specified!' - ); - } - - //Check if permissions are already present for the user. - //If not, create a new permission object. - $permission = \ResourcePermission::findOneBySql( - "resource_id = :resource_id AND user_id = :user_id", - [ - 'resource_id' => $resource_id, - 'user_id' => $user->id - ] - ); - - if (!$permission) { - $permission = new \ResourcePermission(); - $permission->resource_id = $resource_id; - $permission->user_id = $user->id; - } - - $permission->perms = $perms; - - if ($permission->store() === false) { - $this->halt( - 500, - 'Error while saving permissions!' - ); - } - - return $permission->toRawArray(); - } - - - /** - * @delete /resources/permissions/:resource_id/:user_id - */ - public function deletePermission($resource_id, $user_id) - { - if ($resource_id !== 'global' && !\Resource::exists($resource_id)) { - $this->notFound('Resource not found!'); - } - - $user = \User::find($user_id); - if (!$user) { - $this->halt( - 400, - 'No user was provided!' - ); - } - - $current_user = \User::findCurrent(); - - if (!\ResourceManager::userHasGlobalPermission($current_user, 'admin')) { - if ($resource_id !== 'global') { - $resource = \Resource::find($resource_id); - $resource = $resource->getDerivedClassInstance(); - if (!$resource->userHasPermission($current_user, 'admin')) { - $this->halt(403); - } - } else { - //$resource_id == 'global': One must be admin - //to perform this action! - $this->halt(403); - } - } - - $permission = \ResourcePermission::findOneBySql( - "resource_id = :resource_id AND user_id = :user_id", - [ - 'resource_id' => $resource_id, - 'user_id' => $user->id - ] - ); - - if (!$permission) { - //The user already had no global permissions! - return 'OK'; - } - - if ($permission->delete()) { - return 'OK'; - } else { - $this->halt( - 500, - 'Error while deleting global permissions!' - ); - } - } - - - //Methods for temporary permissions: - - - /** - * Get the temporary permission levels of users for the specified resource. - * The begin and end parameters are mandatory to determine a time range - * to collect the temporary permissions in that range. - * - * @param begin: The begin timestamp of the time range. - * @param end: The end timestamp of the time range. - * @param levels: Limit the result set to the specified temporary permission - * levels. Allowed permission levels: user, autor, tutor, admin. - * The permission levels have to be comma separated like in the - * following example: "autor,tutor,admin". - * - * @get /resources/temporary_permissions/:resource_id - */ - public function getTemporaryResourcePermissions($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'admin')) { - throw new \AccessDeniedException(); - } - - $begin = \Request::get('begin'); - $end = \Request::get('end'); - $levels_str = \Request::get('levels'); - $levels = []; - if ($levels_str) { - $levels = explode(',', $levels_str); - } - - if (!$begin or !$end) { - //Use the current day: - $begin = strtotime('today 0:00:00'); - $end = strtotime('today 23:59:59'); - } - - $sql = 'resource_id = :resource_id - AND - ((begin >= :begin AND begin <= :end) - OR - (end >= :begin AND end <= :end)) - OR - (begin < :begin AND end > :end)'; - $sql_array = [ - 'resource_id' => $resource->id, - 'begin' => $begin, - 'end' => $end - ]; - - if ($levels) { - $sql .= 'AND perms IN ( :levels ) '; - $sql_array['levels'] = $levels; - } - - return \ResourceTemporaryPermission::findAndMapBySql( - function (\ResourceTemporaryPermission $permission) { - return $permission->toRawArray(); - }, - $sql, - $sql_array - ); - } - - - /** - * Returns the permissions a specific user has on a specified resource. - * - * @get /resources/temporary_permissions/:resource_id/:user:_id - */ - public function getTemporaryPermission($resource_id, $user_id) - { - if ($resource_id !== 'global') { - if (!\Resource::exists($resource_id)) { - $this->notFound('Resource not found!'); - } - } - - $user = \User::find($user_id); - if (!$user) { - $this->halt( - 400, - 'No user was provided!' - ); - } - - $current_user = \User::findCurrent(); - - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); - $begin = null; - $end = null; - $with_time_range = false; - if ($begin_str && $end_str) { - $with_time_range = true; - $begin = new \DateTime(); - $begin->setTimestamp($begin_str); - $end = new \DateTime(); - $end->setTimestamp($end_str); - } - - if (!\ResourceManager::userHasGlobalPermission($current_user, 'admin')) { - if ($resource_id !== 'global') { - $resource = \Resource::find($resource_id); - $resource = $resource->getDerivedClassInstance(); - if (!$resource->userHasPermission($current_user, 'admin')) { - $this->halt(403); - } - } else { - //$resource_id == 'global': One must be admin - //to perform this action! - $this->halt(403); - } - } - - $permissions = null; - if ($with_time_range) { - $permissions = \ResourceTemporaryPermission::findBySql( - "resource_id = :resource_id AND user_id = :user_id - AND ( - (begin >= :begin AND begin <= :end) - OR - (end >= :begin AND end <= :end) - )", - [ - 'resource_id' => $resource_id, - 'user_id' => $user->id, - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp() - ] - ); - } else { - $permissions = \ResourceTemporaryPermission::findBySql( - "resource_id = :resource_id AND user_id = :user_id", - [ - 'resource_id' => $resource_id, - 'user_id' => $user->id - ] - ); - } - - if ($permissions) { - $result = []; - foreach ($permissions as $permission) { - $result[] = $permission->toRawArray(); - } - return $result; - } else { - //The user already had no global permissions! - return NULL; - } - } - - - /** - * Sets temporary permissions for a user. - * - * @param begin The begin timestamp for the temporary permisssion. - * @param end The end timestamp for the temporary permission. - * @param perms The permission level for the temporary permission. - * - * @post /resources/temporary_permissions/:resource_id/:user_id - */ - public function setTemporaryPermission($resource_id, $user_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource not found!'); - } - - $user = \User::find($user_id); - if (!$user) { - $this->notFound('User not found!'); - } - - $current_user = \User::findCurrent(); - - if (!\ResourceManager::userHasGlobalPermission($current_user, 'admin') - && !$resource->userHasPermission($current_user, 'admin')) { - $this->halt(403); - } - - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); - if (!$begin_str || !$end_str) { - $this->halt( - 400, - 'No time range specified for temporary permission!' - ); - } - - $begin = new \DateTime(); - $begin->setTimestamp($begin_str); - $end = new \DateTime(); - $end->setTimestamp($end_str); - - //Verify permission level: - $perms = \Request::get('perms'); - - if (!in_array($perms, ['user', 'autor', 'tutor', 'admin'])) { - $this->halt( - 400, - 'Invalid permission level specified!' - ); - } - - //Check if permissions are already present for the user. - //If not, create a new permission object. - $permission = \ResourceTemporaryPermission::findOneBySql( - "resource_id = :resource_id AND user_id = :user_id - AND begin = :begin AND end = :end", - [ - 'resource_id' => $resource_id, - 'user_id' => $user->id, - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp() - ] - ); - - if (!$permission) { - $permission = new \ResourceTemporaryPermission(); - $permission->resource_id = $resource_id; - $permission->user_id = $user->id; - $permission->begin = $begin->getTimestamp(); - $permission->end = $end->getTimestamp(); - } - - $permission->perms = $perms; - - if ($permission->store() === false) { - $this->halt( - 500, - 'Error while saving permissions!' - ); - } - - return $permission->toRawArray(); - } - - - /** - * Deletes all temporary permissions of a user. - * If a time interval is given all permissions inside the interval - * are deleted. - * - * @delete /resources/temporary_permissions/:resource_id/:user_id - */ - public function deleteTemporaryPermission($resource_id, $user_id) - { - if ($resource_id !== 'global') { - if (!\Resource::exists($resource_id)) { - $this->notFound('Resource not found!'); - } - } - - $user = \User::find($user_id); - if (!$user) { - $this->notFound('User not found!'); - } - - $current_user = \User::findCurrent(); - - if (!\ResourceManager::userHasGlobalPermission($current_user, 'admin')) { - if ($resource_id !== 'global') { - $resource = \Resource::find($resource_id); - $resource = $resource->getDerivedClassInstance(); - if (!$resource->userHasPermission($current_user, 'admin')) { - $this->halt(403); - } - } else { - //$resource_id == 'global': One must be admin - //to perform this action! - $this->halt(403); - } - } - - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); - $begin = null; - $end = null; - $with_time_range = false; - if ($begin_str and $end_str) { - $with_time_range = true; - $begin = new \DateTime(); - $begin->setTimestamp($begin_str); - $end = new \DateTime(); - $end->setTimestamp($end_str); - } - - if ($with_time_range) { - \ResourceTemporaryPermission::deleteBySql( - "resource_id = :resource_id AND user_id = :user_id - AND ( - (begin >= :begin AND end <= :end) - )", - [ - 'resource_id' => $resource_id, - 'user_id' => $user->id, - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp() - ] - ); - } else { - \ResourceTemporaryPermission::deleteBySql( - "resource_id = :resource_id AND user_id = :user_id", - [ - 'resource_id' => $resource_id, - 'user_id' => $user->id - ] - ); - } - - return 'OK'; - } -} diff --git a/app/routes/ResourceProperties.php b/app/routes/ResourceProperties.php deleted file mode 100644 index 2ddbbaf..0000000 --- a/app/routes/ResourceProperties.php +++ /dev/null @@ -1,224 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * This file contains API routes related to ResourceProperty objects. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @since 4.5 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class ResourceProperties extends \RESTAPI\RouteMap -{ - /** - * Validate access to each route. - */ - public function before() - { - if (!\ResourceManager::userHasGlobalPermission(\User::findCurrent(), 'admin')) { - throw new \AccessDeniedException(); - } - } - - /** - * Returns all resource property definitions. - * - * @get /resources/properties - */ - public function getAllResourcePropertyDefinitions() - { - $properties = \ResourcePropertyDefinition::findBySql('TRUE ORDER BY name ASC'); - - $result = []; - - if ($properties) { - foreach ($properties as $p) { - $result[] = $p->toRawArray(); - } - } - - return $result; - } - - - /** - * Creates a new resource property definition. - * - * @post /resources/add_property - */ - public function addResourcePropertyDefinition() - { - $name = \Request::get('name'); - $description = \Request::i18n('description'); - $type = \Request::get('type'); - $write_permission_level = \Request::get('write_permission_level'); - $options = \Request::get('options', ''); - $range_search = \Request::bool('range_search'); - - if (!$name) { - $this->halt( - 400, - 'The field \'name\' must not be empty!' - ); - } - if (!in_array($type, \ResourcePropertyDefinition::getDefinedTypes())) { - $this->halt( - 400, - 'Invalid property type specified!' - ); - } - if (!in_array($write_permission_level, ['user', 'autor', 'tutor', 'admin'])) { - $this->halt( - 400, - 'Invalid permission level in field \'write_permission_level\'!' - ); - } - - $property = new \ResourcePropertyDefinition(); - $property->name = $name; - $property->description = $description; - $property->type = $type; - $property->options = $options ?: ''; - $property->range_search = $range_search; - $property->write_permission_level = $write_permission_level; - - if (!$property->store()) { - $this->halt( - 500, - 'Error while saving the property!' - ); - } - return $property->toRawArray(); - } - - - /** - * Get a resource property definition object. - * - * @get /resources/property/:property_id - */ - public function getResourcePropertyDefinition($property_id) - { - $property = \ResourcePropertyDefinition::find($property_id); - if (!$property) { - $this->notFound('ResourcePropertyDefinition object not found!'); - } - - return $property->toRawArray(); - } - - - /** - * Modifies a resource property definition. - * - * @put /resources/property/:property_id - */ - public function editResourcePropertyDefinition($property_id) - { - $property = \ResourcePropertyDefinition::find($property_id); - if (!$property) { - $this->notFound('ResourcePropertyDefinition object not found!'); - } - - if ($property->system) { - $this->halt( - 403, - 'System properties must not be edited!' - ); - } - - $name = $this->data['name']; - $description = $this->data['description']; - $type = $this->data['type']; - $write_permission_level = $this->data['write_permission_level']; - $options = $this->data['options']; - $range_search = $this->data['range_search']; - - if ($name) { - $property->name = $name; - } - - if ($description) { - $property->description = $description; - } - - if ($type) { - if (!in_array($type, \ResourcePropertyDefinition::getDefinedTypes())) { - $this->halt( - 400, - 'Invalid property type specified!' - ); - } - $property->type = $type; - } - - if ($write_permission_level) { - if (!in_array($write_permission_level, ['user', 'autor', 'tutor', 'admin'])) { - $this->halt( - 400, - 'Invalid permission level in field \'write_permission_level\'!' - ); - } - $property->write_permission_level = $write_permission_level; - } - - if ($options) { - $property->options = $options; - } - - if ($range_search) { - $property->range_search = $range_search; - } - - if ($property->isDirty()) { - if ($property->store()) { - return $property->toRawArray(); - } else { - $this->halt( - 500, - 'Error while saving the property!' - ); - } - } - - return $property->toRawArray(); - } - - - /** - * Deletes a resource property definition object. - * - * @delete /resources/property/:property_id - */ - public function deleteResourcePropertyDefinition($property_id) - { - $property = \ResourcePropertyDefinition::find($property_id); - if (!$property) { - $this->notFound('ResourcePropertyDefinition object not found!'); - } - - if (!\ResourceManager::userHasGlobalPermission(\User::findCurrent(), 'admin')) { - $this->halt(403); - } - - //Check if the property is in use: - - if ($property->isInUse()) { - $this->halt( - 403, - 'The property is in use and can therefore not be deleted!' - ); - } - - if ($property->delete()) { - return "OK"; - } else { - $this->halt( - 500, - 'Error while deleting resource property definition!' - ); - } - } -} diff --git a/app/routes/ResourceRequest.php b/app/routes/ResourceRequest.php deleted file mode 100644 index 24dfd2e..0000000 --- a/app/routes/ResourceRequest.php +++ /dev/null @@ -1,138 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * This file contains the REST class for resource requests from the - * room and resource management system. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @since 4.5 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class ResourceRequest extends \RESTAPI\RouteMap -{ - - /** - * Helper method that either returns the specified data - * or simply an empty string in case that no request result - * is requested. - */ - protected function sendReturnData($data) - { - if (\Request::submitted('quiet')) { - //Return nothing. - return ''; - } - - //Return data. - return $data; - } - - - /** - * Moves a resource request, if permitted. - * - * @post /resources/request/:request_id/move - */ - public function move($request_id) - { - $request = \ResourceRequest::find($request_id); - if (!$request) { - $this->notFound('Resource request object not found!'); - } - - $current_user = \User::findCurrent(); - - if ($request->isReadOnlyForUser($current_user)) { - throw new \AccessDeniedException(); - } - - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); - - //Try the ISO format first: YYYY-MM-DDTHH:MM:SS±ZZ:ZZ - $begin = \DateTime::createFromFormat(\DateTime::RFC3339, $begin_str); - $end = \DateTime::createFromFormat(\DateTime::RFC3339, $end_str); - if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { - $tz = new \DateTime(); - $tz = $tz->getTimezone(); - $begin = \DateTime::createFromFormat('Y-m-d\TH:i:s', $begin_str, $tz); - $end = \DateTime::createFromFormat('Y-m-d\TH:i:s', $end_str, $tz); - } - - $request->begin = $begin->getTimestamp(); - $request->end = $end->getTimestamp(); - - try { - $request->store(); - return $this->sendReturnData($request->toRawArray()); - } catch (\Exception $e) { - $this->halt(500, $e->getMessage()); - } - } - - - /** - * Changes the reply comment of a request. - * - * @post /resources/request/:request_id/edit_reply_comment - */ - public function editReplyComment($request_id) - { - $request = \ResourceRequest::find($request_id); - if (!$request) { - $this->notFound('Resource request object not found!'); - } - - $current_user = \User::findCurrent(); - - if ($request->isReadOnlyForUser($current_user)) { - throw new \AccessDeniedException(); - } - - $request->reply_comment = \Request::get('reply_comment'); - - try { - if ($request->store() === false) { - throw new \RuntimeException('Could not store comment'); - } - } catch (\Exception $e) { - $this->halt(500, $e->getMessage()); - } - - return $this->sendReturnData($request->toRawArray()); - } - - - /** - * Changes the reply comment of a request. - * - * @post /resources/request/:request_id/toggle_marked - */ - public function toggleMarkedFlag($request_id) - { - $request = \ResourceRequest::find($request_id); - if (!$request) { - $this->notFound('Resource request object not found!'); - } - - $current_user = \User::findCurrent(); - - if ($request->isReadOnlyForUser($current_user)) { - throw new \AccessDeniedException(); - } - - //Switch to the next marking state or return to the unmarked state - //if the next marking state would be after the last defined - //marking state. - $request->marked = (++$request->marked % \ResourceRequest::MARKING_STATES); - - if ($request->isDirty()) { - $request->store(); - } - - return $request; - } -} diff --git a/app/routes/Resources.php b/app/routes/Resources.php deleted file mode 100644 index 7117546..0000000 --- a/app/routes/Resources.php +++ /dev/null @@ -1,950 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * This file contains the REST class for the - * room and resource management system. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @since 4.5 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class Resources extends \RESTAPI\RouteMap -{ - - //Resource routes: - - - /** - * Get a resource object. - * @param derived_class: If the URL parameter derived_class is set - * the resource object is converted to an instance of the - * class that does correct handling of the resource object. - * - * @get /resources/resource/:resource_id - */ - public function getResource($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'user')) { - throw new \AccessDeniedException(); - } - - if (\Request::submitted('derived_classes')) { - $resource = $resource->getDerivedClassInstance(); - } - - $result = $resource->toRawArray(); - - $result['full_name'] = $resource->getFullName(); - $result['has_children'] = $resource->children ? true : false; - - return $result; - } - - - /** - * Modifies a resource object. - * - * @put /resources/resource/:resource_id - */ - public function editResource($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'autor')) { - $this->halt(403); - return; - } - - $name = $this->data['name']; - $description = $this->data['description']; - $parent_id = $this->data['parent_id']; - $properties = $this->data['properties']; - - if ($name) { - $resource->name = $name; - } - if ($description) { - $resource->description = $description; - } - if ($parent_id) { - if (!\Resource::exists($parent_id)) { - $this->halt( - 400, - 'No resource exists with the ID \'' . $parent_id . '\'!' - ); - } - $resource->parent_id = $parent_id; - } - if ($properties) { - foreach ($properties as $name => $value) { - try { - $resource->setProperty($name, $value, $GLOBALS['user']->id); - } catch (\AccessDeniedException $e) { - $this->halt( - 403, - $e->getMessage() - ); - } catch (\Exception $e) { - $this->halt( - 500, - $e->getMessage() - ); - } - } - } - - if ($resource->isDirty()) { - if ($resource->store()) { - return $resource->toRawArray(); - } else { - $this->halt( - 500, - 'Error while saving the resource object!' - ); - } - } - return $resource->toRawArray(); - } - - - /** - * Deletes a resource object. - * - * @delete /resources/resource/:resource_id - */ - public function deleteResource($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'admin')) { - $this->halt(403); - return; - } - - if (\Request::submitted('derived_classes')) { - $resource = $resource->getDerivedClassInstance(); - } - - if ($resource->delete()) { - return 'OK'; - } else { - $this->halt( - 500, - 'Error while deleting the resource object!' - ); - } - } - - - /** - * Returns the child resources of a resource object, if they exist. - * - * @get /resources/resource/:resource_id/children - */ - public function getResourceChildren($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'user')) { - throw new \AccessDeniedException(); - } - - $use_derived_classes = (bool) \Request::submitted('derived_classes'); - - $result = []; - - $children = \Resource::findBySql( - 'parent_id = :resource_id - ORDER BY name ASC', - [ - 'resource_id' => $resource->id - ] - ); - if ($children) { - foreach ($children as $child) { - if ($use_derived_classes) { - $child = $child->getDerivedClassInstance(); - } - $result[] = $child->toRawArray(); - } - } - return $result; - } - - - /** - * Returns the parent resource of a resource object, if it exists. - * - * @get /resources/resource/:resource_id/parent - */ - public function getResourceParent($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'user')) { - throw new \AccessDeniedException(); - } - - if (!$resource->parent) { - $this->notFound('This resource has no parent!'); - } - - $use_derived_classes = (bool) \Request::submitted('derived_classes'); - - if ($use_derived_classes) { - $parent = $resource->parent->getDerivedClassInstance(); - return $parent->toRawArray(); - } - - return $resource->parent->toRawArray(); - } - - - /** - * Get all property objects of a resource. - * - * @get /resources/resource/:resource_id/properties - */ - public function getResourceProperties($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'user')) { - throw new \AccessDeniedException(); - } - - $result = []; - $properties = \ResourceProperty::findBySql( - 'INNER JOIN resource_property_definitions rpd - ON resource_properties.property_id = rpd.property_id - WHERE - resource_properties.resource_id = :resource_id - ORDER BY rpd.name ASC', - [ - 'resource_id' => $resource->id - ] - ); - - if ($properties) { - foreach ($properties as $property) { - $data = $property->toRawArray(); - $data['name'] = $property->definition->name; - $data['type'] = $property->definition->type; - if ($data['type'] == 'position') { - //position properties also get the map-URL: - $data['map_url'] = \ResourceManager::getMapUrlForResourcePosition( - $property - ); - } - $result[] = $data; - } - } - - return $result; - } - - - /** - * Returns the booking plan of a resource for a week specified - * by the parameters begin and end. - * - * @param begin: The begin timestamp of the time range for the booking plan. - * @param end: The end timestamp of the time range for the booking plan. - * - * @allow_nobody - * - * @get /resources/resource/:resource_id/booking_plan - */ - public function getResourceBookingPlan($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - $current_user = \User::findCurrent(); - $nobody_access = true; - - if ($current_user instanceof \User) { - $nobody_access = false; - if (!$resource->bookingPlanVisibleForUser($current_user)) { - throw new \AccessDeniedException(); - } - } elseif ($resource instanceof \Room) { - if (!$resource->bookingPlanVisibleForUser($current_user)) { - throw new \AccessDeniedException(); - } - } - $user_is_resource_user = false; - if ($current_user instanceof \User) { - $user_is_resource_user = $resource->userHasPermission( - $current_user, - 'user' - ); - } - - $display_requests = false; - if ($current_user instanceof \User) { - $display_requests = \Request::get('display_requests'); - } - $display_all_requests = \Request::get('display_all_requests'); - - if ($display_all_requests && !$user_is_resource_user) { - //The user is not allowed to see all requests. - throw new \AccessDeniedException(); - } - - $begin_date = \Request::get('start'); - $end_date = \Request::get('end'); - if (!$begin_date || !$end_date) { - //No time range specified. - $this->halt(400, 'The parameters "start" and "end" are missing!'); - return; - } - - //Try the ISO format first: YYYY-MM-DDTHH:MM:SS±ZZ:ZZ - $begin = \DateTime::createFromFormat(\DateTime::RFC3339, $begin_date); - $end = \DateTime::createFromFormat(\DateTime::RFC3339, $end_date); - - if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { - $begin = new \DateTime(); - $end = new \DateTime(); - //Assume the local timezone and use the Y-m-d format: - $date_regex = '/[0-9]{4}-(0[1-9]|1[0-2])-([0-2][0-9]|3[0-1])/'; - if (preg_match($date_regex, $begin_date)) { - //$begin is specified in the date formay YYYY-MM-DD: - $begin_str = explode('-', $begin_date); - $begin->setDate( - intval($begin_str[0]), - intval($begin_str[1]), - intval($begin_str[2]) - ); - $begin->setTime(0,0,0); - } else { - $begin->setTimestamp($begin_date); - } - //Now we do the same for $end_timestamp: - if (preg_match($date_regex, $end_date)) { - //$begin is specified in the date formay YYYY-MM-DD: - $end_str = explode('-', $end_date); - $end->setDate( - intval($end_str[0]), - intval($end_str[1]), - intval($end_str[2]) - ); - $end->setTime(23,59,59); - } else { - $end->setTimestamp($end_date); - } - } - - //Get parameters: - $booking_types = []; - if (!$nobody_access) { - $booking_types = explode(',', \Request::get('booking_types')); - } - - $begin_timestamp = $begin->getTimestamp(); - $end_timestamp = $end->getTimestamp(); - - //Get the event data sources: - $bookings = \ResourceBooking::findByResourceAndTimeRanges( - $resource, - [ - [ - 'begin' => $begin_timestamp, - 'end' => $end_timestamp - ] - ], - $booking_types - ); - $requests = []; - if ($display_all_requests) { - $requests = \ResourceRequest::findByResourceAndTimeRanges( - $resource, - [ - [ - 'begin' => $begin_timestamp, - 'end' => $end_timestamp - ] - ], - 0 - ); - } elseif ($display_requests) { - //Get the users own request only: - $requests = \ResourceRequest::findByResourceAndTimeRanges( - $resource, - [ - [ - 'begin' => $begin_timestamp, - 'end' => $end_timestamp - ] - ], - 0, - [], - 'user_id = :user_id', - ['user_id' => $current_user->id] - ); - } - - $objects = array_merge($bookings, $requests); - $event_data = \Studip\Fullcalendar::createData($objects, $begin_timestamp, $end_timestamp); - - if ($nobody_access) { - //For nobody users, the code stops here since - //nobody users are not allowed to include additional objects. - return $event_data; - } - - //Check if there are additional objects to be displayed: - $additional_objects = \Request::getArray('additional_objects'); - $additional_object_colours = \Request::getArray('additional_object_colours'); - if ($additional_objects) { - foreach ($additional_objects as $object_class => $object_ids) { - if (!is_a($object_class, '\SimpleORMap', true)) { - continue; - } - if (!is_a($object_class, '\Studip\Calendar\EventSource', true)) { - continue; - } - - $special_colours = []; - if ($additional_object_colours[$object_class]) { - $special_colours = $additional_object_colours[$object_class]; - } - - $additional_objects = $object_class::findMany($object_ids); - foreach ($additional_objects as $additional_object) { - $event_data = $additional_object->getFilteredEventData( - $current_user->id, - null, - null, - $begin, - $end - ); - - if ($special_colours) { - foreach ($event_data as $data) { - $data->text_colour = $special_colours['fg']; - $data->background_colour = $special_colours['bg']; - $data->editable = false; - $event_data[] = $data->toFullcalendarEvent(); - } - } - } - } - } - return $event_data; - } - - - /** - * Returns the booking plan of a resource for a selected semester. - * - * @param semester_id: The ID of the semester. Defaults to the current - * semester, if not set. - * - * @get /resources/resource/:resource_id/semester_plan - */ - public function getResourceSemesterBookingPlan($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - $current_user = \User::findCurrent(); - - if (!$resource->bookingPlanVisibleForUser($current_user)) { - throw new \AccessDeniedException(); - } - - $user_is_resource_user = $resource->userHasPermission( - $current_user, - 'user' - ); - - $display_requests = \Request::get('display_requests'); - $display_all_requests = \Request::get('display_all_requests'); - - $begin = new \DateTime(); - $end = new \DateTime(); - - $semester_id = \Request::get('semester_id'); - $semester = null; - - if ($semester_id) { - $semester = \Semester::find($semester_id); - if (!$semester) { - $this->halt(404, 'Specified semester not found!'); - } - } else { - $semester = \Semester::findCurrent(); - if (!$semester) { - $this->halt(500, 'Current semester not available!'); - } - } - - if (\Request::get('semester_timerange') != 'fullsem') { - $begin->setTimestamp($semester->vorles_beginn); - $end->setTimestamp($semester->vorles_ende); - } else { - $begin->setTimestamp($semester->beginn); - $end->setTimestamp($semester->ende); - } - - //Get parameters: - $booking_types = \Request::getArray('booking_types'); - - $begin_timestamp = $begin->getTimestamp(); - $end_timestamp = $end->getTimestamp(); - - //Get the event data sources: - $bookings = \ResourceBooking::findByResourceAndTimeRanges( - $resource, - [ - [ - 'begin' => $begin_timestamp, - 'end' => $end_timestamp - ] - ], - $booking_types - ); - - $requests = []; - if ($display_all_requests || $display_requests) { - $requests_sql = "INNER JOIN seminar_cycle_dates scd - USING (metadate_id) - WHERE - resource_id = :resource_id - AND - closed = '0' "; - $requests_sql_params = [ - 'begin' => $begin_timestamp, - 'end' => $end_timestamp, - 'resource_id' => $resource->id - ]; - if (!$display_all_requests) { - $requests_sql .= "AND user_id = :user_id "; - $requests_sql_params['user_id'] = $current_user->id; - } - - $requests = \ResourceRequest::findBySql( - $requests_sql, - $requests_sql_params - ); - } - - $merged_objects = []; - $metadates = []; - - foreach ($bookings as $booking) { - $booking->resource = $resource; - $irrelevant_booking = false; - if ($booking->getRepetitionType() != 'weekly') { - if (!\Request::get('display_single_bookings')) { - $irrelevant_booking = true; - } else if ($booking->end < strtotime('today')) { - $irrelevant_booking = true; - } - } - if ($booking->getAssignedUserType() === 'course' && in_array($booking->assigned_course_date->metadate_id, $metadates)) { - $irrelevant_booking = true; - }; - if (!$irrelevant_booking) { - //It is an booking with repetitions that has to be included - //in the semester plan. - if (in_array($booking->getRepetitionType(), ['single','weekly'])) { - $event_list = $booking->convertToEventData([\ResourceBookingInterval::build(['interval_id' => md5(uniqid()), 'begin' => $booking->begin - $booking->preparation_time, 'end' => $booking->end])], $current_user); - } else { - $event_list = $booking->getFilteredEventData(null,null,null,strtotime('today'), $end_timestamp); - } - foreach ($event_list as $event_data) { - if ($booking->getAssignedUserType() === 'course' && $booking->assigned_course_date->metadate_id) { - $index = sprintf( - '%1$s_%2$s_%3$s', - $booking->assigned_course_date->metadate_id, - $event_data->begin->format('NHis'), - $event_data->end->format('NHis') - ); - $metadates[] = $booking->assigned_course_date->metadate_id; - } else { - $index = sprintf( - '%1$s_%2$s_%3$s', - $booking->id, - $event_data->begin->format('NHis'), - $event_data->end->format('NHis') - ); - } - - //Strip some data that cannot be used effectively in here: - $event_data->api_urls = []; - $event_data->editable = false; - - $merged_objects[$index] = $event_data; - } - } - } - - foreach ($requests as $request) { - if ($request->cycle instanceof \SeminarCycleDate) { - $cycle_dates = $request->cycle->getAllDates(); - foreach ($cycle_dates as $cycle_date) { - $relevant_request = $semester->beginn <= $cycle_date->date - && $semester->ende >= $cycle_date->date; - if ($relevant_request) { - //We have found a date for the current semester - //that makes the request relevant. - break; - } - } - if (!$relevant_request) { - continue; - } - $event_data_list = $request->getFilteredEventData( - $current_user->id - ); - - foreach ($event_data_list as $event_data) { - $index = sprintf( - '%1$s_%2$s_%3$s', - $request->metadate_id, - $event_data->begin->format('NHis'), - $event_data->end->format('NHis') - ); - - //Strip some data that cannot be used effectively in here: - $event_data->view_urls = []; - $event_data->api_urls = []; - - $merged_objects[$index] = $event_data; - } - } - } - - //Convert the merged events to Fullcalendar events: - $data = []; - foreach ($merged_objects as $obj) { - $data[] = $obj->toFullCalendarEvent(); - } - - return $data; - } - - - /** - * Gets request of a resource. At your option the requests can be - * limited to a specific time range, specified by the parameters - * begin and end. Furthermore the requests can be filtered by user-ID. - * - * @param begin: A timestamp specifying the begin of the time range. - * @param end: A timestamp specifying the end of the time range. - * @param user_id: This parameter limits the result set to requests - * of the user specified by the user-ID provided in this parameter. - * - * @get /resources/resource/:resource_id/requests - */ - public function getResourceRequests($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'user')) { - throw new \AccessDeniedException(); - } - - $begin = $this->data['begin']; - $end = $this->data['end']; - $user_id = $this->data['user_id']; - - $sql = 'resource_id = :resource_id '; - $sql_array = [ - 'resource_id' => $resource->id - ]; - - if ($begin and $end) { - $sql .= 'AND ((begin >= :begin AND begin <= :end) - OR - (end >= :begin AND end <= :end)) '; - $sql_array['begin'] = $begin; - $sql_array['end'] = $end; - } - - if ($user_id) { - $sql .= 'AND user_id = :user_id '; - $sql_array['user_id'] = $user_id; - } - - $sql .= 'ORDER BY mkdate ASC'; - - $requests = \ResourceRequest::findBySql($sql, $sql_array); - - $result = []; - foreach ($requests as $request) { - $result[] = $request->toRawArray(); - } - - return $result; - } - - - /** - * - * @param begin: A timestamp specifying the begin of the time range. - * @param end: A timestamp specifying the end of the time range. - * @param user_id: This parameter limits the result set to bookings - * of the user specified by the user-ID provided in this parameter. - * @param types: Limits the result to booking types specified in this - * parameter. The allowed types are comma separated like this: "1,2,3". - * The defined types are: - * 0 = normal booking, 1 = reservation, 2 = lock. - * - * @get /resources/resource/:resource_id/bookings - */ - public function getResourceBookings($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'user')) { - throw new \AccessDeniedException(); - } - - $begin = \Request::get('begin'); - $end = \Request::get('end'); - $user_id = \Request::get('user_id'); - $types = []; - $types_str = \Request::get('types'); - if ($types_str) { - $types = explode(',', $types_str); - } - - $sql = 'resource_id = :resource_id '; - $sql_array = [ - 'resource_id' => $resource->id - ]; - - if ($begin and $end) { - $sql .= 'AND ((begin >= :begin AND begin <= :end) - OR - (end >= :begin AND end <= :end)) '; - $sql_array['begin'] = $begin; - $sql_array['end'] = $end; - } - - if ($user_id) { - $sql .= 'AND user_id = :user_id '; - $sql_array['user_id'] = $user_id; - } - if ($types) { - $sql .= 'AND booking_type IN ( :types ) '; - $sql_array['types'] = $types; - } - - $sql .= 'ORDER BY mkdate ASC'; - - $bookings = \ResourceBooking::findBySql($sql, $sql_array); - - $result = []; - if ($bookings) { - foreach ($bookings as $booking) { - $result[] = $booking->toRawArray(); - } - } - - return $result; - } - - - /** - * Creates a booking/reservation/lock for a resource. - * - * @param begin: The begin timestamp for the booking. - * @param end: The end timestamp for the booking. - * @param preparation_time: The amount of seconds for preparation time - * before the begin timestamp. - * @param internal_comment: A comment that is only visible for some - * parts of the staff. - * @param booking_type: The booking type: - * 0 = normal booking - * 1 = reservation - * 2 = lock - * - * @post /resources/resource/:resource_id/assign - */ - public function createResourceBooking($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'user')) { - throw new \AccessDeniedException(); - } - - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); - $preparation_time = \Request::int('preparation_time'); - $internal_comment = \Request::get('internal_comment'); - $booking_type = \Request::int('booking_type'); - - $begin = new \DateTime(); - $begin->setTimestamp($begin_str); - $end = new \DateTime(); - $end->setTimestamp($end_str); - - try { - $booking = $resource->createSimpleBooking( - \User::findCurrent(), - $begin, - $end, - $preparation_time, - $internal_comment, - $booking_type - ); - return $booking; - } catch (\Exception $e) { - $this->halt( - 400, - $e->getMessage() - ); - } - } - - - /** - * Creates a resource request. - * - * @post /resources/resource/:resource_id/request_simple - */ - public function createSimpleResourceRequest($resource_id) - { - $resource = \Resource::find($resource_id); - if (!$resource) { - $this->notFound('Resource object not found!'); - } - - $resource = $resource->getDerivedClassInstance(); - - $user = \User::findCurrent(); - if (!$resource->userHasPermission($user, 'user')) { - throw new \AccessDeniedException(); - } - - $begin_str = \Request::get('begin'); - $end_str = \Request::get('end'); - $comment = \Request::get('comment'); - - $begin = new \DateTime(); - $begin->setTimestamp($begin_str); - $end = new \DateTime(); - $end->setTimestamp($end_str); - - try { - $request = $resource->createSimpleRequest( - $user, - $begin, - $end, - $comment - ); - return $request; - } catch (\Exception $e) { - $this->halt( - 400, - $e->getMessage() - ); - } - } - - - /** - * Change the status of a resource booking interval: - * @post /resources/booking_interval/:interval_id/toggle_takes_place - */ - public function toggleResourceBookingIntervalTakesPlaceField($interval_id) - { - $interval = \ResourceBookingInterval::find($interval_id); - if (!$interval) { - $this->notFound('ResourceBookingInterval object not found!'); - } - - //Get the resource and check the permissions of the user: - $resource = $interval->resource; - if (!$resource) { - $this->halt(500, 'ResourceBookingInterval not linked with a resource!'); - } - - $resource = $resource->getDerivedClassInstance(); - - if (!$resource->userHasPermission(\User::findCurrent(), 'autor', [$interval->begin, $interval->end])) { - $this->halt(403, 'You do not have sufficient permissions to modify the interval!'); - } - - if ( - !$interval->takes_place - && $resource->isAssigned(new \DateTime('@' . $interval->begin), new \DateTime('@' . $interval->end)) - ) { - $this->halt(409, 'Already booked'); - } - //Switch the takes_place field: - $interval->takes_place = $interval->takes_place ? '0' : '1'; - - if ($interval->store()) { - return [ - 'takes_place' => $interval->takes_place - ]; - } else { - $this->halt(500, 'Error while storing the interval!'); - } - } -} diff --git a/app/routes/RoomClipboard.php b/app/routes/RoomClipboard.php deleted file mode 100644 index ffcafe6..0000000 --- a/app/routes/RoomClipboard.php +++ /dev/null @@ -1,322 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * This file contains the REST class for room clipboards - * (clipboards containing room resources). - * - * @author Moritz Strohm <strohm@data-quest.de> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @since 4.5 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class RoomClipboard extends \RESTAPI\RouteMap -{ - //Room clipboard routes: - - /** - * Returns the request/booking plan for a room clipboard. - * - * @get /room_clipboard/:clipboard_id/booking_plan - */ - public function getPlan($clipboard_id = null) - { - if (!$clipboard_id) { - $this->notFound('ID of clipboard has not been provided!'); - } - - $clipboard = \Clipboard::find($clipboard_id); - if (!$clipboard) { - $this->notFound('Clipboard object not found!'); - } - - $current_user = \User::findCurrent(); - - //Permission check: - if ($clipboard->user_id !== $current_user->id) { - throw new \AccessDeniedException(); - } - - $display_requests = \Request::bool('display_requests'); - $display_all_requests = \Request::bool('display_all_requests'); - - $begin_date = \Request::get('start'); - $end_date = \Request::get('end'); - if (!$begin_date || !$end_date) { - //No time range specified. - $this->halt(400, 'The parameters "start" and "end" are missing!'); - return; - } - - //Try the ISO format first: YYYY-MM-DDTHH:MM:SS±ZZ:ZZ - $begin = \DateTime::createFromFormat(\DateTime::RFC3339, $begin_date); - $end = \DateTime::createFromFormat(\DateTime::RFC3339, $end_date); - - if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { - $begin = new \DateTime(); - $end = new \DateTime(); - //Assume the local timezone and use the Y-m-d format: - $date_regex = '/[0-9]{4}-(0[1-9]|1[0-2])-([0-2][0-9]|3[0-1])/'; - if (preg_match($date_regex, $begin_date)) { - //$begin is specified in the date formay YYYY-MM-DD: - $begin_str = explode('-', $begin_date); - $begin->setDate( - intval($begin_str[0]), - intval($begin_str[1]), - intval($begin_str[2]) - ); - $begin->setTime(0,0,0); - } else { - $begin->setTimestamp($begin_date); - } - //Now we do the same for $end_timestamp: - if (preg_match($date_regex, $end_date)) { - //$begin is specified in the date formay YYYY-MM-DD: - $end_str = explode('-', $end_date); - $end->setDate( - intval($end_str[0]), - intval($end_str[1]), - intval($end_str[2]) - ); - $end->setTime(23,59,59); - } else { - $end->setTimestamp($end_date); - } - } - - //Check if a clipboard is selected: - $selected_clipboard_id = $_SESSION['selected_clipboard_id']; - - $rooms = []; - if ($clipboard_id) { - $clipboard = \Clipboard::find($clipboard_id); - } elseif ($selected_clipboard_id) { - $clipboard = \Clipboard::find($selected_clipboard_id); - } else { - $this->halt(400, 'No clipboard selected!'); - } - if ($clipboard) { - $rooms = \Room::findMany($clipboard->getAllRangeIds('Room')); - } else { - $this->halt(404, 'Clipboard not found!'); - } - - $booking_types = \Request::getArray('booking_types'); - - //Room permission check: - $plan_objects = []; - foreach ($rooms as $room) { - if ($room->bookingPlanVisibleForuser($current_user)) { - $plan_objects = array_merge( - $plan_objects, - \ResourceManager::getBookingPlanObjects( - $room, - [ - [ - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp() - ] - ], - $booking_types, - $display_all_requests ? 'all' : $display_requests - ) - ); - } - } - - $data = \Studip\Fullcalendar::createData($plan_objects, $begin, $end); - - return $data; - } - - - /** - * Returns the semester plan for a room clipboard. - * - * @get /room_clipboard/:clipboard_id/semester_plan - */ - public function getSemeterPlan($clipboard_id = null) - { - if (!$clipboard_id) { - $this->notFound('ID of clipboard has not been provided!'); - } - - $clipboard = \Clipboard::find($clipboard_id); - if (!$clipboard) { - $this->notFound('Clipboard object not found!'); - } - - $current_user = \User::findCurrent(); - - //Permission check: - if ($clipboard->user_id !== $current_user->id) { - throw new \AccessDeniedException(); - } - - $display_requests = \Request::bool('display_requests'); - $display_all_requests = \Request::bool('display_all_requests'); - - $begin = new \DateTime(); - $end = new \DateTime(); - - $semester_id = \Request::get('semester_id'); - $semester = null; - - if ($semester_id) { - $semester = \Semester::find($semester_id); - if (!$semester) { - $this->halt(404, 'Specified semester not found!'); - } - } else { - $semester = \Semester::findCurrent(); - if (!$semester) { - $this->halt(500, 'Current semester not available!'); - } - } - - if (\Request::get('semester_timerange') == 'vorles') { - $begin->setTimestamp($semester->vorles_beginn); - $end->setTimestamp($semester->vorles_ende); - } else { - $begin->setTimestamp($semester->beginn); - $end->setTimestamp($semester->ende); - } - - //Check if a clipboard is selected: - $selected_clipboard_id = $_SESSION['selected_clipboard_id']; - - $rooms = []; - if ($clipboard_id) { - $clipboard = \Clipboard::find($clipboard_id); - } elseif ($selected_clipboard_id) { - $clipboard = \Clipboard::find($selected_clipboard_id); - } else { - $this->halt(400, 'No clipboard selected!'); - } - if ($clipboard) { - $rooms = \Room::findMany($clipboard->getAllRangeIds('Room')); - } else { - $this->halt(404, 'Clipboard not found!'); - } - - //Get parameters: - $booking_types = \Request::getArray('booking_types'); - - //Get the event data sources: - $plan_objects = []; - - foreach ($rooms as $room) { - if ($room->bookingPlanVisibleForuser($current_user)) { - $plan_objects = array_merge( - $plan_objects, - \ResourceManager::getBookingPlanObjects( - $room, - [ - [ - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp() - ] - ], - $booking_types, - $display_all_requests ? 'all' : $display_requests - ) - ); - } - } - - $merged_objects = []; - $metadates = []; - foreach ($plan_objects as $plan_object) { - if ($plan_object instanceof \ResourceBooking) { - $irrelevant_booking = - $plan_object->getRepetitionType() != 'weekly' || - ($plan_object->getAssignedUserType() === 'course' && in_array($plan_object->assigned_course_date->metadate_id, $metadates)); - if ($irrelevant_booking) { - continue; - } - - //It is a booking with repetitions that has to be included - //in the semester plan. - - $real_begin = $plan_object->begin; - if ($plan_object->preparation_time > 0) { - $real_begin -= $plan_object->preparation_time; - } - $event_data = $plan_object->convertToEventData([\ResourceBookingInterval::build(['interval_id' => md5(uniqid()), 'begin' => $real_begin, 'end' => $plan_object->end])], $current_user); - - //Merge event data from the same booking that have the - //same weekday and begin and end time into one event. - //If no repetition interval is set and the booking belongs - //to a course date, use the corresponding metadate ID or the - //course date ID in the index. Otherwise use the booking's - //ID (specified by event_data->object_id). - foreach ($event_data as $event) { - if ($plan_object->getAssignedUserType() === 'course') { - $index = sprintf( - '%1$s_%2$s_%3$s', - $plan_object->assigned_course_date->metadate_id, - $event->begin->format('NHis'), - $event->end->format('NHis') - ); - $metadates[] = $plan_object->assigned_course_date->metadate_id; - } else { - $index = sprintf( - '%1$s_%2$s_%3$s', - $plan_object->id, - $event->begin->format('NHis'), - $event->end->format('NHis') - ); - } - - //Strip some data that cannot be used effectively in here: - $event->api_urls = []; - - $merged_objects[$index] = $event; - } - } elseif ($plan_object instanceof \ResourceRequest) { - if ($plan_object->cycle instanceof \SeminarCycleDate) { - $cycle_dates = $plan_object->cycle->getAllDates(); - foreach ($cycle_dates as $cycle_date) { - $relevant_request = $semester->beginn <= $cycle_date->date - && $semester->ende >= $cycle_date->date; - if ($relevant_request) { - //We have found a date for the current semester - //that makes the request relevant. - break; - } - } - if (!$relevant_request) { - continue; - } - $event_data_list = $plan_object->getFilteredEventData( - $current_user->id - ); - - foreach ($event_data_list as $event_data) { - $index = sprintf( - '%1$s_%2$s_%3$s', - $plan_object->metadate_id, - $event_data->begin->format('NHis'), - $event_data->end->format('NHis') - ); - - //Strip some data that cannot be used effectively in here: - $event_data->view_urls = []; - $event_data->api_urls = []; - - $merged_objects[$index] = $event_data; - } - } - } - } - - //Convert the merged events to Fullcalendar events: - $data = []; - foreach ($merged_objects as $obj) { - $data[] = $obj->toFullCalendarEvent(); - } - - return $data; - } -} diff --git a/app/routes/Schedule.php b/app/routes/Schedule.php deleted file mode 100644 index 2341f73..0000000 --- a/app/routes/Schedule.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author André Klaßen <andre.klassen@elan-ev.de> - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition user_id ^[a-f0-9]{1,32}$ - * @condition semester_id ^[a-f0-9]{1,32}$ - */ -class Schedule extends \RESTAPI\RouteMap -{ - /** - * returns schedule for a given user and semester - * - * @get /user/:user_id/schedule/:semester_id - * @get /user/:user_id/schedule - */ - public function getSchedule($user_id, $semester_id = null) - { - if ($user_id !== $GLOBALS['user']->id) { - $this->error(401); - } - - $current_semester = isset($semester_id) - ? \Semester::find($semester_id) - : \Semester::findCurrent(); - - if (!$current_semester) { - $this->notFound('No such semester.'); - } - - $schedule_settings = \UserConfig::get($user_id)->SCHEDULE_SETTINGS; - $days = \CalendarScheduleModel::getDisplayedDays($schedule_settings['glb_days']); - - $entries = \CalendarScheduleModel::getEntries( - $user_id, $current_semester, - $schedule_settings['glb_start_time'], $schedule_settings['glb_end_time'], - $days, - $visible = false - ); - - $json = []; - foreach ($entries as $number_of_day => $schedule_of_day) { - $entries = []; - foreach ($schedule_of_day->entries as $entry) { - $entries[$entry['id']] = self::entryToJson($entry); - } - $json[$number_of_day] = $entries; - } - - $this->etag(md5(serialize($json))); - - return array_reverse($json, true); - } - - - private static function entryToJson($entry) - { - $json = []; - foreach (['start', 'end', 'content', 'title', 'color', 'type'] as $key) { - $json[$key] = in_array($key, ['start', 'end']) - ? (int) $entry[$key] - : $entry[$key]; - } - - return $json; - } -} diff --git a/app/routes/Semester.php b/app/routes/Semester.php deleted file mode 100644 index bdb1ee7..0000000 --- a/app/routes/Semester.php +++ /dev/null @@ -1,115 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition semester_id ^[0-9a-f]{1,32}$ - */ -class Semester extends \RESTAPI\RouteMap -{ - public function __construct() - { - parent::__construct(); - if (!\Request::int('limit')) { - $this->limit = count(\Semester::getAll()); - } - } - - /** - * Returns a list of all semesters. - * - * @get /semesters - * @allow_nobody - */ - public function getSemesters() - { - $semesters = \Semester::getAll(); - - // paginate - $total = count($semesters); - $semesters = array_slice($semesters, $this->offset, $this->limit); - - $json = []; - foreach ($semesters as $semester) { - $url = $this->urlf('/semester/%s', $semester['semester_id']); - $json[$url] = $this->semesterToJSON($semester); - } - - return $this->paginated($json, $total); - } - - /** - * Returns the semester week as string for a given string - * - * @get /semester/:timestamp/week - * @allow_nobody - */ - public function getSemesterWeek(int $timestamp) - { - $semester = \Semester::findByTimestamp($timestamp); - if (!$semester) { - return null; - } - $timestamp = strtotime('today', $timestamp); - $week_begin_timestamp = strtotime('monday this week', $semester->vorles_beginn); - $end_date = $semester->vorles_ende; - - $i = 0; - $result = [ - 'semester_name' => (string)$semester->name, - 'week_number' => sprintf(_('KW %u'), date('W', $timestamp)), - 'current_day' => strftime('%x', $timestamp) - ]; - while ($week_begin_timestamp < $end_date) { - $next_week_timestamp = strtotime('+1 week', $week_begin_timestamp); - if ($week_begin_timestamp <= $timestamp && $timestamp < $next_week_timestamp) { - $result['sem_week'] = sprintf( - _('%u. Vorlesungswoche (ab %s)'), - $i + 1, - strftime('%x', $week_begin_timestamp)); - break; - } - $i += 1; - - $week_begin_timestamp = $next_week_timestamp; - } - - return $result; - } - - /** - * Returns a single semester. - * - * @get /semester/:semester_id - */ - public function getSemester($id) - { - $semester = \Semester::find($id); - if (!$semester) { - $this->notFound(); - } - - $semester_json = $this->semesterToJSON($semester); - $this->etag(md5(serialize($semester_json))); - - return $semester_json; - } - - private function semesterToJSON($semester) - { - return [ - 'id' => $semester['semester_id'], - 'title' => (string) $semester['name'], - 'token' => (string) $semester['semester_token'], - 'begin' => (int) $semester['beginn'], - 'end' => (int) $semester['ende'], - 'seminars_begin' => (int) $semester['vorles_beginn'], - 'seminars_end' => (int) $semester['vorles_ende'], - 'visible' => (int) $semester['visible'], - ]; - } -} diff --git a/app/routes/Studip.php b/app/routes/Studip.php deleted file mode 100644 index 749a53a..0000000 --- a/app/routes/Studip.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -use Config; -use SemClass; -use SemType; - -/** - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - */ -class Studip extends \RESTAPI\RouteMap -{ - /** - * Grundlegende Systemeinstellungen - * - * @get /studip/settings - */ - public function getSettings() - { - $sem_types = array_map(function ($item) { - return [ - 'name' => $item['name'], - 'class' => $item['class'], - ]; - }, SemType::getTypes()); - - $sem_classes = array_map(function ($item) { - $item = (array) $item; - return reset($item); - }, SemClass::getClasses()); - - return [ - 'ALLOW_CHANGE_USERNAME' => Config::get()->ALLOW_CHANGE_USERNAME, - 'ALLOW_CHANGE_EMAIL' => Config::get()->ALLOW_CHANGE_EMAIL, - 'ALLOW_CHANGE_NAME' => Config::get()->ALLOW_CHANGE_NAME, - 'ALLOW_CHANGE_TITLE' => Config::get()->ALLOW_CHANGE_TITLE, - 'INST_TYPE' => $GLOBALS['INST_TYPE'], - 'SEM_TYPE' => $sem_types, - 'SEM_CLASS' => $sem_classes, - 'TERMIN_TYP' => $GLOBALS['TERMIN_TYP'], - 'PERS_TERMIN_KAT' => $GLOBALS['PERS_TERMIN_KAT'], - 'SUPPORT_EMAIL' => $GLOBALS['UNI_CONTACT'], - 'TITLES' => $GLOBALS['DEFAULT_TITLE_FOR_STATUS'], - 'UNI_NAME_CLEAN' => Config::get()->UNI_NAME_CLEAN, - ]; - } - - /** - * Farbeinstellungen - * - * @get /studip/colors - */ - public function getColors() - { - // TODO: Move these definitions somewhere else (but where!?) - return [ - 'background' => '#e1e4e9', - 'dark' => '#34578c', - 'light' => '#899ab9', - ]; - } -} diff --git a/app/routes/User.php b/app/routes/User.php deleted file mode 100644 index d3cce26..0000000 --- a/app/routes/User.php +++ /dev/null @@ -1,300 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author André Klaßen <andre.klassen@elan-ev.de> - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition user_id ^[0-9a-f]{1,32}$ - */ -class User extends \RESTAPI\RouteMap -{ - /**************************************************/ - /* PUBLIC STATIC HELPER METHODS */ - /**************************************************/ - - public static function getMiniUser($routemap, $user) - { - $avatar = \Avatar::getAvatar($user->id); - - return [ - 'id' => $user->id, - 'href' => $routemap->urlf('/user/%s', [htmlReady($user->id)]), - 'name' => self::getNamesOfUser($user), - 'avatar_small' => $avatar->getURL(\Avatar::SMALL), - 'avatar_medium' => $avatar->getURL(\Avatar::MEDIUM), - 'avatar_normal' => $avatar->getURL(\Avatar::NORMAL), - 'avatar_original' => $avatar->getURL(\Avatar::NORMAL) - ]; - } - - public static function getNamesOfUser($user) - { - $name = [ - 'username' => $user->username, - 'formatted' => $user->getFullName(), - 'family' => $user->nachname, - 'given' => $user->vorname, - 'prefix' => $user->title_front, - 'suffix' => $user->title_rear - ]; - return $name; - } - - - /**************************************************/ - /* ROUTES */ - /**************************************************/ - - /** - * Searches for users by a given keyword. - * - * @get /users - */ - public function searchUsers() - { - $needle = \Request::get('q') ?? \Request::get('needle'); - if (!$needle) { - $this->halt(400, 'Missing search paramter ?q='); - } - - $query = \GlobalSearchUsers::getSQL($needle, [], $this->offset + $this->limit); - $result = \DBManager::get()->fetchAll($query); - $total = (int) \DBManager::get()->fetchColumn('SELECT FOUND_ROWS() as found_rows'); - - $user_ids = array_column($result, 'user_id'); - $users = \User::findMany($user_ids); - - return $this->paginated( - array_map(function ($user) { - return self::getMiniUser($this, $user); - }, $users), - $total - ); - } - - - /** - * getUser - retrieves data of a user - * - * @get /user/:user_id - * @get /user - */ - public function getUser($user_id = '') - { - $user_id = $user_id ?: $GLOBALS['user']->id; - - $user = \User::findFull($user_id); - if (!$user) { - $this->halt(404, sprintf('User %s not found', $user_id)); - } - - $visibilities = get_local_visibility_by_id($user_id, 'homepage'); - if (is_array(json_decode($visibilities, true))) { - $visibilities = json_decode($visibilities, true); - } else { - $visibilities = []; - } - - $get_field = function ($field, $visibility) use ($user_id, $user, $visibilities) { - if (!$user[$field] - || !is_element_visible_for_user($GLOBALS['user']->id, $user_id, $visibilities[$visibility])) - { - return ''; - } - return $user[$field]; - }; - - $avatar = \Avatar::getAvatar($user_id); - - $user = [ - 'user_id' => $user_id, - 'username' => $user['username'], - 'name' => self::getNamesOfUser($user), - 'perms' => $user['perms'], - 'email' => get_visible_email($user_id), - 'avatar_small' => $avatar->getURL(\Avatar::SMALL), - 'avatar_medium' => $avatar->getURL(\Avatar::MEDIUM), - 'avatar_normal' => $avatar->getURL(\Avatar::NORMAL), - 'avatar_original' => $avatar->getURL(\Avatar::NORMAL), - 'phone' => $get_field('privatnr', 'private_phone'), - 'homepage' => $get_field('Home', 'homepage'), - 'privadr' => strip_tags($get_field('privadr', 'privadr')), - ]; - - // Data fields - $datafields = []; - foreach (\DataFieldEntry::getDataFieldEntries($user_id, 'user') as $entry) { - if (!$entry->isVisible()) { - continue; - } - if (!\Visibility::verify($entry->getID(), $user_id)) { - continue; - } - $datafields[] = [ - 'type' => $entry->getType(), - 'id' => $entry->getId(), - 'name' => (string) $entry->getName(), - 'value' => $entry->getValue(), - ]; - } - $user['datafields'] = $datafields; - - $this->etag(md5(serialize($user))); - - return $user; - - } - - - /** - * deleteUser - deletes a user - * - * @delete /user/:user_id - */ - public function deleteUser($user_id) - { - if (!$GLOBALS['perm']->have_perm('root')) { - $this->error(401); - } - - if (!$GLOBALS['user']->id === $user_id) { - $this->error(400, 'Must not delete yourself'); - } - - $user = \User::find($user_id); - $user->delete(); - - $this->status(204); - } - - - /** - * returns institutes for a given user - * - * @get /user/:user_id/institutes - */ - public function getInstitutes($user_id) - { - $user = \User::find($user_id); - if (!$user) { - $this->notFound(sprintf('User %s not found', $user_id)); - } - - $query = "SELECT i0.Institut_id AS institute_id, i0.Name AS name, - inst_perms AS perms, sprechzeiten AS consultation, - raum AS room, ui.telefon AS phone, ui.fax, - i0.Strasse AS street, i0.Plz AS city, - i1.Name AS faculty_name, i1.Strasse AS faculty_street, - i1.Plz AS faculty_city - FROM user_inst AS ui - JOIN Institute AS i0 USING (Institut_id) - LEFT JOIN Institute AS i1 ON (i0.fakultaets_id = i1.Institut_id) - WHERE visible = 1 AND user_id = :user_id - ORDER BY priority ASC"; - $statement = \DBManager::get()->prepare($query); - $statement->bindValue(':user_id', $user_id); - $statement->execute(); - - $institutes = [ - 'work' => [], - 'study' => [], - ]; - - foreach ($statement->fetchAll(\PDO::FETCH_ASSOC) as $row) { - if ($row['perms'] === 'user') { - $institutes['study'][] = $row; - } else { - $institutes['work'][] = $row; - } - } - - $this->etag(md5(serialize($institutes))); - - $result = array_slice($institutes, $this->offset, $this->limit); - return $this->paginated( - $result, - count($institutes['study']) + count($institutes['work']), - compact('user_id') - ); - } - - - /** - * Get the root file folder of a user's file area. - * - * @get /user/:user_id/top_folder - */ - public function getTopFolder($user_id) - { - $user = \User::find($user_id); - if (!$user) { - $this->notFound("User with id {$user_id} not found!"); - } - - if ($user->id !== \User::findCurrent()->id) { - $this->error(403, 'You are not allowed to see another user\'s personal file area!'); - } - - $top_folder = \Folder::findTopFolder($user->id, 'user'); - - if (!$top_folder) { - $this->notFound("No folder found for user with id {$user_id}!"); - } - - return (new FileSystem())->getFolder($top_folder->id); - } - - /** - * Patches the course member data of a user and course. Pass data to be - * patched via a valid json object in the body. Fields that my be patched: - * - * - group - the associated group in the overview of the users's courses - * - visibility - visible state of the course - * - * @patch /user/:user_id/courses/:course_id - * - * @todo more patchable fields? - */ - public function patchCourseGroup($user_id, $course_id) - { - $user = \User::find($user_id); - if (!$user) { - $this->notFound('User not found'); - } - - if ($user->id !== $GLOBALS['user']->id) { - $this->halt(403, "You may not alter this user's data"); - } - - $member = \CourseMember::find([$course_id, $user->id]); - if (!$member) { - $this->notFound('You are not a member of the course'); - } - - if (isset($this->data['group'])) { - if (!is_numeric($this->data['group']) || $this->data['group'] < 0 || $this->data['group'] > 8) { - $this->halt(400, 'Given group is not inside the valid range 0..8'); - } - $member->gruppe = $this->data['group']; - } - - if (isset($this->data['visibility'])) { - if (in_array($member->status, ['tutor', 'dozent'])) { - $this->halt(400, 'You may not change the visibility status for this course since you are a teacher.'); - } - if (!in_array($this->data['visibility'], ['yes', 'no'])) { - $this->halt(400, 'Visibility may only be "yes" or "no".'); - } - $member->visible = $this->data['visibility']; - } - - if ($member->isDirty()) { - $member->store(); - } - - $this->halt(204); - } -} diff --git a/app/routes/UserConfig.php b/app/routes/UserConfig.php deleted file mode 100644 index ba01538..0000000 --- a/app/routes/UserConfig.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -use RESTAPI\RouteMap; -use RESTAPI\Router; - -/** - * API routes for accessing user config values. - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @license GPL2 or any later version - * @since Stud.IP 3.4 - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition user_id ^[0-9a-f]{1,32}$ - * - * @status 404 if user does not exist - * @status 403 if user may access the request config item - */ -class UserConfig extends RouteMap -{ - // Stores the user's config instance - private $config; - - /** - * Performs checks if the user exists and may actually access the - * requested config. - * - * @param Router $router Instance of the api router - * @param array $handler Detected handler router - * @param array $parameters Parameters of the called route - */ - public function before(Router $router, array $handler, array $parameters) - { - // Check whether user exist - if (\User::find($parameters['user_id']) === null) { - $this->error(404, sprintf('User %s not found', $parameters['user_id'])); - } - - // Check whether user accesses own config or user is root - if ($parameters['user_id'] !== $GLOBALS['user']->id && $GLOBALS['user']->perms !== 'root') { - $this->error(403, 'User may only access own config'); - } - - $this->config = \UserConfig::get($parameters['user_id']); - } - - /** - * Returns the value of a specific config entry for a given user - * - * @get /user/:user_id/config/:field - * - * @return mixed Value for the request config item - * @status 404 if config item does not exist - */ - public function getConfig($user_id, $field) - { - // Check whether key exists in config - if (!isset($this->config[$field])) { - $this->error(404, sprintf('No config item for field %s and user %s', - $field, $user_id)); - } - - return $this->config[$field]; - } - - /** - * Stored the value of a specific config entry for a given user - * - * @put /user/:user_id/config/:field - * - * @status 204 on success - * @status 400 if no value is given - */ - public function setConfig($user_id, $field) - { - if (!isset($this->data['value'])) { - $this->error(400, 'No value given in request'); - } - - $this->config->store($field, $this->data['value']); - - $this->status(204); - } - - /** - * Removes a specific config entry for a given user - * - * @delete /user/:user_id/config/:field - * - * @status 204 on success - */ - public function deleteConfig($user_id, $field) - { - $this->config->delete($field); - - $this->status(204); - } -} diff --git a/app/routes/Wiki.php b/app/routes/Wiki.php deleted file mode 100644 index 7f54628..0000000 --- a/app/routes/Wiki.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -namespace RESTAPI\Routes; - -/** - * @author <mlunzena@uos.de> - * @license GPL 2 or later - * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0. - * - * @condition range_id ^[0-9a-f]{1,32}$ - */ -class Wiki extends \RESTAPI\RouteMap -{ - public function before() - { - require_once 'User.php'; - } - - /** - * Wikiseitenindex einer Veranstaltung - * - * @get /course/:range_id/wiki - */ - public function getCourseWiki($range_id) - { - $pages = \WikiPage::findBySQL("`range_id` = ? ORDER BY `name` ASC", [$range_id]); - - if (!$pages[0]->isReadable()) { - $this->error(401); - } - - $total = sizeof($pages); - $pages = $pages->limit($this->offset, $this->limit); - - $linked_pages = []; - foreach ($pages as $page) { - $url = $this->urlf('/course/%s/wiki/%s', [$range_id, htmlReady($page['keyword'])]); - $linked_pages[$url] = $this->wikiPageToJson($page, ["content"]); - } - - $this->etag(md5(serialize($linked_pages))); - - return $this->paginated($linked_pages, $total, compact('range_id')); - } - - /** - * Wikiseite auslesen - * - * @get /course/:range_id/wiki/:keyword - * @get /course/:range_id/wiki/:keyword/:version - */ - public function getCourseWikiKeyword($range_id, $keyword, $version = null) - { - $page = $this->requirePage($range_id, $keyword, $version); - $wiki_json = $this->wikiPageToJson($page); - $this->etag(md5(serialize($wiki_json))); - $this->lastmodified($page->chdate); - return $wiki_json; - } - - /** - * Wikiseite ändern/hinzufügen - * - * @put /course/:range_id/wiki/:keyword - */ - public function putCourseWikiKeyword($range_id, $keyword) - { - if (!isset($this->data['content'])) { - $this->error(400, 'No content provided'); - } - - $page =\WikiPage::findOneBySQL("`range_id` = ? AND `name` = ?", [$range_id, $keyword]); - if (!$page) { - $page = new \WikiPage(); - $page->range_id = $range_id; - $page->name = $keyword; - } - - if (!$page->isEditable()) { - $this->error(401); - } - - $page->content = $this->data['content']; - $page->store(); - - $url = sprintf('course/%s/wiki/%s/%d', htmlReady($range_id), htmlReady($keyword), count($page->versions) + 1); - $this->redirect($url, 201, 'ok'); - } - - /**************************************************/ - /* PRIVATE HELPER METHODS */ - /**************************************************/ - - private function requirePage($range_id, $keyword, $version = null) - { - $page = \WikiPage::findOneBySQL("`range_id` = ? AND `name` = ?", [$range_id, $keyword]); - - if (!$page) { - $this->notFound(); - } - - if (!$page->isReadable($GLOBALS['user']->id)) { - $this->error(401); - } - if ($version !== null && $version !== count($page->versions) + 1) { - return $page->versions[count($page->versions) - 1 - $version]; - } else { - return $page; - } - } - - private function wikiPageToJson($page, $without = []) - { - $json = [ - 'range_id' => $page->range_id, - 'keyword' => $page->name, - 'chdate' => $page->chdate, - 'version' => 1 - ]; - - // (pre-rendered) content - if (!in_array('content', $without)) { - $json['content'] = $page->content; - $json['content_html'] = wikiReady($page->content, true, $page->range_id, $page->id); - } - if (!in_array('user', $without)) { - if ($page->author) { - $json['user'] = User::getMiniUser($this, $page->user_id); - } - } - - foreach ($without as $key) { - if (isset($json[$key])) { - unset($json[$key]); - } - } - - // string to int conversions as SORM does not know about ints - foreach (['chdate', 'mkdate', 'filesize', 'downloads'] as $key) { - if (isset($json[$key])) { - $json[$key] = (int) $json[$key]; - } - } - - return $json; - } - - -} diff --git a/app/views/accessibility/forms/report_barrier.php b/app/views/accessibility/forms/report_barrier.php index 5dd8d7d..51ca079 100644 --- a/app/views/accessibility/forms/report_barrier.php +++ b/app/views/accessibility/forms/report_barrier.php @@ -1,2 +1,2 @@ -<?= MessageBox::info(_('Auf dieser Seite können Sie eine Barriere melden, die die Nutzbarkeit von Stud.IP für Sie einschränkt. Füllen Sie dazu das untenstehende Formular aus.'))->hideClose() ?></p> +<?= MessageBox::info(_('Auf dieser Seite können Sie eine Barriere melden, die die Nutzbarkeit von Stud.IP für Sie einschränkt. Füllen Sie dazu das folgende Formular aus.'))->hideClose() ?></p> <?= $form->render() ?> diff --git a/app/views/activityfeed/configuration.php b/app/views/activityfeed/configuration.php index ceb87eb..9161db0 100644 --- a/app/views/activityfeed/configuration.php +++ b/app/views/activityfeed/configuration.php @@ -15,7 +15,7 @@ <? foreach ($provider as $prv_id => $prv_name) : ?> <label> <input type="checkbox" name="provider[<?= $context ?>][]" value="<?= htmlReady($prv_id) ?>" - <?= empty($config) || (is_array($config[$context]) && in_array($prv_id, $config[$context])) ? 'checked' : ''?>> + <?= empty($config) || (isset($config[$context]) && is_array($config[$context]) && in_array($prv_id, $config[$context])) ? 'checked' : ''?>> <?= htmlReady($prv_name) ?> </label> <? endforeach ?> diff --git a/app/views/admin/api/config.php b/app/views/admin/api/config.php deleted file mode 100644 index 83d2ae5..0000000 --- a/app/views/admin/api/config.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * @var Admin_ApiController $controller - * @var array $config - */ -use Studip\Button, Studip\LinkButton; -?> - -<form class="default" action="<?= $controller->url_for('admin/api/config') ?>" method="post"> - <fieldset> - <legend><?= _('Konfiguration') ?></legend> - - <input type="hidden" name="active" value="0"> - <label> - <input type="checkbox" name="active" value="1" <? if ($config['API_ENABLED']) echo 'checked'; ?>> - <?= _('REST-API aktiviert') ?> - </label> - - - <label class="caption" for="auth"> - <?= _('Standard-Authentifizierung beim Login') ?> - <select name="auth" id="auth"> - <? foreach ($GLOBALS['STUDIP_AUTH_PLUGIN'] as $plugin): ?> - <option <? if ($config['API_OAUTH_AUTH_PLUGIN'] === $plugin) echo 'selected'; ?>> - <?= $plugin ?> - </option> - <? endforeach; ?> - </select> - </label> - </fieldset> - <footer> - <?= Button::createAccept(_('Speichern')) ?> - <?= LinkButton::createCancel(_('Abbrechen'), $controller->url_for('admin/api')) ?> - </footer> -</form> diff --git a/app/views/admin/api/edit.php b/app/views/admin/api/edit.php deleted file mode 100644 index f1c7e03..0000000 --- a/app/views/admin/api/edit.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -/** - * @var Admin_ApiController $controller - * @var RESTAPI\Consumer\Base $consumer - * @var array $types - */ -use Studip\Button, Studip\LinkButton; -?> - -<? if ($consumer->id): ?> - <h1> - <?= sprintf( - _('Registrierte Applikation "%s" bearbeiten'), - htmlReady($consumer->title) - ) ?> - </h1> -<? else: ?> - <h1 class="hide-in-dialog"> - <?= _('Neue Applikation registrieren') ?> - </h1> -<? endif; ?> - -<form class="settings default" - action="<?= $controller->url_for('admin/api/edit', $consumer->id) ?>" method="post"> - <?= CSRFProtection::tokenTag() ?> - - <fieldset> - <legend><?= _('Grundeinstellungen') ?></legend> - - <label for="active"> - <input type="checkbox" class="switch" id="active" name="active" value="1" - <?= $consumer->active ? 'checked' : '' ?>> - <?= _('Aktiviert') ?> - </label> - - - <label for="title"> - <?= _('Titel') ?> - <input required type="text" id="title" name="title" - placeholder="<?= _('Beispiel-Applikation') ?>" - value="<?= htmlReady($consumer->title) ?>" - maxlength="128"> - </label> - - <label for="contact"> - <?= _('Kontaktperson') ?> - <input required type="text" id="contact" name="contact" - placeholder="John Doe" - value="<?= htmlReady($consumer->contact) ?>" - maxlength="255"> - </label> - - <label for="email"> - <?= _('Kontaktadresse') ?> - <input required type="text" id="email" name="email" - placeholder="support@appsite.tld" - value="<?= htmlReady($consumer->email) ?>" - maxlength="255"> - </label> - - <label for="callback"> - <?= _('Callback URL') ?> - <input required type="text" id="callback" name="callback" - placeholder="http://appsite.tld/auth" - value="<?= htmlReady($consumer->callback) ?>" - maxlength="255"> - </label> - - <? if ($consumer->id): ?> - <label for="consumer_key"> - <?= _('Consumer Key') ?> - <input readonly type="text" id="consumer_key" - value="<?= htmlReady($consumer->auth_key) ?>"> - </label> - - <label for="consumer_secret"> - <?= _('Consumer Secret') ?> - <input readonly type="text" id="consumer_secret" - value="<?= htmlReady($consumer->auth_secret) ?>"> - </label> - - <div class="centered"> - <?= strftime(_('Erstellt am %d.%m.%Y %H:%M:%S'), $consumer->mkdate) ?><br> - <? if ($consumer->mkdate != $consumer->chdate): ?> - <?= strftime(_('Zuletzt geändert am %d.%m.%Y %H:%M:%S'), $consumer->chdate) ?> - <? endif; ?> - </div> - <? endif; ?> - </fieldset> - - <fieldset> - <legend><?= _('Applikation-Details') ?></legend> - - <label for="commercial"> - <input type="checkbox" class="switch" id="commercial" name="commercial" value="1" - <?= $consumer->commercial ? 'checked' : '' ?>> - <?= _('Kommerziell') ?> - </label> - - <label for="description"> - <?= _('Beschreibung') ?> - <textarea id="description" name="description" maxlength="65535"><?= htmlReady($consumer->description) ?></textarea> - </label> - - <label for="url"> - <?= _('URL') ?> - <input type="text" id="url" name="url" - placeholder="http://appsite.tld" - value="<?= htmlReady($consumer->url) ?>" - maxlength="255"> - </label> - - <label for="type"> - <?= _('Typ') ?> - <select name="type" id="type"> - <option value="">- <?= _('Keine Angabe') ?> -</option> - <? foreach ($types as $type => $label): ?> - <option value="<?= $type ?>" <?= $consumer->type == $type ? 'selected' : '' ?>> - <?= $label ?> - </option> - <? endforeach; ?> - </select> - </label> - - - <label for="notes"> - <?= _('Notizen') ?> - <textarea id="notes" name="notes" maxlength="65535"><?= htmlReady($consumer->notes) ?></textarea> - </label> - </fieldset> - - <footer data-dialog-button> - <?= Button::createAccept(_('Speichern'), 'store') ?> - <?= LinkButton::createCancel(_('Abbrechen'), $controller->url_for('admin/api')) ?> - </footer> -</form> diff --git a/app/views/admin/api/index.php b/app/views/admin/api/index.php deleted file mode 100644 index 132deac..0000000 --- a/app/views/admin/api/index.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * @var Admin_ApiController $controller - * @var RESTAPI\Consumer\Base[] $consumers - * @var array $types - */ -?> -<? if (!empty($consumers)): ?> -<form action="#" method="post" class="default"> -<table class="default"> - <caption><?= _('Registrierte Applikationen') ?></caption> - <thead> - <tr> - <th><?= ('Aktiv') ?></th> - <th><?= _('Name') ?></th> - <th><?= _('Typ') ?></th> - <th><?= _('Kontakt') ?></th> - <th><?= _('Kommerziell') ?></th> - <th> </th> - </tr> - </thead> - <tbody> -<? foreach ($consumers as $consumer): ?> - <tr> - <td id="<?= $consumer->id ?>"> - <a href="<?= $controller->url_for('admin/api/toggle', $consumer->id, $consumer->active ? 'off' : 'on') ?>"> - <?= Icon::create('checkbox-' . ($consumer->active ? '' : 'un') . 'checked', 'clickable')->asImg() ?> - </a> - </td> - <td> - <? if ($consumer->url): ?> - <a href="<?= htmlReady($consumer->url) ?>" target="_blank" rel="noopener noreferrer"> - <?= htmlReady($consumer->title) ?> - </a> - <? else: ?> - <?= htmlReady($consumer->title) ?> - <? endif; ?> - </td> - <td><?= $types[$consumer->type] ?? ' ' ?></td> - <td> - <a href="mailto:<?= htmlReady($consumer->email) ?>"> - <?= htmlReady($consumer->contact) ?> - </a> - </td> - - <td><?= Icon::create('checkbox-' . ($consumer->commercial ? '' : 'un') . 'checked', 'clickable')->asImg() ?></td> - <td class="actions"> - <a href="<?= $controller->url_for('admin/api/keys', $consumer->id) ?>" - data-dialog="size=auto" - title="<?= htmlReady(sprintf(_('Schlüssel anzeigen für Applikation "%s"'), $consumer->title)) ?>"> - <?= Icon::create('info-circle', 'clickable')->asImg() ?> - </a> - <a href="<?= $controller->url_for('admin/api/edit', $consumer->id) ?>" title="<?= _('Applikation bearbeiten') ?>" data-dialog> - <?= Icon::create('edit', 'clickable')->asImg() ?> - </a> - <a href="<?= $controller->url_for('admin/api/permissions', $consumer->id) ?>" title="<?= _('Zugriffsberechtigungen verwalten') ?>"> - <?= Icon::create('admin', 'clickable')->asImg() ?> - </a> - <?= Icon::create('trash')->asInput([ - 'formaction' => $controller->url_for('admin/api/delete/', $consumer->id), - 'title' => sprintf(_('Applikation "%s" entfernen'), $consumer->title), - 'data-confirm' => '', - 'style' => 'vertical-align: middle' - ]) ?> - </td> - </tr> -<? endforeach; ?> - </tbody> -</table> -</form> - -<? else: ?> -<p> - <?= MessageBox::info(_('Es wurde noch keine Applikation registriert.'), - [sprintf(_('Klicken Sie <a href="%s">hier</a>, um eine Applikation zu registrieren.'), $controller->url_for('admin/api/edit'))]) ?> -</p> -<? endif; ?> diff --git a/app/views/admin/api/permissions.php b/app/views/admin/api/permissions.php deleted file mode 100644 index 9eb48e3..0000000 --- a/app/views/admin/api/permissions.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * @var Admin_ApiController $controller - * @var RESTAPI\ConsumerPermissions $permissions - * @var string $consumer_id - * @var array $routes - * @var bool $global - */ -?> -<form action="<?= $controller->url_for('admin/api/permissions', $consumer_id) ?>" method="post" class="default"> -<table class="default"> - <thead> - <tr> - <th><?= _('Zugriff') ?></th> - <th><?= _('Route') ?></th> - <th><?= _('Methoden') ?></th> - <th><?= _('Zugriff auf') ?></th> - <th><?= _('Quelle') ?></th> - </tr> - </thead> -<? foreach ($routes as $route => $methods): ?> - <tbody> - - <? $i = 0; ?> - <? foreach ($methods as $method => $info): ?> - <tr style="vertical-align: top;"> - <td> - <input type="hidden" name="permission[<?= urlencode($route) ?>][<?= urlencode($method) ?>]" value="0"> - <input type="checkbox" name="permission[<?= urlencode($route) ?>][<?= urlencode($method) ?>]" - <? if (!$global || $global->check($route, $method)): ?> - <? if ($permissions->check($route, $method)) echo 'checked'; ?> - <? else: ?> - disabled - <? endif; ?> - value="1"> - </td> - <? if ($i++): ?> - <td> </td> - <? else: ?> - <td><?= htmlReady($route) ?></td> - <? endif; ?> - <td><?= htmlReady($method) ?></td> - <td><?= htmlReady($info['description']) ?></td> - <td><?= $info['source'] ?></td> - </tr> - <? endforeach; ?> - </tbody> -<? endforeach; ?> - <tfoot> - <tr> - <td> - <label> - <input type="checkbox" data-proxyfor="[name^=permission]:checkbox"> <?= _('Alle') ?> - </label> - </td> - <td colspan="4"> - <?= Studip\Button::createAccept(_('Speichern'), 'store') ?> - </td> - </tr> - </tfoot> -</table> -</form> diff --git a/app/views/admin/autoinsert/manual.php b/app/views/admin/autoinsert/manual.php index dd0f834..a4f5f5e 100644 --- a/app/views/admin/autoinsert/manual.php +++ b/app/views/admin/autoinsert/manual.php @@ -114,14 +114,14 @@ <? foreach ($values[$type] as $key => $value): ?> <? if (is_array($value)): ?> <option value="<?= $key ?>" - class="nested-item-header" <?= in_array($key, (array)@$filter[$type]) ? 'selected="selected"' : '' ?>><?= htmlReady($value['name']) ?></option> + class="nested-item-header" <?= in_array($key, $filter[$type] ?? []) ? 'selected' : '' ?>><?= htmlReady($value['name']) ?></option> <? foreach ($value['values'] as $k => $v): ?> <option value="<?= $k ?>" - class="nested-item" <?= in_array($k, (array)@$filter[$type]) ? 'selected="selected"' : '' ?>><?= htmlReady($v) ?></option> + class="nested-item" <?= in_array($k, $filter[$type] ?? []) ? 'selected' : '' ?>><?= htmlReady($v) ?></option> <? endforeach; ?> <? else: ?> <option - value="<?= $key ?>" <?= in_array($key, (array)@$filter[$type]) ? 'selected="selected"' : '' ?>><?= htmlReady($value) ?></option> + value="<?= $key ?>" <?= in_array($key, $filter[$type] ?? []) ? 'selected' : '' ?>><?= htmlReady($value) ?></option> <? endif ?> <? endforeach; ?> </select> diff --git a/app/views/admin/configuration/table-row.php b/app/views/admin/configuration/table-row.php index 12769ed..a0725a5 100644 --- a/app/views/admin/configuration/table-row.php +++ b/app/views/admin/configuration/table-row.php @@ -9,12 +9,16 @@ ?> <tr id="field-<?= htmlReady($field) ?>"> <td> + <? if (!Config::get()->fromEnv($field)): ?> <a data-dialog href="<?= $controller->link_for($linkchunk, compact('field')) ?>"> <?= htmlReady($field) ?> </a> - <? if (!empty($description)): ?> - <br><small><?= htmlReady($description) ?></small> - <? endif; ?> + <? else: ?> + <?= htmlReady($field) ?> + <? endif; ?> + <? if (!empty($description)): ?> + <br><small><?= htmlReady($description) ?></small> + <? endif; ?> </td> <td class="wrap-content"> <? if ($type === 'string' || $type === 'i18n'): ?> @@ -31,8 +35,14 @@ </td> <td><?= htmlReady($type) ?></td> <td class="actions"> + <? if (!Config::get()->fromEnv($field)): ?> <a data-dialog="size=auto" href="<?= $controller->link_for($linkchunk, compact('field')) ?>"> <?= Icon::create('edit')->asImg(['title' => _('Konfigurationsparameter bearbeiten')]) ?> </a> + <? else: ?> + <?= Icon::create('ufo', Icon::ROLE_INFO)->asImg([ + 'title' => _('Wert wurde über eine Umgebungsvariable gesetzt und kann an dieser Stelle nicht verändert werden.'), + ]) ?> + <? endif; ?> </td> </tr> diff --git a/app/views/admin/configuration/type-edit.php b/app/views/admin/configuration/type-edit.php index 9fddb45..1cec3f0 100644 --- a/app/views/admin/configuration/type-edit.php +++ b/app/views/admin/configuration/type-edit.php @@ -16,7 +16,7 @@ <input name="value" type="number" id="item-value" value="<?= htmlReady($value) ?>"> <? elseif ($type === 'array') : ?> - <?php $v = version_compare(PHP_VERSION, '5.4.0', '>=') ? json_encode($value, JSON_UNESCAPED_UNICODE) : json_encode($value) ?> + <?php $v = json_encode($value, JSON_UNESCAPED_UNICODE) ?> <textarea cols="80" rows="5" name="value" id="item-value"><?= htmlReady($v, true, true) ?></textarea> <? elseif ($type === 'i18n'): ?> <?= I18N::textarea('value', $value, [ diff --git a/app/views/admin/courseplanning/pick_color.php b/app/views/admin/courseplanning/pick_color.php index dbd93a8..61cc7fc 100644 --- a/app/views/admin/courseplanning/pick_color.php +++ b/app/views/admin/courseplanning/pick_color.php @@ -13,10 +13,12 @@ <div id="event-color-picker"></div> +<? if (!empty($semtype)): ?> <label> <input name="event_color_semtype" type="checkbox" value="1"> <?= sprintf(_('Farbtyp für alle VA dieses Typs (%s) übernehmen'), htmlReady($semtype)) ?> </label> +<? endif; ?> <div data-dialog-button> <?= Studip\Button::createAccept(_('Speichern'), 'save') ?> diff --git a/app/views/admin/courses/courses.php b/app/views/admin/courses/courses.php deleted file mode 100644 index d225270..0000000 --- a/app/views/admin/courses/courses.php +++ /dev/null @@ -1,270 +0,0 @@ -<?php -/** - * @var string $selected_action - * @var array $view_filter - * @var Semester $semester - * @var int $count_courses - * @var string $sortby - * @var string $sortFlag - * @var int $nav_elements - * @var array $courses - */ - -$colspan = 2 -?> - -<? if (!empty($actions[$selected_action]['multimode'])) : ?> - <form action="<?= URLHelper::getLink($actions[$selected_action]['url']) ?>" method="post" - <?= !empty($actions[$selected_action]['dialogform']) ? ' data-dialog="auto"' : '' ?>> -<? endif ?> -<?= CSRFProtection::tokenTag() ?> -<table class="default course-admin"> - <colgroup> - <col style="width: 2%"> - <? if (in_array('number', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 8%"> - <? endif ?> - <? if (in_array('name', $view_filter)) : ?> - <? $colspan++ ?> - <col> - <? endif ?> - <? if (in_array('type', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 10%"> - <? endif ?> - <? if (in_array('room_time', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 30%"> - <? endif ?> - <? if (in_array('semester', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 10%"> - <? endif ?> - <? if (in_array('institute', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 10%"> - <? endif ?> - <? if (in_array('requests', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 5%"> - <? endif ?> - <? if (in_array('teachers', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 10%"> - <? endif ?> - <? if (in_array('members', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 3%"> - <? endif ?> - <? if (in_array('waiting', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 5%"> - <? endif ?> - <? if (in_array('preliminary', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 5%"> - <? endif ?> - <? if (in_array('contents', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 8%"> - <? endif ?> - <? if (in_array('last_activity', $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 8%"> - <? endif ?> - <? foreach (PluginManager::getInstance()->getPlugins("AdminCourseContents") as $plugin) : ?> - <? foreach ($plugin->adminAvailableContents() as $index => $label) : ?> - <? if (in_array($plugin->getPluginId()."_".$index, $view_filter)) : ?> - <? $colspan++ ?> - <col style="width: 8%"> - <? endif ?> - <? endforeach ?> - <? endforeach ?> - <col style="width: 15%"> - - </colgroup> - <caption> - <? if (!$GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE) : ?> - <?= _('Veranstaltungen') ?> - <? else : ?> - <?= htmlReady(sprintf(_('Veranstaltungen im %s'), $semester->name)) ?> - <? endif ?> - <span class="actions"> - <?= sprintf('%u %s', $count_courses, $count_courses > 1 ? _('Veranstaltungen') : _('Veranstaltung')) ?> - </span> - </caption> - <thead> - <tr class="sortable"> - <? if (Config::get()->ADMIN_COURSES_SHOW_COMPLETE): ?> - <th <? if ($sortby === 'completion') printf('class="sort%s"', mb_strtolower($sortFlag)) ?>> - <a href="<?= URLHelper::getLink('', ['sortby' => 'completion', 'sortFlag' => mb_strtolower($sortFlag)]) ?>" class="course-completion" title="<?= _('Bearbeitungsstatus') ?>"> - <?= _('Bearbeitungsstatus') ?> - </a> - </th> - <? else: ?> - <th> - - </th> - <? endif; ?> - <? if (in_array('number', $view_filter)) : ?> - <th <?= ($sortby == 'VeranstaltungsNummer') ? sprintf('class="sort%s"', mb_strtolower($sortFlag)) : '' ?>> - <a href="<?= - URLHelper::getLink('', ['sortby' => 'VeranstaltungsNummer', - 'sortFlag' => mb_strtolower($sortFlag)]) ?>"> - <?= _('Nr.') ?> - </a> - </th> - <? endif ?> - <? if (in_array('name', $view_filter)) : ?> - <th <?= ($sortby == 'Name') ? sprintf('class="sort%s"', mb_strtolower($sortFlag)) : '' ?>> - <a href="<?= - URLHelper::getLink('', ['sortby' => 'Name', - 'sortFlag' => mb_strtolower($sortFlag)]) ?>"> - <?= _('Name') ?> - </a> - </th> - <? endif ?> - <? if (in_array('type', $view_filter)) : ?> - <th <?= ($sortby == 'status') ? sprintf('class="sort%s"', mb_strtolower($sortFlag)) : '' ?>> - <a href="<?= - URLHelper::getLink('', ['sortby' => 'status', - 'sortFlag' => mb_strtolower($sortFlag)]) ?>"> - <?= _("VA-Typ") ?> - </a> - </th> - <? endif ?> - <? if (in_array('room_time', $view_filter)) : ?> - <th><?= _('Raum/Zeit') ?></th> - <? endif ?> - <? if (in_array('semester', $view_filter)) : ?> - <th <?= ($sortby == 'start_time') ? sprintf('class="sort%s"', mb_strtolower($sortFlag)) : '' ?>> - <a href="<?= URLHelper::getLink('', ['sortby' => 'start_time', 'sortFlag' => mb_strtolower($sortFlag)]) ?>"> - <?= _('Semester') ?> - </a> - </th> - <? endif ?> - <? if (in_array('institute', $view_filter)) : ?> - <th <?= ($sortby == 'institute') ? sprintf('class="sort%s"', mb_strtolower($sortFlag)) : '' ?>> - <a href="<?= URLHelper::getLink('', ['sortby' => 'institute', 'sortFlag' => mb_strtolower($sortFlag)]) ?>"> - <?= _('Einrichtung') ?> - </a> - </th> - <? endif ?> - <? if (in_array('requests', $view_filter)) : ?> - <th <?= ($sortby == 'requests') ? sprintf('class="sort%s"', mb_strtolower($sortFlag)) : '' ?>> - <a href="<?= - URLHelper::getLink('', ['sortby' => 'requests', - 'sortFlag' => mb_strtolower($sortFlag)]) ?>"> - <abbr title="<?= _('Raumanfragen') ?>"> - <?= _('RA') ?> - </abbr> - </a> - </th> - <? endif ?> - <? if (in_array('teachers', $view_filter)) : ?> - <th><?= _('Lehrende') ?></th> - <? endif ?> - <? if (in_array('members', $view_filter)) : ?> - <th <?= ($sortby == 'teilnehmer') ? sprintf('class="sort%s"', mb_strtolower($sortFlag)) : '' ?>> - <a href="<?= URLHelper::getLink('', ['sortby' => 'teilnehmer', - 'sortFlag' => mb_strtolower($sortFlag)]) ?>"> - <abbr title="<?= _('Teilnehmende') ?>"> - <?= _('TN') ?> - </abbr> - </a> - </th> - <? endif ?> - <? if (in_array('waiting', $view_filter)) : ?> - <th <? if ($sortby == 'waiting') printf('class="sort%s"', mb_strtolower($sortFlag)); ?>> - <a href="<?= URLHelper::getLink('', ['sortby' => 'waiting', - 'sortFlag' => mb_strtolower($sortFlag)]) ?>"> - <?= _('Warteliste') ?> - </a> - </th> - <? endif ?> - <? if (in_array('preliminary', $view_filter)) : ?> - <th <?= ($sortby == 'prelim') ? sprintf('class="sort%s"', mb_strtolower($sortFlag)) : '' ?>> - <a href="<?= - URLHelper::getLink('', ['sortby' => 'prelim', - 'sortFlag' => mb_strtolower($sortFlag)]) ?>"><?= _('Vorläufig') ?></a> - </th> - <? endif ?> - <? if (in_array('contents', $view_filter)) : ?> - <th style="width: <?= $nav_elements * 27 ?>px"> - <?= _('Inhalt') ?> - </th> - <? endif ?> - <? if (in_array('last_activity', $view_filter)) : ?> - <th style="width: <?= $nav_elements * 27 ?>px"> - <?= _('letzte Aktivität') ?> - </th> - <? endif ?> - <? foreach (PluginManager::getInstance()->getPlugins("AdminCourseContents") as $plugin) : ?> - <? foreach ($plugin->adminAvailableContents() as $index => $label) : ?> - <? if (in_array($plugin->getPluginId()."_".$index, $view_filter)) : ?> - <th style="width: <?= $nav_elements * 27 ?>px"><?= htmlReady($label) ?></th> - <? endif ?> - <? endforeach ?> - <? endforeach ?> - <th style="text-align: center" class="actions"> - <?= _('Aktion') ?> - </th> - </tr> - <? if (!empty($actions[$selected_action]['multimode'])) : ?> - <?= $this->render_partial('admin/courses/additional_inputs.php', compact('colspan')) ?> - <? if (count($courses) > 10): ?> - <tr> - <th colspan="<?= $colspan ?>" style="text-align: right"> - <? if (is_a($actions[$selected_action]['multimode'], "\\Studip\\Button")) : ?> - <?= $actions[$selected_action]['multimode'] ?> - <? else : ?> - <?= Studip\Button::createAccept( - is_string($actions[$selected_action]['multimode']) - ? $actions[$selected_action]['multimode'] - : $actions[$selected_action]['title'], - 'save_action', - $selected_action == 16 ? ['data-dialog' => 1] : null) ?> - <? endif ?> - </th> - </tr> - <? endif; ?> - <? endif ?> - </thead> - <tbody> - <? foreach ($courses as $semid => $values) : ?> - <?= $this->render_partial('admin/courses/_course', - [ - 'semid' => $semid, - 'values' => $values, - 'view_filter' => $view_filter, - 'actions' => $actions, - 'selected_action' => $selected_action, - 'courses' => $courses, - 'parent' => $parent ?? null - ] - ) ?> - <? endforeach ?> - </tbody> -<? if (!empty($actions[$selected_action]['multimode'])) : ?> - <tfoot> - <tr> - <td colspan="<?= $colspan ?>" style="text-align: right"> - <? if (is_a($actions[$selected_action]['multimode'], "\\Studip\\Button")) : ?> - <?= $actions[$selected_action]['multimode'] ?> - <? else : ?> - <?= Studip\Button::createAccept( - is_string($actions[$selected_action]['multimode']) - ? $actions[$selected_action]['multimode'] - : $actions[$selected_action]['title'], - $actions[$selected_action]['name'], - $selected_action == 16 ? ['data-dialog' => 1] : null) ?> - <? endif ?> - </td> - </tr> - </tfoot> - <? endif ?> -</table> -<? if (!empty($actions[$selected_action]['multimode'])) : ?> -</form> -<? endif ?> diff --git a/app/views/admin/cronjobs/logs/index.php b/app/views/admin/cronjobs/logs/index.php index 1073d1b..d17a4a5 100644 --- a/app/views/admin/cronjobs/logs/index.php +++ b/app/views/admin/cronjobs/logs/index.php @@ -135,7 +135,7 @@ use Studip\Button, Studip\LinkButton; <tfoot> <tr> <td colspan="6"> - <select name="action" data-activates="button[name=bulk]"> + <select name="action" data-activates="button[name=bulk]" aria-label="<?= _('Aktion auswählen') ?>"> <option value="">- <?= _('Aktion auswählen') ?></option> <option value="delete"><?= _('Löschen') ?></option> </select> diff --git a/app/views/admin/cronjobs/schedules/display.php b/app/views/admin/cronjobs/schedules/display.php index 8eb9075..ddf1d21 100644 --- a/app/views/admin/cronjobs/schedules/display.php +++ b/app/views/admin/cronjobs/schedules/display.php @@ -16,9 +16,6 @@ <dt><?= _('Aktiv') ?></dt> <dd><?= $schedule->active ? _('Ja') : _('Nein') ?></dd> - <dt><?= _('Priorität') ?></dt> - <dd><?= CronjobSchedule::describePriority($schedule->priority) ?></dd> - <? if (count($schedule->parameters) > 0): ?> <dt><?= _('Parameter') ?></dt> <dd> @@ -33,23 +30,8 @@ <dt><?= _('Aufgabe') ?></dt> <dd><?= htmlReady($schedule->task->name) ?></dd> - <dt><?= _('Typ') ?></dt> -<? if ($schedule->type === 'once'): ?> - <dd> - <?= sprintf(_('Einmalig am %s um %s'), date('d.m.Y', $schedule->next_execution), date('H:i', $schedule->next_execution)) ?> - </dd> - - <dt><?= _('Ausgeführt') ?>?</dt> - <dd> - <? if ($schedule->execution_count > 0): ?> - <?= _('Ja') ?>, <?= sprintf(_('am %s um %s'), date('d.m.Y', $schedule->last_execution), date('H:i:s', $schedule->last_execution)) ?> - <? else: ?> - <?= _('Nein') ?> - <? endif; ?> - </dd> -<? else: ?> + <dt><?= _('Ausführungsrhytmus') ?></dt> <dd> - <?= _('Regelmässig') ?> <?= $this->render_partial('admin/cronjobs/schedules/periodic-schedule', $schedule->toArray()) ?> </dd> @@ -68,8 +50,6 @@ <dt><?= _('Letztes Ergebnis') ?></dt> <dd><code><?= htmlReady($schedule->last_result) ?></code></dd> <? endif; ?> - -<? endif; ?> </dl> <div data-dialog-button> diff --git a/app/views/admin/cronjobs/schedules/edit.php b/app/views/admin/cronjobs/schedules/edit.php index 96be249..8754510 100644 --- a/app/views/admin/cronjobs/schedules/edit.php +++ b/app/views/admin/cronjobs/schedules/edit.php @@ -42,17 +42,6 @@ $days_of_week = [ </label> <label> - <?= _('Priorität') ?> - <select name="priority" id="priority"> - <? foreach (CronjobSchedule::getPriorities() as $priority => $label): ?> - <option value="<?= $priority ?>" <? if ((!$schedule->priority && $priority === CronjobSchedule::PRIORITY_NORMAL) || $schedule->priority === $priority) echo 'selected'; ?>> - <?= htmlReady($label) ?> - </option> - <? endforeach; ?> - </select> - </label> - - <label> <?= _('Titel') ?> <input type="text" name="title" id="title" value="<?= htmlReady($schedule->title ?: '') ?>"> </label> @@ -118,14 +107,6 @@ $days_of_week = [ <fieldset> <legend><?= _('Zeitplan') ?></legend> - <label> - <input type="radio" name="type" value="periodic" - data-activates="[name^='periodic']" - data-deactivates="[name^='once']" - <? if ($schedule->type === 'periodic') echo 'checked'; ?>> - <?= _('Wiederholt') ?> - </label> - <section> <table class="default"> <colgroup> @@ -218,26 +199,6 @@ $days_of_week = [ </tbody> </table> </section> - - <label> - <input type="radio" name="type" value="once" - data-activates="input[name^='once']" - data-deactivates="[name^='periodic']" - <? if ($schedule->type === 'once') echo 'checked'; ?>> - <?= _('Einmalig') ?> - </label> - - <label class="col-1"> - <?= _('Datum') ?> - <input type="text" name="once[date]" data-date-picker class="size-s" - value="<? if ($schedule->type === 'once' && $schedule->next_execution) echo date('d.m.Y', $schedule->next_execution); ?>"> - </label> - - <label class="col-1"> - <?= _('Uhrzeit') ?> - <input type="text" name="once[time]" data-time-picker class="size-s" - value="<? if ($schedule->type === 'once' && $schedule->next_execution) echo date('H:i', $schedule->next_execution) ?>"> - </label> </fieldset> <footer class="buttons"> diff --git a/app/views/admin/cronjobs/schedules/index.php b/app/views/admin/cronjobs/schedules/index.php index 6160875..79a65be 100644 --- a/app/views/admin/cronjobs/schedules/index.php +++ b/app/views/admin/cronjobs/schedules/index.php @@ -18,30 +18,18 @@ use Studip\Button, Studip\LinkButton; <?= sprintf(_('Passend: %u von %u Cronjobs'), count($schedules), $total) ?> <? endif; ?> </legend> - <label class="col-2"> - <?= _('Typ') ?> - <select name="filter[type]" id="type" class="submit-upon-select"> - <option value=""><?= _('Alle Cronjobs anzeigen') ?></option> - <option value="once" <? if ($filter['type'] === 'once') echo 'selected'; ?>> - <?= _('Nur einmalige Cronjobs anzeigen') ?> - </option> - <option value="periodic" <? if ($filter['type'] === 'periodic') echo 'selected'; ?>> - <?= _('Nur regelmässige Cronjobs anzeigen') ?> - </option> - </select> - </label> - <label class="col-2"> + <label class="col-3"> <?= _('Aufgabe') ?> <select name="filter[task_id]" id="task_id" class="submit-upon-select"> <option value=""><?= _('Alle Cronjobs anzeigen') ?></option> <? foreach ($tasks as $task): ?> - <option value="<?= $task->task_id ?>" <? if ($filter['task_id'] === $task->task_id) echo 'selected'; ?>> + <option value="<?= $task->task_id ?>" <? if (isset($filter['task_id']) && $filter['task_id'] === $task->task_id) echo 'selected'; ?>> <?= htmlReady($task->name) ?> </option> <? endforeach; ?> </select> </label> - <label class="col-2"> + <label class="col-3"> <?= _('Status') ?> <select name="filter[status]" id="status" class="submit-upon-select"> <option value=""><?= _('Alle Cronjobs anzeigen') ?></option> @@ -74,7 +62,6 @@ use Studip\Button, Studip\LinkButton; <col style="width: 20px"> <col> <col style="width: 40px"> - <col style="width: 100px"> <col style="width: 30px"> <col style="width: 30px"> <col style="width: 30px"> @@ -91,7 +78,6 @@ use Studip\Button, Studip\LinkButton; </th> <th data-sort="text"><?= _('Cronjob') ?></th> <th data-sort="htmldata"><?= _('Aktiv') ?></th> - <th data-sort="text"><?= _('Typ') ?></th> <th colspan="5" data-sort="false"><?= _('Ausführung') ?></th> <th data-sort="false"><?= _('Optionen') ?></th> </tr> @@ -124,14 +110,7 @@ use Studip\Button, Studip\LinkButton; </a> <? endif; ?> </td> - <td><?= $schedule->type === 'once' ? _('Einmalig') : _('Regelmässig') ?></td> - <? if ($schedule->type === 'once'): ?> - <td colspan="5"> - <?= strftime('%x %R', $schedule->next_execution) ?> - </td> - <? else: ?> - <?= $this->render_partial('admin/cronjobs/schedules/periodic-schedule', $schedule->toArray() + ['display' => 'table-cells']) ?> - <? endif; ?> + <?= $this->render_partial('admin/cronjobs/schedules/periodic-schedule', $schedule->toArray() + ['display' => 'table-cells']) ?> <td style="text-align: right"> <a data-dialog href="<?= $controller->display($schedule) ?>"> <?= Icon::create('admin')->asImg(['title' => _('Cronjob anzeigen')]) ?> @@ -152,7 +131,7 @@ use Studip\Button, Studip\LinkButton; </tbody> <tfoot> <tr> - <td colspan="10"> + <td colspan="9"> <select name="action" data-activates=".cronjobs button[name=bulk]" aria-label="<?= _('Aktion auswählen')?>"> <option value="">- <?= _('Aktion auswählen') ?> -</option> <option value="activate"><?= _('Aktivieren') ?></option> diff --git a/app/views/admin/cronjobs/tasks/index.php b/app/views/admin/cronjobs/tasks/index.php index c39df93..0edd191 100644 --- a/app/views/admin/cronjobs/tasks/index.php +++ b/app/views/admin/cronjobs/tasks/index.php @@ -78,7 +78,7 @@ use Studip\Button; <tfoot> <tr> <td colspan="6"> - <select name="action" data-activates=".cronjobs button[name=bulk]"> + <select name="action" data-activates=".cronjobs button[name=bulk]" aria-label="<?= _('Aktion auswählen') ?>"> <option value="">- <?= _('Aktion auswählen') ?></option> <option value="activate"><?= _('Aktivieren') ?></option> <option value="deactivate"><?= _('Deaktivieren') ?></option> diff --git a/app/views/admin/datafields/new.php b/app/views/admin/datafields/new.php index 0c6f696..e64293f 100644 --- a/app/views/admin/datafields/new.php +++ b/app/views/admin/datafields/new.php @@ -45,7 +45,7 @@ use Studip\Button, Studip\LinkButton; <?= _('Veranstaltungskategorie') ?> <? elseif ($object_typ === 'inst'): ?> <?= _('Einrichtungstyp') ?> - <? elseif ($object_typ === 'moduldeskriptor' || $object_type === 'modulteildeskriptor') : ?> + <? elseif ($object_typ === 'moduldeskriptor' || $object_typ === 'modulteildeskriptor') : ?> <?= _('Sprache') ?> <? elseif ($object_typ === 'studycourse'): ?> <?= _('Typ/Abschnitt') ?> diff --git a/app/views/admin/lockrules/_form.php b/app/views/admin/lockrules/_form.php index c4ef111..b96c7b6 100644 --- a/app/views/admin/lockrules/_form.php +++ b/app/views/admin/lockrules/_form.php @@ -85,12 +85,14 @@ use Studip\Button; </td> <td> <input type="radio" - name="lockdata_attributes[<?= $attr ?>]" <?= ($lock_rule['attributes'][$attr] ? 'checked' : '') ?> - value="1"/> + name="lockdata_attributes[<?= $attr ?>]" + <?= !empty($lock_rule['attributes'][$attr]) ? 'checked' : '' ?> + value="1"> </td> <td> <input type="radio" - name="lockdata_attributes[<?= $attr ?>]" <?= (!$lock_rule['attributes'][$attr] ? 'checked' : '') ?> + name="lockdata_attributes[<?= $attr ?>]" + <?= empty($lock_rule['attributes'][$attr]) ? 'checked' : '' ?> value="0"/> </td> </tr> diff --git a/app/views/admin/plugin/edit_automaticupdate.php b/app/views/admin/plugin/edit_automaticupdate.php index 7e87c31..2feb1bc 100644 --- a/app/views/admin/plugin/edit_automaticupdate.php +++ b/app/views/admin/plugin/edit_automaticupdate.php @@ -46,7 +46,7 @@ if ($_SERVER['HTTPS'] == 'on' && $_SERVER['SERVER_PORT'] != 443 || <fieldset> <legend><?= _("Daten für das bereitstellende System") ?></legend> <p class="info"> - <?= _("Tragen Sie bei gitlab, github.com oder dem Pluginmarktplatz untenstehende URL ein, die der Webhook aufrufen soll.") ?> + <?= _("Tragen Sie bei gitlab, github.com oder dem Pluginmarktplatz die folgende URL ein, die der Webhook aufrufen soll.") ?> <? if ($plugin['automatic_update_secret']) : ?> <?= _("Dieser Aufruf muss noch mit dem Sicherheitstoken abgesichert werden.") ?> <? endif ?> diff --git a/app/views/admin/tree/create.php b/app/views/admin/tree/create.php index 027507a..55d26a5 100644 --- a/app/views/admin/tree/create.php +++ b/app/views/admin/tree/create.php @@ -20,8 +20,8 @@ <? foreach ($GLOBALS['SEM_TREE_TYPES'] as $index => $type) : ?> <option value="<?= htmlReady($index) ?>"> <?= $type['name'] ?: _('Standard') ?> - <?= !$type['editable'] ? _('(nicht mehr nachträglich änderbar)') : '' ?> - <?= $type['hidden'] ? _('(dieser Knoten ist versteckt)') : '' ?> + <?= empty($type['editable']) ? _('(nicht mehr nachträglich änderbar)') : '' ?> + <?= !empty($type['hidden']) ? _('(dieser Knoten ist versteckt)') : '' ?> </option> <? endforeach ?> </select> diff --git a/app/views/admin/user/edit.php b/app/views/admin/user/edit.php index 75ca932..47596a9 100644 --- a/app/views/admin/user/edit.php +++ b/app/views/admin/user/edit.php @@ -29,7 +29,10 @@ use Studip\Button, Studip\LinkButton; <? if ($user->locked): ?> <br> <span style="color: red"> - (<?= sprintf(_('gesperrt von %s'), htmlReady(get_fullname($user->locked_by))) ?> + (<?= htmlReady(sprintf( + _('gesperrt von %s'), + $user->locked_by ? get_fullname($user->locked_by) : _('unbekannt') + )) ?> <? if ($user->lock_comment): ?> , <?= _('Kommentar') ?>: <?= htmlReady($user->lock_comment) ?> <? endif; ?> @@ -341,7 +344,7 @@ use Studip\Button, Studip\LinkButton; <br> - <? if ($user->online->last_lifesign): ?> + <? if (!empty($user->online->last_lifesign)): ?> <abbr title="<?= strftime('%x %X', $user->online->last_lifesign) ?>"> <?= reltime($user->online->last_lifesign, true, 2) ?> </abbr> @@ -431,7 +434,7 @@ use Studip\Button, Studip\LinkButton; <?= sprintf( '%s, %s, %s. %s', htmlReady($usc->studycourse->name), - htmlReady($usc->degree->name), + htmlReady($usc->degree->name ?? _('Unbekannt')), htmlReady($usc->semester), _('Fachsemester') ) ?> diff --git a/app/views/admin/webservice_access/index.php b/app/views/admin/webservice_access/index.php index 07edb4c..223e5c3 100644 --- a/app/views/admin/webservice_access/index.php +++ b/app/views/admin/webservice_access/index.php @@ -28,7 +28,7 @@ </th> </tr> <? foreach ($ws_rules as $rule): ?> - <tr class="<?= TextHelper::cycle('table_row_even', 'table_row_odd') ?>"> + <tr> <? if (isset($edit) && $edit == $rule->id) :?> <td> <a name="edit"></a> diff --git a/app/views/api/authorizations/index.php b/app/views/api/authorizations/index.php deleted file mode 100644 index 95645f4..0000000 --- a/app/views/api/authorizations/index.php +++ /dev/null @@ -1,44 +0,0 @@ -<? use Studip\Button, Studip\LinkButton; ?> - -<? if (empty($consumers)): ?> -<?= MessageBox::info(_('Sie haben noch keinen Apps Zugriff auf Ihren Account gewährt.')) ?> -<? else: ?> -<table class="oauth-apps default"> - <caption><?= _('Applikationen') ?></caption> - <thead> - <tr> - <th><?= _('Name') ?></th> - <th> </th> - </thead> - <tbody> - <? foreach ($consumers as $consumer): ?> - <tr> - <td> - <h3> - <? if ($consumer->url): ?> - <a href="<?= htmlReady($consumer->url) ?>" target="_blank" rel="noopener noreferrer"> - <?= htmlReady($consumer->title) ?> - </a> - <? else: ?> - <?= htmlReady($consumer->title) ?> - <? endif; ?> - <? if (isset($types[$consumer->type])): ?> - <small>(<?= htmlReady($types[$consumer->type]) ?>)</small> - <? endif; ?> - </h3> - <? if ($consumer->description): ?> - <p><?= htmlReady($consumer->description) ?></p> - <? endif; ?> - </td> - <td class="actions"> - <?= LinkButton::createCancel( - _('App entfernen'), - $controller->url_for('api/authorizations/revoke', $consumer->id), - ['data-confirm' => _('Wollen Sie der App wirklich den Zugriff auf Ihre Daten untersagen?')] - ) ?> - </td> - </tr> -<? endforeach; ?> - </tbody> -</table> -<? endif; ?> diff --git a/app/views/api/oauth/authorize.php b/app/views/api/oauth/authorize.php deleted file mode 100644 index 6c66532..0000000 --- a/app/views/api/oauth/authorize.php +++ /dev/null @@ -1,34 +0,0 @@ -<section class="oauth authorize"> - <p> - <?= sprintf( - _('Die Applikation <strong>%s</strong> möchte auf Ihre Daten zugreifen.'), - htmlReady($consumer->title) - ) ?> - </p> - - <form action="<?= $controller->url_for('api/oauth/authorize?oauth_token=' . $token) ?>" method="post"> - <input type="hidden" name="oauth_callback" value="<?= htmlReady($oauth_callback) ?>"> - <p> - <?= Studip\Button::createAccept(_('Erlauben'), 'allow') ?> - <?= Studip\LinkButton::createCancel(_('Verweigern'), $consumer->callback) ?> - </p> - </form> - - <p> - <?= Avatar::getAvatar($GLOBALS['user']->id)->getImageTag(Avatar::SMALL) ?> - - <?= sprintf( - _('Angemeldet als <strong>%s</strong> (%s)'), - htmlReady($GLOBALS['user']->getFullName()), - htmlReady($GLOBALS['user']->username) - ) ?><br> - <small> - <a href="<?= URLHelper::getLink('logout.php') ?>"> - <?= sprintf( - _('Sind sie nicht <strong>%s</strong>, so melden Sie sich bitte ab und versuchen es erneut.'), - htmlReady($GLOBALS['user']->getFullName()) - ) ?> - </a> - </small> - </p> -</section> diff --git a/app/views/api/oauth/authorized.php b/app/views/api/oauth/authorized.php deleted file mode 100644 index e69de29..0000000 --- a/app/views/api/oauth/authorized.php +++ /dev/null diff --git a/app/views/blubber/compose.php b/app/views/blubber/compose.php index 3b8b2a0..3ea46c4 100644 --- a/app/views/blubber/compose.php +++ b/app/views/blubber/compose.php @@ -1,68 +1,79 @@ -<form class="default" action="<?= $controller->compose($thread ? $thread->getId() : null) ?>" method="post"> +<?php +/** + * @var BlubberController $controller + * @var BlubberThread $thread + * @var Contact[] $contacts + */ +?> - <?= CSRFProtection::tokenTag() ?> +<form class="default" action="<?= $controller->compose($thread ? $thread->getId() : null) ?>" method="post" data-dialog> - <div <?= !$thread ? "" : 'style="display: none;"' ?>> - <div class="file_select_possibilities"> + <?= CSRFProtection::tokenTag() ?> - <a href="#" onclick="jQuery('.file_select_possibilities').hide(); jQuery('.private_blubber_composer').show(); return false;"> + <div class="file_select_possibilities" <?= !$thread ? "" : 'style="display: none;"' ?>> + <div> + <a href="#" + onclick="$('.file_select_possibilities').hide(); $('.private_blubber_composer').show(); return false;"> <?= Icon::create('group3')->asImg(50) ?> <?= _('Kontakte') ?> </a> - <a href="<?= $controller->link_for("blubber/index/global") ?>"> + <a href="<?= $controller->link_for('blubber/index/global') ?>"> <?= Icon::create('globe')->asImg(50) ?> <?= _('Öffentlich') ?> </a> <? if (!$GLOBALS['perm']->have_perm('admin')) : ?> - <a href="#" onclick="jQuery('.file_select_possibilities').hide(); jQuery('.course_blubber_composer').show(); return false;"> + <a href="#" + onclick="$('.file_select_possibilities').hide(); $('.course_blubber_composer').show(); return false;"> <?= Icon::create('seminar')->asImg(50) ?> <?= _('Veranstaltung') ?> </a> <? endif ?> - </div> </div> <div class="course_blubber_composer" style="display: none;"> - <? if (!$GLOBALS['perm']->have_perm("admin")) : ?> - <ul class="clean"> - <? foreach (CourseMember::findBySQL("INNER JOIN seminare USING (Seminar_id) WHERE user_id = ? ORDER BY seminare.name ASC", [$GLOBALS['user']->id]) as $member) : ?> - <li> - <a href="<?= $controller->to_course($member['seminar_id']) ?>"> - <?= CourseAvatar::getAvatar($member['seminar_id'])->getImageTag(Avatar::SMALL) ?> - <?= htmlReady($member->course['name']) ?> - </a> - </li> - <? endforeach ?> - </ul> - <? endif ?> + <? if (!$GLOBALS['perm']->have_perm('admin')) : ?> + <ul class="clean"> + <? foreach (CourseMember::findBySQL("INNER JOIN seminare USING (Seminar_id) WHERE user_id = ? ORDER BY seminare.name ASC", [$GLOBALS['user']->id]) as $member) : ?> + <li> + <a href="<?= $controller->to_course($member['seminar_id']) ?>"> + <?= CourseAvatar::getAvatar($member['seminar_id'])->getImageTag(Avatar::SMALL) ?> + <?= htmlReady($member->course['name']) ?> + </a> + </li> + <? endforeach ?> + </ul> + <? endif ?> </div> <div class="private_blubber_composer" style="display: none;"> - <label for="blubber_contacts"> - <?= _('Kontakte') ?> - </label> - <div class="blubber_composer_select_container"> + + <? if (!empty($contacts)) : ?> + <div class="blubber_composer_select_container"> <span class="container"> - <select name="user_ids[]" class="select2" id="blubber_contacts" multiple> + <label> + <?= _('Kontakte') ?> + <select name="user_ids[]" class="select2" id="blubber_contacts" multiple> <? foreach ($contacts as $contact) : ?> - <option value="<?= htmlReady($contact['user_id']) ?>" data-avatar="<?= htmlReady(Avatar::getAvatar($contact['user_id'])->getImageTag(Avatar::SMALL)) ?>"> + <option value="<?= htmlReady($contact->user_id) ?>" + data-avatar="<?= htmlReady(Avatar::getAvatar($contact['user_id'])->getImageTag(Avatar::SMALL)) ?>"> <?= htmlReady($contact->friend->getFullName()) ?> </option> <? endforeach ?> </select> + </label> </span> - <a href="" onClick="$('#blubber_contacts').focus().select2('open'); return false;"> - <?= Icon::create("search", "clickable")->asImg(20, ['class' => "text-bottom"]) ?> - </a> - <a href="" onClick="$('#blubber_contacts').val(null).trigger('change'); return false;"> - <?= Icon::create("decline", "clickable")->asImg(20, ['class' => "text-bottom"]) ?> - </a> - </div> - + <a href="" onClick="$('#blubber_contacts').trigger('focus').select2('open'); return false;"> + <?= Icon::create('search')->asImg(['class' => 'text-bottom']) ?> + </a> + <a href="" onClick="$('#blubber_contacts').val(null).trigger('change'); return false;"> + <?= Icon::create('decline',)->asImg(['class' => "text-bottom"]) ?> + </a> + </div> + <? endif ?> <script> jQuery(function ($) { let format = function (state) { @@ -83,7 +94,6 @@ STUDIP.Blubber.Composer.init(); }); </script> - <div class=".more_persons"> <?= _('Weitere Personen') ?> @@ -92,19 +102,21 @@ <input type="hidden" name="user_ids[]" :value="user.user_id"> <span>{{ user.name }}</span> <a href="#" @click.prevent="removeUser"> - <studip-icon shape="trash" :size="20" role="clickable"></studip-icon> + <studip-icon shape="trash"></studip-icon> </a> </li> </ul> + <quicksearch :searchtype="quicksearch" name="qs" @input="addRange" :placeholder="$gettext('Suchen')"></quicksearch> <div class="blubber_composer_select_container"> <?= QuickSearch::get('search_user_id', new StandardSearch('user_id')) - ->fireJSFunctionOnSelect('STUDIP.Blubber.Composer.vue.addUser')->render() ?> + ->setInputStyle('width: 90%') + ->fireJSFunctionOnSelect('STUDIP.Blubber.Composer.vue.addUser')->render() ?> - <a href="" onClick="$('input[name=search_user_id_parameter]').focus(); return false;"> - <?= Icon::create("search", "clickable")->asImg(20, ['class' => "text-bottom"]) ?> + <a href="" onClick="$('input[name=search_user_id_parameter]').trigger('focus'); return false;"> + <?= Icon::create('search')->asImg(['class' => "text-bottom"]) ?> </a> <a href="" onClick="STUDIP.Blubber.Composer.vue.clearUsers(); return false;"> - <?= Icon::create("decline", "clickable")->asImg(20, ['class' => "text-bottom"]) ?> + <?= Icon::create('decline')->asImg(['class' => "text-bottom"]) ?> </a> </div> </div> diff --git a/app/views/calendar/calendar/export.php b/app/views/calendar/calendar/export.php index 3fa302e..b4171fe 100644 --- a/app/views/calendar/calendar/export.php +++ b/app/views/calendar/calendar/export.php @@ -7,8 +7,8 @@ * @var DateTimeImmutable $end */ ?> -<form class="default" method="post" - action="<?= $controller->link_for('calendar/calendar/export/' . $user_id) ?>"> +<form class="default" method="post" data-dialog="size=auto" + action="<?= $controller->link_for('calendar/calendar/export', $user_id ?? null) ?>"> <?= CSRFProtection::tokenTag() ?> <fieldset> <legend><?= _('Termine exportieren') ?></legend> diff --git a/app/views/calendar/contentbox/_termin.php b/app/views/calendar/contentbox/_termin.php index ded7d0b..3daf699 100644 --- a/app/views/calendar/contentbox/_termin.php +++ b/app/views/calendar/contentbox/_termin.php @@ -13,19 +13,19 @@ </span> <? if ($admin && $isProfile && $termin->getObjectClass() === 'CalendarDateAssignment') : ?> <a href="<?= URLHelper::getLink('dispatch.php/calendar/calendar') ?>" - title="<?= _('Zum Kalender') ?>"> + title="<?= _('Zum Kalender') ?>" aria-label="<?= _('Zum Kalender') ?>"> <?= Icon::create('schedule')->asImg(['class' => 'text-bottom']) ?> </a> <? if ($termin->calendar_date->isWritable($GLOBALS['user']->id)) : ?> <a href="<?= URLHelper::getLink('dispatch.php/calendar/date/edit/' . $termin->getPrimaryObjectId()) ?>" - title="<?= _('Termin bearbeiten') ?>" + title="<?= _('Termin bearbeiten') ?>" aria-label="<?= _('Termin bearbeiten') ?>" data-dialog> <?= Icon::create('edit')->asImg(['class' => 'text-bottom']) ?> </a> <? endif ?> <? elseif (!$course_range && in_array($termin->getObjectClass(), ['CourseDate', 'CourseExDate'])) : ?> <a href="<?= URLHelper::getLink('dispatch.php/course/dates', ['cid' => $termin->getPrimaryObjectId()]) ?>" - title="<?= _('Zur Veranstaltung') ?>"> + title="<?= _('Zur Veranstaltung') ?>" aria-label="<?= _('Zur Veranstaltung') ?>"> <?= Icon::create('seminar')->asImg(['class'=> 'text-bottom']) ?> </a> <? endif ?> diff --git a/app/views/calendar/contentbox/display.php b/app/views/calendar/contentbox/display.php index c0387fd..bd7b266 100644 --- a/app/views/calendar/contentbox/display.php +++ b/app/views/calendar/contentbox/display.php @@ -10,12 +10,12 @@ <? if ($isProfile) : ?> <a href="<?= URLHelper::getLink('dispatch.php/calendar/date/add') ?>" data-dialog="reload-on-close" - title="<?= _('Neuen Termin anlegen') ?>"> + title="<?= _('Neuen Termin anlegen') ?>" aria-label="<?= _('Neuen Termin anlegen') ?>"> <?= Icon::create('add', 'clickable')->asImg(['class' => 'text-bottom']) ?> </a> <? else: ?> <a href="<?= URLHelper::getLink("dispatch.php/course/timesrooms", ['cid' => $range_id]) ?>" - title="<?= _('Neuen Termin anlegen') ?>"> + title="<?= _('Neuen Termin anlegen') ?>" aria-label="<?= _('Neuen Termin anlegen') ?>"> <?= Icon::create('admin', 'clickable')->asImg(['class' => 'text-bottom']) ?> </a> <? endif ?> diff --git a/app/views/calendar/schedule/_colorpicker.php b/app/views/calendar/schedule/_colorpicker.php index 310c93a..20ef1a8 100644 --- a/app/views/calendar/schedule/_colorpicker.php +++ b/app/views/calendar/schedule/_colorpicker.php @@ -4,8 +4,11 @@ <? for ($index = 1; $index <= 18; $index++): ?> <span> <input type="radio" name="entry_color" value="<?= $index ?>" id="color-<?= $index ?>" - <? if ($index == $selected) echo 'checked'; ?>> - <label class="undecorated schedule-category<?= $index ?>" for="color-<?= $index ?>"></label> + <?= $index === $selected ? 'checked' : '' ?>> + <label class="undecorated schedule-category<?= $index ?> enter-accessible" + for="color-<?= $index ?>" + aria-label="<?= sprintf(_('Farbe %u zuordnen'), $index) ?>" + title="<?= sprintf(_('Farbe %u zuordnen'), $index) ?>"></label> </span> <? endfor; ?> </div> diff --git a/app/views/calendar/schedule/_semester_chooser.php b/app/views/calendar/schedule/_semester_chooser.php index 3af4018..a8a783e 100644 --- a/app/views/calendar/schedule/_semester_chooser.php +++ b/app/views/calendar/schedule/_semester_chooser.php @@ -1,7 +1,8 @@ <form method="post" class="default" action="<?= $controller->link_for( isset($inst_mode) && $inst_mode == true ? 'calendar/instschedule/index' : 'calendar/schedule/index' ) ?>"> - <select name="semester_id" class="submit-upon-select"> + <label for="semester_id" class="sr-only"><?= _('Angezeigtes Semester') ?></label> + <select name="semester_id" class="submit-upon-select" id="semester_id"> <? foreach ($semesters as $semester) : ?> <? if ($semester['ende'] > time() - strtotime('1year 1day')) : ?> <option diff --git a/app/views/calendar/schedule/settings.php b/app/views/calendar/schedule/settings.php index a9f24b2..0e2674e 100644 --- a/app/views/calendar/schedule/settings.php +++ b/app/views/calendar/schedule/settings.php @@ -2,10 +2,9 @@ <?= CSRFProtection::tokenTag() ?> <fieldset> <legend> - <?= _('Darstellung des Stundenplans ändern') ?> + <?= _('Angezeigter Zeitraum') ?> </legend> <section> - <?= _('Angezeigter Zeitraum') ?> <section class="hgroup"> <label> <?= _('von') ?> @@ -22,8 +21,12 @@ <?= _('Uhr') ?><br> </section> </section> - <section class='settings'> + </fieldset> + <fieldset> + <legend> <?= _('Angezeigte Wochentage') ?> + </legend> + <section class='settings'> <? foreach ([1, 2, 3, 4, 5, 6, 0] as $day) : ?> <label> <input type="checkbox" name="days[]" value="<?= $day ?>" diff --git a/app/views/consultation/admin/create.php b/app/views/consultation/admin/create.php index c973ccf..34385b5 100644 --- a/app/views/consultation/admin/create.php +++ b/app/views/consultation/admin/create.php @@ -1,252 +1,36 @@ <?php /** * @var Consultation_AdminController $controller - * @var Trails_Flash $flash + * @var Trails\Flash $flash * @var string|null $room * @var array $responsible * @var Range $range + * @var int $slot_count_threshold */ -$days_of_the_week = [ - _('Montag') => 1, - _('Dienstag') => 2, - _('Mittwoch') => 3, - _('Donnerstag') => 4, - _('Freitag') => 5, - _('Samstag') => 6, - _('Sonntag') => 0 -]; -$intervals = [ - _('wöchentlich') => 1, - _('zweiwöchentlich') => 2, - _('dreiwöchentlich') => 3, - _('monatlich') => 4, -]; -?> - -<form action="<?= $controller->store() ?>" method="post" class="default" data-dialog> - <?= CSRFProtection::tokenTag() ?> - -<? if ($flash['confirm-many']): ?> - <?= MessageBox::info( - _('Sie erstellen eine sehr große Anzahl an Terminen.') . ' ' . - _('Bitte bestätigen Sie diese Aktion.'), - [ - '<label><input type="checkbox" name="confirmed" value="1">' . - sprintf( - _('Ja, ich möchte wirklich %s Termine erstellen.'), - number_format($flash['confirm-many'], 0, ',', '.') - ) . - '</label>' - ] - )->hideClose() ?> -<? endif; ?> - - <fieldset> - <legend> - <?= _('Ort und Zeit') ?> - </legend> - - <label> - <span class="required"><?= _('Ort') ?></span> - - <input required type="text" name="room" - value="<?= htmlReady(Request::get('room', $room)) ?>" - placeholder="<?= _('Ort') ?>"> - </label> - - <label class="col-3"> - <span class="required"><?= _('Beginn') ?></span> - - <input required type="text" name="start-date" id="start-date" - value="<?= htmlReady(Request::get('start-date', strftime('%d.%m.%Y', strtotime('+7 days')))) ?>" - placeholder="<?= _('tt.mm.jjjj') ?>" - data-date-picker='{">=":"today","disable_holidays": true}'> - </label> - - <label class="col-3"> - <span class="required"><?= _('Ende') ?></span> - - <input required type="text" name="end-date" id="end-date" - value="<?= htmlReady(Request::get('end-date', strftime('%d.%m.%Y', strtotime('+4 weeks')))) ?>" - placeholder="<?= _('tt.mm.jjjj') ?>" - data-date-picker='{">=":"#start-date","disable_holidays": true}'> - </label> - - <label class="col-3"> - <span class="required"><?= _('Am Wochentag') ?></span> - - <select required name="day-of-week"> - <? foreach ($days_of_the_week as $day => $value): ?> - <option value="<?= $value ?>" <? if (Request::get('day-of-week', strftime('%w')) == $value) echo 'selected'; ?>> - <?= htmlReady($day) ?> - </option> - <? endforeach; ?> - </select> - </label> - - <label class="col-3"> - <span class="required"><?= _('Intervall') ?></span> - <select required name="interval"> - <? foreach ($intervals as $interval => $value): ?> - <option value="<?= $value ?>" <? if (Request::int('interval') == $value) echo 'selected'; ?>> - <?= htmlReady($interval) ?> - </option> - <? endforeach; ?> - </select> - </label> - - <label for="start-time" class="col-3"> - <span class="required"><?= _('Von') ?></span> - - <input required type="text" name="start-time" id="start-time" - value="<?= htmlReady(Request::get('start-time', '08:00')) ?>" - placeholder="<?= _('HH:mm') ?>" - data-time-picker='{"<":"#end-time"}'> - </label> - - <label for="ende_hour" class="col-3"> - <span class="required"><?= _('Bis') ?></span> - - <input required type="text" name="end-time" id="end-time" - value="<?= htmlReady(Request::get('end-time', '09:00')) ?>" - placeholder="<?= _('HH:mm') ?>" - data-time-picker='{">":"#start-time"}'> - </label> - - <label class="col-3"> - <span class="required"><?= _('Dauer eines Termins in Minuten') ?></span> - <input required type="text" name="duration" - value="<?= htmlReady(Request::int('duration', 15)) ?>" - maxlength="3" pattern="^\d+$"> - </label> +$convertResponsibilities = function ($input) { + if ($input === false) { + return json_encode(false); + } - <label class="col-3"> - <?= _('Maximale Teilnehmerzahl') ?> - <?= tooltipIcon(_('Falls Sie mehrere Personen zulassen wollen (wie z.B. zu einer Klausureinsicht), so geben Sie hier die maximale Anzahl an Personen an, die sich anmelden dürfen.')) ?> - <input required type="text" name="size" id="size" - min="1" max="50" value="<?= Request::int('size', 1) ?>"> - </label> + foreach ($input as $key => $values) { + $input[$key] = array_map( + fn($item) => ['id' => $item->id, 'label' => $item instanceof Statusgruppen ? $item->getName() : $item->getFullName()], + $values + ); + } - <label> - <input type="checkbox" name="pause" value="1" - data-shows=".pause-inputs" data-activates=".pause-inputs input" - <? if (Request::bool('pause')) echo 'checked'; ?>> - <?= _('Pausen zwischen den Terminen einfügen?') ?> - </label> + return json_encode($input); +} - <label class="col-3 pause-inputs"> - <?= _('Eine Pause nach wie vielen Minuten einfügen?') ?> - <input type="number" name="pause_time" - value="<?= htmlReady(Request::int('pause_time', 45)) ?>" - min="1"> - </label> - - <label class="col-3 pause-inputs"> - <?= _('Dauer der Pause in Minuten') ?> - <input type="number" name="pause_duration" - value="<?= Request::int('pause_duration', 15) ?>" - min="1"> - </label> - - <label> - <input type="checkbox" name="lock" value="1" data-shows=".lock-inputs" data-activates=".lock-inputs input"> - <?= _('Termine für Buchungen sperren?') ?> - </label> - - <label class="lock-inputs"> - <?= _('Wieviele Stunden vor Beginn des Blocks sollen die Termine für Buchungen gesperrt werden?') ?> - <input type="number" name="lock_time" - value="<?= htmlReady(Request::int('lock_time', 24)) ?>" - min="1"> - </label> - </fieldset> - -<? if ($responsible): ?> - <fieldset> - <legend><?= _('Durchführende Personen, Gruppen oder Einrichtungen') ?></legend> - - <? if ($range instanceof Institute): ?> - <p> - <?= _('Bei Einrichtungen muss mindestens eine durchführende Person, Gruppe oder Einrichtung zugewiesen ' - . 'werden.') ?> - </p> - <p> - <?= _('Bitte beachten Sie, dass bei Zuweisungen von Statusgruppen alle Personen der Gruppe mit dem Status ' - . '"tutor" und "dozent" als durchführende Personen zugewiesen werden und über alle Buchungen ' - . 'informiert werden.') ?> - <?= _('Gleiches gilt für eine zugewiesene Einrichtung. Bitte achten Sie darauf, dass Sie Ihre hier ' - . ' getroffene Auswahl in Absprache tätigen.') ?> - </p> - <? endif; ?> - - <?= $this->render_partial('consultation/admin/block-responsibilities.php', compact('responsible')) ?> - </fieldset> -<? endif; ?> - - <fieldset> - <legend><?= _('Weitere Einstellungen') ?></legend> - - <label> - <?= _('Information zu den Terminen in diesem Block') ?> - <textarea name="note"><?= htmlReady(Request::get('note')) ?></textarea> - </label> - - <label> - <input type="checkbox" name="calender-events" value="1" - <? if (Request::bool('calender-events')) echo 'checked'; ?>> - <?= _('Die freien Termine auch im Kalender markieren') ?> - </label> - - <? if ($range instanceof Course): ?> - <label> - <input type="checkbox" name="mail-to-tutors" value="1" checked> - <?= _('Tutor/innen beim Versand von Buchungsbenachrichtigungen berücksichtigen?') ?> - </label> - <? endif; ?> - - <label> - <input type="checkbox" name="show-participants" value="1" - <? if (Request::bool('show-participants')) echo 'checked'; ?>> - <?= _('Namen der buchenden Personen sind öffentlich sichtbar') ?> - </label> - - <label> - <?= _('Grund der Buchung abfragen') ?> - </label> - <div class="hgroup"> - <label> - <input type="radio" name="require-reason" value="yes" - <? if (Request::get('require-reason') === 'yes') echo 'checked'; ?>> - <?= _('Ja, zwingend erforderlich') ?> - </label> - - <label> - <input type="radio" name="require-reason" value="optional" - <? if (Request::get('require-reason', 'optional') === 'optional') echo 'checked'; ?>> - <?= _('Ja, optional') ?> - </label> - - <label> - <input type="radio" name="require-reason" value="no" - <? if (Request::get('require-reason') === 'no') echo 'checked'; ?>> - <?= _('Nein') ?> - </label> - </div> - - <label> - <?= _('Bestätigung für folgenden Text einholen') ?> - (<?= _('optional') ?>) - <?= tooltipIcon(_('Wird hier ein Text eingegeben, so müssen Buchende bestätigen, dass sie diesen Text gelesen haben.')) ?> - <textarea name="confirmation-text"><?= htmlReady(Request::get('confirmation-text')) ?></textarea> - </label> - </fieldset> - - <footer data-dialog-button> - <?= Studip\Button::createAccept(_('Termin speichern')) ?> - <?= Studip\LinkButton::createCancel( - _('Abbrechen'), - $controller->indexURL() - ) ?> - </footer> -</form> +?> +<div data-vue-app="<?= htmlReady(json_encode(['components' => ['ConsultationCreator']])) ?>" + is="ConsultationCreator" + cancel-url="<?= $controller->indexURL() ?>" + store-url="<?= $controller->storeURL() ?>" + :with-responsible="<?= htmlReady($convertResponsibilities($responsible)) ?>" + range-type="<?= get_class($range) ?>" + default-room="<?= htmlReady($room) ?>" + :slot-count-threshold="<?= htmlReady($slot_count_threshold) ?>" + :as-dialog="<?= json_encode(Request::isXhr()) ?>" +></div> diff --git a/app/views/consultation/admin/edit.php b/app/views/consultation/admin/edit.php index 347a05a..f9b416a 100644 --- a/app/views/consultation/admin/edit.php +++ b/app/views/consultation/admin/edit.php @@ -41,8 +41,8 @@ </label> <? if ($responsible): ?> - <?= $this->render_partial('consultation/admin/block-responsibilities.php', compact('responsible', 'block')) ?> - <? endif; ?> + <?= $this->render_partial('consultation/admin/block-responsibilities.php', compact('responsible', 'block')) ?> + <? endif; ?> <label> <?= _('Maximale Teilnehmerzahl') ?> @@ -72,6 +72,12 @@ </label> <label> + <input type="checkbox" name="consecutive" value="1" + <? if ($block->consecutive) echo 'checked'; ?>> + <?= _('Termine innerhalb dieses Blocks nur fortlaufend vergeben') ?> + </label> + + <label> <?= _('Grund der Buchung abfragen') ?> </label> <div class="hgroup"> diff --git a/app/views/consultation/admin/index.php b/app/views/consultation/admin/index.php index 52a352c..43cd5bf 100644 --- a/app/views/consultation/admin/index.php +++ b/app/views/consultation/admin/index.php @@ -13,7 +13,7 @@ <?= MessageBox::info(sprintf( implode('<br>', [ _('Derzeit sind keine Termine eingetragen.'), - '<a href="%s" class="button" data-dialog="size=auto">%s</a>', + '<a href="%s" class="button" data-dialog="size=big">%s</a>', ]), $controller->create(), _('Terminblöcke anlegen') diff --git a/app/views/consultation/admin/ungrouped.php b/app/views/consultation/admin/ungrouped.php index d41d43d..1796e31 100644 --- a/app/views/consultation/admin/ungrouped.php +++ b/app/views/consultation/admin/ungrouped.php @@ -14,7 +14,7 @@ <?= MessageBox::info(sprintf( implode('<br>', [ _('Derzeit sind keine Termine eingetragen.'), - '<a href="%s" class="button" data-dialog="size=auto">%s</a>', + '<a href="%s" class="button" data-dialog="size=big">%s</a>', ]), $controller->create(), _('Terminblöcke anlegen') @@ -183,9 +183,7 @@ </div> <? endif; ?> </td> - <td> - <?= htmlReady($block->room) ?> - </td> + <td><?= htmlReady($slot->block->room) ?></td> <td> <?= $this->render_partial('consultation/slot-occupation.php', compact('slot')) ?> </td> diff --git a/app/views/consultation/overview/ungrouped.php b/app/views/consultation/overview/ungrouped.php index b4d5c62..147f588 100644 --- a/app/views/consultation/overview/ungrouped.php +++ b/app/views/consultation/overview/ungrouped.php @@ -1,3 +1,14 @@ +<?php +/** + * @var ConsultationBlock[] $blocks + * @var Consultation_OverviewController $controller + * @var int $count + * @var int $limit + * @var int $page + * + * @var callable $displayNote + */ +?> <? if (count($blocks) === 0): ?> <?= MessageBox::info(_('Aktuell werden keine Termine angeboten.'))->hideClose() ?> diff --git a/app/views/contact/index.php b/app/views/contact/index.php index a988191..81f5004 100644 --- a/app/views/contact/index.php +++ b/app/views/contact/index.php @@ -106,7 +106,7 @@ <tfoot> <tr> <td colspan="5"> - <select name="action_contact" id="contact_action" aria-label="<?= _('Aktion ausführen') ?>"> + <select name="action_contact" id="contact_action" aria-label="<?= _('Aktion auswählen') ?>"> <option value="">- <?= _('Aktion auswählen') ?></option> <option value="remove"><?= $filter ? _('Kontakte aus Gruppe entfernen') : _('Kontakte entfernen') ?></option> </select> diff --git a/app/views/course/basicdata/view.php b/app/views/course/basicdata/view.php index 9f7b5b3..9438aeb 100644 --- a/app/views/course/basicdata/view.php +++ b/app/views/course/basicdata/view.php @@ -12,19 +12,14 @@ use Studip\Button, Studip\LinkButton; */ $dialog_attr = Request::isXhr() ? ' data-dialog="size=50%"' : ''; - -$message_types = ['msg' => "success", 'error' => "error", 'info' => "info"]; ?> -<? if (is_array($flash['msg'])) foreach ($flash['msg'] as $msg) : ?> - <?= MessageBox::{$message_types[$msg[0]]}($msg[1]) ?> -<? endforeach ?> - <form name="course-details" name="details" method="post" action="<?= $controller->link_for('course/basicdata/set', $course_id) ?>" <?= $dialog_attr ?> class="default collapsable"> <?= CSRFProtection::tokenTag() ?> <input id="open_variable" type="hidden" name="open" value="<?= $flash['open'] ?>"> <?= Studip\Button::createAccept(_('Speichern'), 'store', ['style' => 'display: none;']) ?> - <fieldset <?= isset($flash['open']) && $flash['open'] != 'bd_basicsettings' ? 'class="collapsed"' : ''?> data-open="bd_basicsettings"> + <fieldset <?= isset($flash['open']) && $flash['open'] != 'bd_basicsettings' ? 'class="collapsed"' : ''?> data-open="bd_basicsettings" + aria-expanded="<?= isset($flash['open']) && $flash['open'] === 'bd_basicsettings' ? 'true' : 'false' ?>"> <legend><?= _('Grundeinstellungen') ?></legend> <? if (!$attributes): ?> @@ -55,7 +50,8 @@ $message_types = ['msg' => "success", 'error' => "error", 'info' => "info"]; </label> </fieldset> - <fieldset <?= !isset($flash['open']) || $flash['open'] != 'inset' ? 'class="collapsed"' : ''?> data-open="bd_inst"> + <fieldset <?= !isset($flash['open']) || $flash['open'] != 'inset' ? 'class="collapsed"' : ''?> data-open="bd_inst" + aria-expanded="<?= isset($flash['open']) && $flash['open'] === 'inset' ? 'true' : 'false' ?>"> <legend><?= _('Einrichtungen') ?></legend> <? if (!$institutional): ?> @@ -78,7 +74,8 @@ $message_types = ['msg' => "success", 'error' => "error", 'info' => "info"]; <? endif; ?> </fieldset> - <fieldset <?= !isset($flash['open']) || $flash['open'] != 'bd_personal' ? 'class="collapsed"' : ''?>> + <fieldset <?= !isset($flash['open']) || $flash['open'] != 'bd_personal' ? 'class="collapsed"' : ''?> + aria-expanded="<?= isset($flash['open']) && $flash['open'] === 'bd_personal' ? 'true' : 'false' ?>"> <legend><?= _('Personal') ?></legend> <table class="default"> @@ -293,7 +290,8 @@ $message_types = ['msg' => "success", 'error' => "error", 'info' => "info"]; </tbody> </table> </fieldset> - <fieldset <?= !isset($flash['open']) || $flash['open'] != 'bd_description' ? 'class="collapsed"' : ''?> data-open="bd_description"> + <fieldset <?= !isset($flash['open']) || $flash['open'] != 'bd_description' ? 'class="collapsed"' : ''?> data-open="bd_description" + aria-expanded="<?= isset($flash['open']) && $flash['open'] === 'bd_description' ? 'true' : 'false' ?>"> <legend><?= _('Weitere Angaben') ?></legend> <? if (!$descriptions): ?> diff --git a/app/views/course/cancel_dates/index.php b/app/views/course/cancel_dates/index.php index 3c092bc..4f3e297 100644 --- a/app/views/course/cancel_dates/index.php +++ b/app/views/course/cancel_dates/index.php @@ -20,7 +20,7 @@ <?= _('Benachrichtigung über ausfallende Termine an alle Teilnehmenden verschicken') ?> </label> </fieldset> - <? if ($issue_id) : ?> + <? if (!empty($issue_id)) : ?> <input type="hidden" name="issue_id" value="<?= $issue_id ?>"> <? else : ?> <input type="hidden" name="termin_id" value="<?= $dates[0]->getTerminId() ?>"> diff --git a/app/views/course/contentmodules/info.php b/app/views/course/contentmodules/info.php index 11bf9dc..93d3ad9 100644 --- a/app/views/course/contentmodules/info.php +++ b/app/views/course/contentmodules/info.php @@ -5,27 +5,17 @@ <div class="main_part"> <div class="header"> <div class="image"> - <? - if ($metadata['icon']) { - $icon = $metadata['icon'] instanceof Icon - ? $metadata['icon']->asImagePath() - : Icon::create($plugin->getPluginURL().'/'.$metadata['icon'])->asImagePath(); - } else { - $icon = null; - } - if ($icon && !is_a($icon, 'Icon')) { - $icon = Icon::create($icon); - } - ?> - <? if ($icon) : ?> - <?= $icon->asImg(100) ?> - <? endif ?> + <? if ($metadata['icon']): ?> + <?= $metadata['icon']->copyWithRole(Icon::ROLE_INFO)->asImg(100) ?> + <? endif; ?> </div> <div class="text"> <h1><?= htmlReady($metadata['displayname'] ?? $plugin->getPluginName()) ?></h1> + <? if (!empty($metadata['summary'])): ?> <strong> <?= htmlReady($metadata['summary']) ?> </strong> + <? endif; ?> </div> </div> <div class="content-modules-controls-vue-app" is="ContentModulesControl" module_id="<?= htmlReady($plugin->getPluginId()) ?>"></div> diff --git a/app/views/course/dates/_date_row.php b/app/views/course/dates/_date_row.php index 0246455..b10deac 100644 --- a/app/views/course/dates/_date_row.php +++ b/app/views/course/dates/_date_row.php @@ -1,10 +1,21 @@ <?php +/** + * @var bool $show_raumzeit + * @var bool $has_access + * @var bool $is_next_date + * @var bool $cancelled_dates_locked + * @var Course_DatesController $controller + * @var CourseDate $date + * @var Course $course + */ +?> +<?php $icon = 'date'; $dialog_url = $show_raumzeit ? $controller->url_for('course/dates/details/' . $date->id) : $controller->url_for('course/dates/singledate/' . $date->id); ?> -<tr id="date_<?= $date->id ?>" <? if ($is_next_date) echo 'class="nextdate" title="' . _('Der nächste Termin') . '"'; ?> data-termin-id="<?= htmlReady($date->id) ?>"> +<tr id="date_<?= $date->id ?>" <? if (!empty($is_next_date)) echo 'class="nextdate" title="' . _('Der nächste Termin') . '"'; ?> data-termin-id="<?= htmlReady($date->id) ?>"> <td data-sort-value="<?= htmlReady($date->date) ?>" class="date_name"> <a href="<?= $dialog_url ?>" data-dialog> <?= Icon::create($icon)->asImg(['class' => 'text-bottom']) ?> @@ -30,7 +41,11 @@ $dialog_url = $show_raumzeit <? if (count($date->statusgruppen) > 0) : ?> <ul class="clean"> <? foreach ($date->statusgruppen as $statusgruppe) : ?> - <li><?= htmlReady($statusgruppe->name) ?></li> + <li> + <a href="<?= $controller->link_for('course/statusgroups/details', $statusgruppe) ?>" data-dialog="size=default"> + <?= htmlReady($statusgruppe->name) ?> + </a> + </li> <? endforeach ?> </ul> <? else : ?> diff --git a/app/views/course/details/index.php b/app/views/course/details/index.php index eb7da10..545454a 100644 --- a/app/views/course/details/index.php +++ b/app/views/course/details/index.php @@ -496,7 +496,7 @@ if (!empty($mvv_tree)) : ?> </article> <? endif ?> -<? foreach (PluginManager::getInstance()->getPlugins('DetailspagePlugin') as $plugin) : ?> +<? foreach (PluginManager::getInstance()->getPlugins(DetailspagePlugin::class) as $plugin) : ?> <? $template = $plugin->getDetailspageTemplate($course) ?> <? if ($template) : ?> <article class="studip"> diff --git a/app/views/course/enrolment/_priocourses.php b/app/views/course/enrolment/_priocourses.php index 749a19c..a1e1bce 100644 --- a/app/views/course/enrolment/_priocourses.php +++ b/app/views/course/enrolment/_priocourses.php @@ -36,15 +36,15 @@ asort($user_prio); <? endif; ?> <p class="hidden-medium-down"> - <?= _('Ziehen Sie die in Frage kommenden Veranstaltungen auf die rechte Seite ' + <?= _('Ziehen Sie die in Frage kommenden Veranstaltungen in den Bereich für ausgewählte Veranstaltungen ' . 'und ordnen Sie sie dort in der Reihenfolge der von Ihnen gewünschten ' - . 'Priorität an. Sie können mehr Veranstaltungen nach rechts ziehen als Sie ' + . 'Priorität an. Sie können mehr Veranstaltungen ziehen als Sie ' . 'tatsächlich belegen wollen.') ?> </p> <p class="hidden-medium-up"> - <?= _('Sortieren Sie die in Frage kommenden Veranstaltungen auf die rechte Seite ' + <?= _('Sortieren Sie die in Frage kommenden Veranstaltungen im Bereich für ausgewählte Veranstaltungen ' . 'und ordnen Sie sie dort in der Reihenfolge der von Ihnen gewünschten ' - . 'Priorität an. Sie können mehr Veranstaltungen nach rechts zuweisen als Sie ' + . 'Priorität an. Sie können mehr Veranstaltungen zuweisen als Sie ' . 'tatsächlich belegen wollen.') ?> </p> @@ -79,7 +79,7 @@ asort($user_prio); <ul id="selected-courses"> <li class="empty"> <span class="hidden-medium-up"> - <?= _('Die gewünschten Veranstaltungen links auswählen') ?> + <?= _('Die gewünschten Veranstaltungen aus der Liste der verfügbaren Veranstaltungen auswählen') ?> </span> <span class="hidden-medium-down"> <?= _('Gewünschte Veranstaltungen hierhin ziehen') ?> diff --git a/app/views/course/forum/area/add.php b/app/views/course/forum/area/add.php index 881c122..1449a5d 100644 --- a/app/views/course/forum/area/add.php +++ b/app/views/course/forum/area/add.php @@ -7,7 +7,7 @@ <? else : ?> <? $num_postings = ForumVisit::getCount($entry['topic_id'], $visitdate) ?> <?= Icon::create('forum', $num_postings > 0 ? Icon::ROLE_ATTENTION : Icon::ROLE_INFO)->asImg([ - 'title' => ForumHelpers::getVisitText($num_postings, $entry['topic_id'], $constraint['depth']), + 'title' => ForumHelpers::getVisitText($num_postings, $entry['topic_id']), ]) ?> <? endif ?> </td> @@ -36,7 +36,7 @@ </td> <td class="postings"> - <?= number_format(max($entry['num_postings'] - 1, 0), 0, ',', '.') ?> + <?= number_format(max(($entry['num_postings'] ?? 0) - 1, 0), 0, ',', '.') ?> </td> <td class="answer hidden-tiny-down"> diff --git a/app/views/course/forum/index/_areas.php b/app/views/course/forum/index/_areas.php index 11f32be..4f4fa0f 100644 --- a/app/views/course/forum/index/_areas.php +++ b/app/views/course/forum/index/_areas.php @@ -46,17 +46,18 @@ <col> <col> <col class="hidden-tiny-down"> - <col> + <col style="width: 20px"> </colgroup> <thead> <tr> - <th colspan="2"> <?= _('Name des Bereichs') ?></th> + <th></th> + <th> <?= _('Name des Bereichs') ?></th> <th data-type="answers"><?= _("Beiträge") ?></th> <th data-type="last_posting" class="hidden-tiny-down"> <?= _("letzte Antwort") ?> </th> - <th></th> + <th> <?= _('Aktionen') ?> </th> </tr> </thead> diff --git a/app/views/course/forum/index/_last_post.php b/app/views/course/forum/index/_last_post.php index 3532d40..b5d854a 100644 --- a/app/views/course/forum/index/_last_post.php +++ b/app/views/course/forum/index/_last_post.php @@ -1,4 +1,4 @@ -<? if (is_array($entry['last_posting']) && count($entry['last_posting'])) : ?> +<? if (!empty($entry['last_posting']) && is_array($entry['last_posting'])) : ?> <?= _('von') ?> <? if (!empty($entry['last_posting']['anonymous'])): ?> <?= _('Anonym') ?> diff --git a/app/views/course/forum/index/_post.php b/app/views/course/forum/index/_post.php index 7b6aaa0..192a414 100644 --- a/app/views/course/forum/index/_post.php +++ b/app/views/course/forum/index/_post.php @@ -91,7 +91,7 @@ <span data-show-topic="<?= $post['topic_id'] ?>" data-topic-content="<?= $post['topic_id'] ?>" <?= $edit_posting != $post['topic_id'] ? '' : 'style="display: none;"' ?>> <?= ForumHelpers::highlight($post['content'], $highlight) ?> - <?= OpenGraph::extract(formatReady(ForumEntry::removeQuotes($post['content_raw'])))->render() ?> + <?= OpenGraph::extract(ForumEntry::removeQuotes($post['content_raw']))->render() ?> </span> </div> diff --git a/app/views/course/lvgselector/form.php b/app/views/course/lvgselector/form.php index 86e2af9..a1ae506 100644 --- a/app/views/course/lvgselector/form.php +++ b/app/views/course/lvgselector/form.php @@ -48,12 +48,12 @@ <em><?= sprintf(_("Der Suchbegriff '%s' lieferte kein Ergebnis."), htmlReady($selection->getSearchKey())) ?></em> <? else : ?> <h3><?= _('Suchergebnisse') ?>:</h3> - <? TextHelper::reset_cycle(); $show_path = TRUE; $show_link = FALSE; ?> + <? $show_path = TRUE; $show_link = FALSE; ?> <? foreach ($selection->getSearchResult() as $area) : ?> <? // MVV: show LvGruppen with complete trails only ?> <? $pathes = ModuleManagementModelTreeItem::getPathes($area->getTrails(['Modulteil', 'StgteilabschnittModul', 'StgteilAbschnitt', 'StgteilVersion', 'Studiengang'])); ?> <? if (count($pathes)) : ?> - <div class="<?= TextHelper::cycle('odd', 'even') ?>"> + <div> <?= $this->render_partial('course/lvgselector/entry', compact('area', 'show_path', 'show_link', 'pathes')); ?> </div> <? endif; ?> diff --git a/app/views/course/lvgselector/subtree.php b/app/views/course/lvgselector/subtree.php index f61463f..b8e05d4 100644 --- a/app/views/course/lvgselector/subtree.php +++ b/app/views/course/lvgselector/subtree.php @@ -8,7 +8,7 @@ $has_children = $child->hasChildren(); ?> - <div class="<?= TextHelper::cycle('odd', 'even') ?>"> + <div> <?= $this->render_partial('course/lvgselector/entry', ['area' => $child, 'show_link' => $has_children]) ?> diff --git a/app/views/course/members/accepted_list.php b/app/views/course/members/accepted_list.php index 7b34ef3..39cdca7 100644 --- a/app/views/course/members/accepted_list.php +++ b/app/views/course/members/accepted_list.php @@ -39,7 +39,7 @@ type="checkbox" name="all" value="1" data-proxyfor=":checkbox[name^=accepted]"> </th> <? endif ?> - <th></th> + <th><p class="sr-only"><?= _('Nummer') ?></p></th> <th <?if ($sort_by === 'nachname' && $sort_status === 'accepted') printf('class="sort%s"', $order); ?>> <? $order = $sort_status !== 'accepted' ? 'desc' : $order; ?> <a href="<?= URLHelper::getLink(sprintf( @@ -153,7 +153,7 @@ <tfoot> <tr> <td class="printhead" colspan="6"> - <select name="action_accepted" id="action_accepted" aria-label="<?= _('Aktion ausführen') ?>"> + <select name="action_accepted" id="action_accepted" aria-label="<?= _('Aktion wählen') ?>"> <option value="">- <?= _('Aktion wählen') ?></option> <option value="upgrade"><?= _('Akzeptieren') ?></option> <option value="remove"><?= _('Austragen') ?></option> diff --git a/app/views/course/members/autor_list.php b/app/views/course/members/autor_list.php index 0464d47..31ed8fa 100644 --- a/app/views/course/members/autor_list.php +++ b/app/views/course/members/autor_list.php @@ -48,7 +48,7 @@ type="checkbox" name="all" value="1" data-proxyfor=":checkbox[name^=autor]"> </th> <? endif ?> - <th></th> + <th><p class="sr-only"><?= _('Nummer') ?></p></th> <th <? if ($sort_by === 'nachname' && $sort_status === 'autor') printf('class="sort%s"', $order); ?>> <? $order = $sort_status !== 'autor' ? 'desc' : $order; ?> <a href="<?= URLHelper::getLink(sprintf( @@ -183,8 +183,8 @@ <tfoot> <tr> <td colspan="<?= $cols_foot ?>"> - <select name="action_autor" id="action_autor" aria-label="<?= _('Aktion ausführen') ?>"> - <option value="">- <?= _('Aktion wählen') ?></option> + <select name="action_autor" id="action_autor" aria-label="<?= _('Aktion auswählen') ?>"> + <option value="">- <?= _('Aktion auswählen') ?></option> <? if($is_dozent) : ?> <option value="upgrade"> <?= sprintf(_('Zu %s hochstufen'), htmlReady($status_groups['tutor'])) ?> diff --git a/app/views/course/members/awaiting_list.php b/app/views/course/members/awaiting_list.php index b8c1e08..c57bd2d 100644 --- a/app/views/course/members/awaiting_list.php +++ b/app/views/course/members/awaiting_list.php @@ -36,7 +36,7 @@ data-activates="#action_awaiting,button[name='submit_awaiting']"> </th> <? endif ?> - <th></th> + <th><p class="sr-only"><?= _('Nummer') ?></p></th> <th <? if ($sort_by === 'nachname' && $sort_status === $waiting_type) printf('class="sort%s"', $order); ?>> <a href="<?= URLHelper::getLink(sprintf( "?sortby=nachname&sort_status={$waiting_type}&order=%s&toggle=%s#awaiting", @@ -139,8 +139,8 @@ <tfoot> <tr> <td colspan="6"> - <select name="action_awaiting" id="action_awaiting" aria-label="<?= _('Aktion ausführen') ?>"> - <option value="">- <?= _('Aktion wählen') ?></option> + <select name="action_awaiting" id="action_awaiting" aria-label="<?= _('Aktion auswählen') ?>"> + <option value="">- <?= _('Aktion auswählen') ?></option> <option value="upgrade_autor"> <?= sprintf(_('Zu %s hochstufen'), htmlReady($status_groups['autor'])) ?> </option> diff --git a/app/views/course/members/dozent_list.php b/app/views/course/members/dozent_list.php index 9b11d07..a2bae4e 100644 --- a/app/views/course/members/dozent_list.php +++ b/app/views/course/members/dozent_list.php @@ -25,7 +25,7 @@ </colgroup> <thead> <tr class="sortable"> - <th></th> + <th><p class="sr-only"><?= _('Nummer') ?></p></th> <th <? if ($sort_by === 'nachname' && $sort_status === 'dozent') printf('class="sort%s"', $order); ?>> <? $order = $sort_status !== 'dozent' ? 'desc' : $order; ?> <a href="<?= URLHelper::getLink(sprintf( diff --git a/app/views/course/members/tutor_list.php b/app/views/course/members/tutor_list.php index 6af208c..350d83d 100644 --- a/app/views/course/members/tutor_list.php +++ b/app/views/course/members/tutor_list.php @@ -43,7 +43,7 @@ type="checkbox" name="all" value="1" data-proxyfor=":checkbox[name^=tutor]"> </th> <? endif ?> - <th></th> + <th><p class="sr-only"><?= _('Nummer') ?></p></th> <th <? if ($sort_by === 'nachname' && $sort_status === 'tutor') printf('class="sort%s"', $order); ?>> <? $order = $sort_status !== 'tutor' ? 'desc' : $order; ?> <a href="<?= URLHelper::getLink(sprintf( @@ -163,7 +163,7 @@ <tfoot> <tr> <td colspan="6"> - <select name="action_tutor" id="tutor_action" aria-label="<?= _('Aktion ausführen') ?>"> + <select name="action_tutor" id="tutor_action" aria-label="<?= _('Aktion auswählen') ?>"> <option value="">- <?= _('Aktion auswählen') ?></option> <option value="downgrade"><?= sprintf(_('Zu %s herunterstufen'), htmlReady($status_groups['autor'])) ?></option> <option value="remove"><?= _('Austragen') ?></option> diff --git a/app/views/course/members/user_list.php b/app/views/course/members/user_list.php index 9b2acea..21409b5 100644 --- a/app/views/course/members/user_list.php +++ b/app/views/course/members/user_list.php @@ -43,7 +43,7 @@ type="checkbox" name="all" value="1" data-proxyfor=":checkbox[name^=user]"> </th> <? endif ?> - <th></th> + <th><p class="sr-only"><?= _('Nummer') ?></p></th> <th <? if ($sort_by === 'nachname' && $sort_status === 'user') printf('class="sort%s"', $order); ?>> <? $order = $sort_status !== 'user' ? 'desc' : $order; ?> <a href="<?= URLHelper::getLink(sprintf( @@ -154,7 +154,7 @@ <tfoot> <tr> <td colspan="6"> - <select name="action_user" id="user_action" aria-label="<?= _('Aktion ausführen') ?>"> + <select name="action_user" id="user_action" aria-label="<?= _('Aktion auswählen') ?>"> <option value="">- <?= _('Aktion auswählen') ?></option> <option value="upgrade"> <?= sprintf(_('Zu %s hochstufen'), htmlReady($status_groups['autor'])) ?> diff --git a/app/views/course/overview/index.php b/app/views/course/overview/index.php index 39d801d..b238740 100644 --- a/app/views/course/overview/index.php +++ b/app/views/course/overview/index.php @@ -64,11 +64,6 @@ if (!empty($dates)) { echo $dates; } -// Anzeige von Umfragen -if (!empty($evaluations)) { - echo $evaluations; -} - if (!empty($questionnaires)) { echo $questionnaires; } @@ -81,7 +76,7 @@ if (!empty($plugins)) { $template = $plugin->getInfoTemplate($course_id); if ($template) { - echo $template->render(null, $layout); + echo $template->render(layout: $layout); $layout->clear_attributes(); } } diff --git a/app/views/course/room_requests/_new_request_form_footer.php b/app/views/course/room_requests/_new_request_form_footer.php index 4b84790..0d50467 100644 --- a/app/views/course/room_requests/_new_request_form_footer.php +++ b/app/views/course/room_requests/_new_request_form_footer.php @@ -16,13 +16,21 @@ <? endif ?> <? if ($step === 1 || $step === 2) : ?> - <? if ($_SESSION[$request_id]['search_by'] !== 'category') : ?> - <? \Studip\Button::create(_('Raum auswählen'), 'select_room') ?> + <? if ( + !isset($_SESSION[$request_id]['search_by']) + || $_SESSION[$request_id]['search_by'] !== 'category' + ) : ?> + <? \Studip\Button::create(_('Raum auswählen'), 'select_room') ?> <? endif ?> <? endif ?> - <? if (($step === 1 && $_SESSION[$request_id]['room_category_id'] !== '0') - || $step === 2) : ?> + <? if ( + ( + $step === 1 + && !empty($_SESSION[$request_id]['room_category_id']) + ) + || $step === 2 + ) : ?> <?= \Studip\Button::create(_('Weiter'), 'show_summary') ?> <? endif ?> diff --git a/app/views/course/room_requests/request_find_matching_rooms.php b/app/views/course/room_requests/request_find_matching_rooms.php index 01f42a4..ce24e0f 100644 --- a/app/views/course/room_requests/request_find_matching_rooms.php +++ b/app/views/course/room_requests/request_find_matching_rooms.php @@ -41,7 +41,7 @@ ] ) ?> - <? if ($category) : ?> + <? if (!empty($category)) : ?> <?= Icon::create('decline')->asInput( [ 'title' => _('alle Angaben zurücksetzen'), @@ -85,7 +85,7 @@ <label> <?= _('Raumname') ?> <span class="flex-row"> - <input type="text" name="room_name" value="<?= htmlReady($_SESSION[$request_id]['room_name']) ?>"> + <input type="text" name="room_name" value="<?= htmlReady($_SESSION[$request_id]['room_name'] ?? '') ?>"> <?= Icon::create('search')->asInput( [ 'title' => _('Räume suchen'), @@ -107,7 +107,7 @@ <input type="radio" name="selected_room_id" data-activates="button[type='submit'][name='select_room']" value="<?= htmlReady($room->id) ?>" - <? if ($_SESSION[$request_id]['room_id'] === $room->id) echo 'checked' ?>> + <? if (isset($_SESSION[$request_id]['room_id']) && $_SESSION[$request_id]['room_id'] === $room->id) echo 'checked' ?>> <?= htmlReady(mila($room->name, 60)) . ' (' . $room['category']->name . ')'?> <? if ($room->properties): ?> <? $property_names = $room->getInfolabelProperties() diff --git a/app/views/course/statusgroups/batch_action.php b/app/views/course/statusgroups/batch_action.php index 10bec38..5600d3a 100644 --- a/app/views/course/statusgroups/batch_action.php +++ b/app/views/course/statusgroups/batch_action.php @@ -1,26 +1,26 @@ <?php - if ($edit_size) { + if (!empty($edit_size)) { echo $this->render_partial('course/statusgroups/_edit_groups_size', compact('groups')); - } elseif ($edit_selfassign) { + } elseif (!empty($edit_selfassign)) { echo $this->render_partial('course/statusgroups/_edit_groups_selfassign', compact('groups')); - } elseif ($askdelete) { + } elseif (!empty($askdelete)) { echo $this->render_partial('course/statusgroups/_askdelete_groups', compact('groups')); - } elseif ($movemembers) { + } elseif (!empty($movemembers)) { echo $this->render_partial( 'course/statusgroups/_move_members', compact('target_groups', 'members', 'source_group') ); - } elseif ($copymembers) { + } elseif (!empty($copymembers)) { echo $this->render_partial( 'course/statusgroups/_copy_members', compact('target_groups', 'members', 'source_group') ); - } elseif ($deletemembers) { + } elseif (!empty($deletemembers)) { echo $this->render_partial( 'course/statusgroups/_askdelete_members', compact('members', 'source_group') ); - } elseif ($cancelmembers) { + } elseif (!empty($cancelmembers)) { echo $this->render_partial( 'course/statusgroups/_askcancel_members', compact('members') diff --git a/app/views/course/statusgroups/details.php b/app/views/course/statusgroups/details.php new file mode 100644 index 0000000..db80777 --- /dev/null +++ b/app/views/course/statusgroups/details.php @@ -0,0 +1,30 @@ +<?php +/** + * @var Statusgruppen $group + */ +?> +<table class="default"> + <caption class="hide-in-dialog"> + <?= sprintf(_('Gruppe %s'), htmlReady($group->name)) ?> + </caption> + <colgroup> + <col style="width: 32px"> + <col> + </colgroup> + <tbody> + <? foreach ($group->members as $member): ?> + <tr> + <td> + <a href="<?= URLHelper::getLink('dispatch.php/profile', ['username' => $member->user->username], true) ?>"> + <?= $member->avatar() ?> + </a> + </td> + <td> + <a href="<?= URLHelper::getLink('dispatch.php/profile', ['username' => $member->user->username], true) ?>"> + <?= htmlReady($member->user->getFullname()) ?> + </a> + </td> + </tr> + <? endforeach; ?> + </tbody> +</table> diff --git a/app/views/course/timesrooms/_cycleRow.php b/app/views/course/timesrooms/_cycleRow.php index 71a5085..5a5f60b 100644 --- a/app/views/course/timesrooms/_cycleRow.php +++ b/app/views/course/timesrooms/_cycleRow.php @@ -82,8 +82,8 @@ $is_exTermin = $termin instanceof CourseExDate; <?= $room_holiday ?: '' ?> <? endif ?> - <? $room_request_exists = RoomRequest::existsByDate($termin->id, true) ?> - <? if ($room_request_exists): ?> + <? $room_request = RoomRequest::findByDate($termin->id) ?> + <? if ($room_request && $room_request->closed == ResourceRequest::STATE_OPEN): ?> <? $msg_info = _('Für diesen Termin existiert eine Raumanfrage.') ?> <?= tooltipIcon($msg_info) ?> <? endif ?> @@ -123,9 +123,33 @@ $is_exTermin = $termin instanceof CourseExDate; <? $actionMenu->addLink( $controller->url_for('course/timesrooms/editDate/' . $termin->id, $linkAttributes), _('Termin bearbeiten'), - Icon::create('edit', Icon::ROLE_CLICKABLE, ['title' => _('Diesen Termin bearbeiten')]), + Icon::create('edit'), ['data-dialog' => ''] ) ?> + <? $actionMenu + ->conditionAll(Config::get()->RESOURCES_ENABLE && Config::get()->RESOURCES_ALLOW_ROOM_REQUESTS) + ->condition((bool) $room_request) + ->addLink( + $controller->url_for( + 'course/room_requests/request_show_summary', + $room_request + ), + _('Raumanfrage bearbeiten'), + Icon::create('room-occupied'), + ['data-dialog' => 'size=big'] + ) + ->condition(!$room_request) + ->addLink( + $controller->url_for( + 'course/room_requests/new_request', + ['range_str' => 'date', 'range_id' => $termin->id] + ), + _('Neue Raumanfrage'), + Icon::create('room-request'), + ['data-dialog' => 'size=big'] + ) + ->conditionAll(true) + ?> <? $actionMenu->addLink( $controller->url_for( diff --git a/app/views/course/timesrooms/_roomRequest.php b/app/views/course/timesrooms/_roomRequest.php index 989fccb..ffa72b0 100644 --- a/app/views/course/timesrooms/_roomRequest.php +++ b/app/views/course/timesrooms/_roomRequest.php @@ -93,8 +93,7 @@ ), _('Diese Anfrage selbst auflösen'), Icon::create('admin'), - ['title' => _('Diese Anfrage selbst auflösen')], - ['data-dialog' => '1'] + ['title' => _('Diese Anfrage selbst auflösen'), 'data-dialog' => ''] ) ?> <? endif ?> <? $actionMenu->addLink( diff --git a/app/views/course/timesrooms/editDate.php b/app/views/course/timesrooms/editDate.php index c4bd405..3fbae99 100644 --- a/app/views/course/timesrooms/editDate.php +++ b/app/views/course/timesrooms/editDate.php @@ -1,3 +1,18 @@ +<?php +/** + * @var Course_TimesroomsController $controller + * @var CourseDate $date + * @var Room[] $selectable_rooms + * @var QuickSearch|null $room_search + * @var bool $only_bookable_rooms + * @var int $preparation_time + * @var int $max_preparation_time + * @var CourseMember[] $teachers + * @var User[] $assigned_teachers + * @var Statusgruppen[] $groups + * @var Statusgruppen[] $assigned_groups + */ +?> <form action="<?= $controller->link_for('course/timesrooms/saveDate/' . $date->termin_id) ?>" method="post" class="default collapsable" <?= Request::int('fromDialog') ? 'data-dialog="size=big"' : '' ?>> <?= CSRFProtection::tokenTag() ?> @@ -177,7 +192,7 @@ <ul> <? foreach ($assigned_groups as $group) : ?> <li data-selection-id="<?= htmlReady($group->id) ?>"> - <input type="hidden" name="assigned_groups[]" + <input type="hidden" name="assigned-groups[]" value="<?= htmlReady($group->id) ?>"> <span class="studip-selection-label"> @@ -223,21 +238,5 @@ ), ['data-dialog' => 'size=big']) ?> <? endif ?> - <? if (Request::isXhr() && !$locked && Config::get()->RESOURCES_ENABLE && Config::get()->RESOURCES_ALLOW_ROOM_REQUESTS): ?> - <? ?> - <?= Studip\LinkButton::create( - (isset($request_id) ? _('Zur Raumanfrage wechseln') : _('Raumanfrage erstellen')), - ( - isset($request_id) - ? $controller->url_for( - 'course/room_requests/request_show_summary/' . $request_id - ) - : $controller->url_for( - 'course/room_requests/new_request/' . $request_id, - array_merge($params, ['range_str' => 'date', 'range_id' => $date->id]) - ) - ), - ['data-dialog' => 'size=big']) ?> - <? endif ?> </footer> </form> diff --git a/app/views/course/wiki/ask_deleting.php b/app/views/course/wiki/ask_deleting.php new file mode 100644 index 0000000..5afd19f --- /dev/null +++ b/app/views/course/wiki/ask_deleting.php @@ -0,0 +1,31 @@ +<form action="" method="post"> + <?= CSRFProtection::tokenTag() ?> + <div class="file_select_possibilities"> + <div> + <div class="clickable"> + <?= Icon::create('archive2')->asInput(50, [ + 'formaction' => $controller->deleteversionURL($page, ['redirect_to' => 'page']), + 'data-confirm' => _('Wirklich die letzte Änderung löschen?') + ]) ?> + <button + class="undecorated" + data-confirm="<?= _('Wirklich die letzte Änderung löschen?') ?>" + formaction="<?= $controller->deleteversionURL($page, ['redirect_to' => 'page']) ?>"> + <?= _('Nur die letzte Änderung löschen') ?> + </button> + </div> + <div class="clickable"> + <?= Icon::create('wiki')->asInput(50, [ + 'formaction' => $controller->deleteURL($page), + 'data-confirm' => _('Wollen Sie wirklich die komplette Seite löschen?') + ]) ?> + <button + class="undecorated" + data-confirm="<?= _('Wollen Sie wirklich die komplette Seite löschen?') ?>" + formaction="<?= $controller->deleteURL($page) ?>"> + <?= _('Ganze Wikiseite löschen') ?> + </button> + </div> + </div> + </div> +</form> diff --git a/app/views/course/wiki/history.php b/app/views/course/wiki/history.php index 81fde59..cbd324d 100644 --- a/app/views/course/wiki/history.php +++ b/app/views/course/wiki/history.php @@ -45,6 +45,16 @@ <a href="<?= $controller->versiondiff($page) ?>" data-dialog> <?= Icon::create('log')->asImg(['class' => 'text-bottom']) ?> </a> + <? if ($page->isEditable()) : ?> + <form action="<?= $controller->deleteversion($page) ?>" + method="post" + class="inline" + title="<?= _('Version löschen') ?>" + data-confirm="<?= _('Wirklich diese Version löschen?') ?>"> + <?= CSRFProtection::tokenTag() ?> + <?= Icon::create('trash')->asInput() ?> + </form> + <? endif ?> </td> </tr> <? foreach ($page->versions as $i => $version) : ?> @@ -69,6 +79,16 @@ <a href="<?= $controller->versiondiff($page, $version->id) ?>" data-dialog> <?= Icon::create('log')->asImg(['class' => 'text-bottom']) ?> </a> + <? if ($page->isEditable()) : ?> + <form action="<?= $controller->deleteversion($page, $version->id) ?>" + method="post" + class="inline" + title="<?= _('Version löschen') ?>" + data-confirm="<?= _('Wirklich diese Version löschen?') ?>"> + <?= CSRFProtection::tokenTag() ?> + <?= Icon::create('trash')->asInput() ?> + </form> + <? endif ?> </td> </tr> <? endforeach ?> diff --git a/app/views/course/wiki/newpages.php b/app/views/course/wiki/newpages.php index d9e0dce..99e0f29 100644 --- a/app/views/course/wiki/newpages.php +++ b/app/views/course/wiki/newpages.php @@ -55,36 +55,41 @@ <td> <? $authors = [$page->user_id => $page->user]; + $versions = [$page]; $oldcontent = ""; $oldversion = $page; while ($oldversion = $oldversion->predecessor) { if ($oldversion->mkdate >= $last_visit && $oldversion->user_id !== User::findCurrent()->id) { $oldcontent = $oldversion->content; if (!isset($authors[$oldversion->user_id])) { + $versions[] = $oldversion; $authors[$oldversion->user_id] = $oldversion->user; } } else { break; } } + if ($oldversion) { + $oldcontent = $oldversion->content; + } $oldcontent = strip_tags(wikiReady($oldcontent)); $content = strip_tags(wikiReady($page->content)); $commonFromStart = $controller->findLongestCommonSubstring($content, $oldcontent); $commonFromEnd = $controller->findLongestCommonSubstring($content, $oldcontent, true); - $content = mb_substr($content, $commonFromStart, $commonFromEnd); - $oldcontent = mb_substr($oldcontent, $commonFromStart, $commonFromEnd); + $oldcontent = mb_substr($oldcontent, $commonFromStart, mb_strlen($oldcontent) - mb_strlen($content)); + $content = mb_substr($content, $commonFromStart, $commonFromEnd - $commonFromStart); if ($content) { echo htmlReady(mila($content, 300), true, true); } elseif ($oldcontent) { - echo _('Gelöscht') . ': ' . htmlReady($oldcontent, true, true); + echo _('Gelöscht') . ': ' . htmlReady(mila($oldcontent, 300), true, true); } ?> </td> <td> <ul class="wiki_authors"> - <? foreach ($authors as $user) : ?> + <? foreach ($authors as $user_id => $user) : ?> <li> <? if ($user): ?> <a href="<?= URLHelper::getLink('dispatch.php/profile', ['username' => $user->username]) ?>" @@ -95,6 +100,15 @@ <? else: ?> <?= _('unbekannt') ?> <? endif; ?> + <? foreach ($versions as $version) : ?> + <? if ($version->user_id === $user_id) : ?> + <a href="<?= $controller->versiondiff($page, is_a($version, 'WikiVersion') ? $version->id : null) ?>" + title="<?= _('Einzelne Änderung anzeigen') ?>" + data-dialog> + <?= Icon::create('log')->asImg(['class' => 'text-bottom']) ?> + </a> + <? endif ?> + <? endforeach ?> </li> <? endforeach ?> </ul> diff --git a/app/views/course/wiki/search.php b/app/views/course/wiki/search.php index 27811eb..8ef886a 100644 --- a/app/views/course/wiki/search.php +++ b/app/views/course/wiki/search.php @@ -10,7 +10,7 @@ <table class="default"> <caption> - <?= sprintf(_('Treffer für Suche nach <em>%s</em> in allen Versionen'), htmlReady(Request::get('search'))) ?> + <?= sprintf(_('Treffer für Suche nach <em>%s</em>'), htmlReady(Request::get('search'))) ?> </caption> <thead> <tr> @@ -45,6 +45,7 @@ <td> <? $content = Studip\Markup::removeHtml($content); + $ignore_next_hits = 0; $offset = 0; $output = []; @@ -55,7 +56,7 @@ break; } $offset = $pos + 1; - if (($ignore_next_hits--) > 0) { + if ($ignore_next_hits-- > 0) { // if more than one occurence is found // in a fragment to be displayed, // the fragment is only shown once @@ -95,6 +96,20 @@ <? endif ?> </td> </tr> + <? if ($pagedata['is_in_history'] && count($pagedata['versions']) > 1 || ($pagedata['is_in_content'] && count($pagedata['versions']) > 0)) : ?> + <tr> + <td colspan="3"> + <a href="<?= $controller->searchpage($page_id, ['search' => Request::get('search')]) ?>"> + <?= Icon::create('add')->asImg(['class' => 'text-bottom']) ?> + <? if (count($pagedata['versions']) === 1) : ?> + <?= _('Weiterer Treffer in einer älteren Version.') ?> + <? else : ?> + <?= sprintf(_('Weitere Treffer in %d älteren Versionen.'), count($pagedata['versions'])) ?> + <? endif ?> + </a> + </td> + </tr> + <? endif ?> <? endforeach ?> </tbody> </table> diff --git a/app/views/course/wiki/searchpage.php b/app/views/course/wiki/searchpage.php new file mode 100644 index 0000000..95b6ff4 --- /dev/null +++ b/app/views/course/wiki/searchpage.php @@ -0,0 +1,48 @@ +<table class="default"> + <caption> + <?= sprintf(_('Treffer für Suche nach <em>%s</em> auf Seite %s'), htmlReady(Request::get('search')), htmlReady($page->name)) ?> + </caption> + <thead> + <tr> + <th><?= _('Seite') ?></th> + <th><?= _('Treffer') ?></th> + <th><?= _('Datum') ?></th> + </tr> + </thead> + <tbody> + <? $pos_name = mb_stripos($page->name, Request::get('search')) ?> + <? $pos_content = mb_stripos($page->content, Request::get('search')) ?> + <? if ($pos_name !== false || $pos_content !== false) : ?> + <tr> + <td> + <a href="<?= $controller->page($page) ?>"> + <?= htmlReady($page->name) ?> + </a> + </td> + <td> + <?= $controller->findTextualHits($page->content, Request::get('search'), 200) ?> + </td> + <td> + <?= $page->chdate > 0 ? date('d.m.Y H:i:s', $page->chdate) : _('unbekannt') ?> + (<?= _('Version').' '.htmlReady($page->versionnumber) ?>) + </td> + </tr> + <? endif ?> + <? foreach ($versions as $version) : ?> + <tr> + <td> + <a href="<?= $controller->version($version) ?>"> + <?= htmlReady($version->name) ?> + </a> + </td> + <td> + <?= $controller->findTextualHits($version->content, Request::get('search'), 200) ?> + </td> + <td> + <?= $version->mkdate > 0 ? date('d.m.Y H:i:s', $version->mkdate) : _('unbekannt') ?> + (<?= _('Version').' '.htmlReady($version->versionnumber) ?>) + </td> + </tr> + <? endforeach ?> + </tbody> +</table> diff --git a/app/views/course/wizard/steps/basicdata/index.php b/app/views/course/wizard/steps/basicdata/index.php index e1f10a4..f19643a 100644 --- a/app/views/course/wizard/steps/basicdata/index.php +++ b/app/views/course/wizard/steps/basicdata/index.php @@ -123,7 +123,7 @@ </div> </section> -<?php if ($dsearch) : ?> +<?php if (isset($dsearch)) : ?> <section> <label for="deputy_id_3"> <?= _('Vertretungen') ?> diff --git a/app/views/course/wizard/steps/lvgroups/_node.php b/app/views/course/wizard/steps/lvgroups/_node.php index 8b159bd..78db2f1 100644 --- a/app/views/course/wizard/steps/lvgroups/_node.php +++ b/app/views/course/wizard/steps/lvgroups/_node.php @@ -1,4 +1,4 @@ -<? if (!$search_result || in_array($node->id, $search_result)) : ?> +<? if (empty($search_result) || in_array($node->id, $search_result)) : ?> <? $id = $node->id . '-' . $pos_id; ?> <? if (!count($children)) : ?> <li><?= _('Keine Module verfügbar') ?></li> diff --git a/app/views/course/wizard/steps/lvgroups/index.php b/app/views/course/wizard/steps/lvgroups/index.php index 744cc94..0d8e196 100644 --- a/app/views/course/wizard/steps/lvgroups/index.php +++ b/app/views/course/wizard/steps/lvgroups/index.php @@ -17,25 +17,33 @@ </li> </ul> </div> -<? if (!$values['locked']) : ?> +<? if (empty($values['locked'])) : ?> - <div id="lvgroup-tree-open-nodes"> - <? foreach ($open_lvg_nodes as $opennode) : ?> - <input type="hidden" name="open_lvg_nodes[]" value="<?= $opennode; ?>"> - <? endforeach; ?> - </div> + <div id="lvgroup-tree-open-nodes"> + <? foreach ($open_lvg_nodes as $opennode) : ?> + <input type="hidden" name="open_lvg_nodes[]" value="<?= $opennode; ?>"> + <? endforeach; ?> + </div> <div id="studyareas" data-ajax-url="<?= $ajax_url ?>" data-forward-url="<?= $no_js_url ?>" data-no-search-result="<?=_('Es wurde kein Suchergebnis gefunden.') ?>"> <h2><?= _('Lehrveranstaltungsgruppen Suche') ?></h2> <div> <input type="text" size="40" style="width: auto;" name="search" id="lvgroup-tree-search" - value="<?= $values['searchterm'] ?>"> + value="<?= htmlReady($values['searchterm'] ?? '') ?>"> <span id="lvgroup-tree-search-start"> - <?= Icon::create('search', 'clickable')->asInput(["name" => 'start_search', "onclick" => "return STUDIP.MVV.CourseWizard.searchTree()", "class" => $search_result?'hidden-no-js':'']) ?> + <?= Icon::create('search')->asInput([ + 'name' => 'start_search', + 'onclick' => 'return STUDIP.MVV.CourseWizard.searchTree()', + 'class' => !empty($search_result) ? 'hidden-no-js' : '', + ]) ?> </span> <span id="lvgroup-tree-search-reset" class="hidden-js"> - <?= Icon::create('refresh', 'clickable')->asInput(["name" => 'reset_search', "onclick" => "return STUDIP.MVV.CourseWizard.resetSearch()", "class" => $search_result?'':' hidden-no-js']) ?> + <?= Icon::create('refresh')->asInput([ + 'name' => 'reset_search', + 'onclick' => 'return STUDIP.MVV.CourseWizard.resetSearch()', + 'class' => !empty($search_result) ? '' : ' hidden-no-js', + ]) ?> </span> </div> @@ -56,23 +64,25 @@ <? $pos_id = 1; ?> <? foreach ((array) $tree as $node) : ?> <? $children = $node->getChildren() ?> - <? if (count($children)) : ?> - <?= $this->render_partial('lvgroups/_node', - ['node' => $node, 'pos_id' => $pos_id++, - 'open_nodes' => $open_lvg_nodes ?: [], - 'search_result' => $search_result ?: [], - 'children' => $node->getChildren()]) ?> + <? if (count($children) > 0) : ?> + <?= $this->render_partial('lvgroups/_node', [ + 'node' => $node, + 'pos_id' => $pos_id++, + 'open_nodes' => $open_lvg_nodes ?: [], + 'search_result' => $search_result ?? [], + 'children' => $node->getChildren(), + ]) ?> <? endif ?> <? endforeach; ?> </ul> </li> </ul> </div> - <? if ($values['open_lvg_nodes']) : ?> + <? if (!empty($values['open_lvg_nodes'])) : ?> <input type="hidden" name="open_nodes" value="<?= json_encode($values['open_lvg_nodes']) ?>"/> <? endif; ?> - <? if ($values['searchterm']) : ?> - <input type="hidden" name="searchterm" value="<?= $values['searchterm'] ?>"/> + <? if (!empty($values['searchterm'])) : ?> + <input type="hidden" name="searchterm" value="<?= htmlReady($values['searchterm']) ?>"> <? endif; ?> <script> //<!-- diff --git a/app/views/course/wizard/steps/lvgroups/lvgroup_entry.php b/app/views/course/wizard/steps/lvgroups/lvgroup_entry.php index 09d2405..a7e2cd0 100644 --- a/app/views/course/wizard/steps/lvgroups/lvgroup_entry.php +++ b/app/views/course/wizard/steps/lvgroups/lvgroup_entry.php @@ -2,7 +2,7 @@ # Lifter010: TODO $_id = htmlReady(implode('_', (array) $area->getId())); ?> -<li id="lvgroup-tree-assigned-<?= $_id ?>" class="<?= TextHelper::cycle('odd', 'even') ?>"> +<li id="lvgroup-tree-assigned-<?= $_id ?>"> <? if (!$locked) : ?> <?= Icon::create('trash', 'clickable')->asInput(["name" => 'lvgruppe_selection[remove]['.$_id.']', "onclick" => "return STUDIP.MVV.CourseWizard.removeLVGroup('".$_id."')", "class" => '', "data-id" => $_id, "data-course_id" => htmlReady($course_id)]) ?> diff --git a/app/views/course/wizard/steps/lvgroups/lvgroup_searchentry.php b/app/views/course/wizard/steps/lvgroups/lvgroup_searchentry.php index a6715da..7c51448 100644 --- a/app/views/course/wizard/steps/lvgroups/lvgroup_searchentry.php +++ b/app/views/course/wizard/steps/lvgroups/lvgroup_searchentry.php @@ -1,7 +1,7 @@ <?php $_id = htmlReady(implode('_', (array) $area->getId())); ?> -<li id="lvgruppe_search_<?= $_id ?>" class="<?= TextHelper::cycle('odd', 'even') ?>"> +<li id="lvgruppe_search_<?= $_id ?>"> <?= Icon::create('arr_2left', Icon::ROLE_SORT)->asInput([ 'name' => "assign[{$_id}]", diff --git a/app/views/course/wizard/steps/studyareas/_assigned_node.php b/app/views/course/wizard/steps/studyareas/_assigned_node.php index 459cddd..9525fc8 100644 --- a/app/views/course/wizard/steps/studyareas/_assigned_node.php +++ b/app/views/course/wizard/steps/studyareas/_assigned_node.php @@ -1,12 +1,12 @@ <li class="sem-tree-assigned-<?= $element['id'] ?>"> <?= htmlReady($element['name']) ?> - <?php if (!$values['locked'] && $element['assignable'] && in_array($element['id'], $studyareas ?: [])) : ?> - <?= Icon::create('trash', 'clickable')->asInput(["name" => 'unassign['.$element['id'].']', "onclick" => "return STUDIP.CourseWizard.unassignNode('".$element['id']."')"]) ?> - <input type="hidden" name="studyareas[]" value="<?= $element['id'] ?>"/> + <?php if (empty($values['locked']) && $element['assignable'] && in_array($element['id'], $studyareas ?: [])) : ?> + <?= Icon::create('trash')->asInput(["name" => 'unassign['.$element['id'].']', "onclick" => "return STUDIP.CourseWizard.unassignNode('".$element['id']."')"]) ?> + <input type="hidden" name="studyareas[]" value="<?= htmlReady($element['id']) ?>"/> <?php endif ?> <ul> <?php foreach ($element['children'] as $c) : ?> <?= $this->render_partial('studyareas/_assigned_node', ['element' => $c]) ?> <?php endforeach ?> </ul> -</li>
\ No newline at end of file +</li> diff --git a/app/views/course/wizard/summary.php b/app/views/course/wizard/summary.php index 5217bac..ec5a646 100644 --- a/app/views/course/wizard/summary.php +++ b/app/views/course/wizard/summary.php @@ -1,4 +1,13 @@ -<form class="default" action="<?= $controller->url_for('course/wizard/process', $stepnumber, $temp_id) ?>" method="post"> +<?php +/** + * @var Course_WikiController $controller + * @var int $stepnumber + * @var string $temp_id + * @var bool $dialog + * @var Course|null $source_course + */ +?> +<form class="default" action="<?= $controller->link_for('course/wizard/process', $stepnumber, $temp_id) ?>" method="post"> <fieldset> <legend><?= _('Anlegen der Veranstaltung') ?></legend> diff --git a/app/views/evaluation/_actions.php b/app/views/evaluation/_actions.php deleted file mode 100644 index e702ff2..0000000 --- a/app/views/evaluation/_actions.php +++ /dev/null @@ -1,4 +0,0 @@ -<?= Icon::create('pause', 'clickable')->asImg() ?> -<?= Icon::create('decline', 'clickable')->asImg() ?> -<?= Icon::create('admin', 'clickable')->asImg() ?> -<?= Icon::create('trash', 'clickable')->asImg() ?> diff --git a/app/views/evaluation/_admin_list_vote.php b/app/views/evaluation/_admin_list_vote.php deleted file mode 100644 index a98335b..0000000 --- a/app/views/evaluation/_admin_list_vote.php +++ /dev/null @@ -1,27 +0,0 @@ -<? foreach ($votes as $vote): ?> - <tr> - <td> - <?= htmlReady($vote->title) ?> - </td> - <td> - <?= ObjectdisplayHelper::link($vote->author) ?> - </td> - <td> - <?= strftime("%d.%m.%Y %T", $vote->startdate) ?> - </td> - <td> - <? if ($vote->stopdate): ?> - <?= strftime("%d.%m.%Y %T", $vote->stopdate) ?> - <? else: ?> - <? if ($vote->timespan): ?> - <?= strftime("%d.%m.%Y %T", $vote->startdate + $vote->timespan) ?> - <? else: ?> - <?= _('Unbegrenzt') ?> - <? endif; ?> - <? endif; ?> - </td> - <td class="actions"> - <?= $this->render_partial("vote/_actions.php", ['vote' => $vote]) ?> - </td> - </tr> -<? endforeach; ?>
\ No newline at end of file diff --git a/app/views/evaluation/_buttons.php b/app/views/evaluation/_buttons.php deleted file mode 100644 index b5a3b33..0000000 --- a/app/views/evaluation/_buttons.php +++ /dev/null @@ -1,22 +0,0 @@ -<? if (!$controller->showResult($vote)): ?> - <? if ($vote->isRunning() && !$nobody) : ?> - <?= Studip\Button::create(_('Abstimmen'), 'vote', ['value' => $vote->id]) ?> - <? endif ?> - <?= Studip\LinkButton::create(_('Ergebnisse'), ContentBoxHelper::href($vote->id, ['preview[]' => $vote->id])) ?> -<? else: ?> - <?= Studip\LinkButton::create(_('Ergebnisse ausblenden'), ContentBoxHelper::href($vote->id, ['preview' => 0])) ?> - <?= Request::get('sort') - ? Studip\LinkButton::create(_('Nicht sortieren'), ContentBoxHelper::href($vote->id, ['preview[]' => $vote->id, 'sort' => 0])) - : Studip\LinkButton::create(_('Sortieren'), ContentBoxHelper::href($vote->id, ['preview[]' => $vote->id, 'sort' => 1])) - ?> - <? if ($vote->changeable && $vote->state == 'active' && !$nobody): ?> - <?= Studip\LinkButton::create(_('Antwort ändern'), ContentBoxHelper::href($vote->id, ['change' => 1])) ?> - <? endif; ?> - <? if (!$vote->anonymous && ($admin || $vote->namesvisibility)): ?> - <? if (Request::get('revealNames') === $vote->id) : ?> - <?= Studip\LinkButton::create(_('Namen ausblenden'), ContentBoxHelper::href($vote->id, ['revealNames' => null])) ?> - <? else : ?> - <?= Studip\LinkButton::create(_('Namen zeigen'), ContentBoxHelper::href($vote->id, ['revealNames' => $vote->id])); ?> - <? endif; ?> - <? endif; ?> -<? endif; ?>
\ No newline at end of file diff --git a/app/views/evaluation/_evaluation.php b/app/views/evaluation/_evaluation.php deleted file mode 100644 index 82c6fd1..0000000 --- a/app/views/evaluation/_evaluation.php +++ /dev/null @@ -1,57 +0,0 @@ -<? $is_new = ($evaluation->chdate >= object_get_visit($evaluation->id, 'eval', false, false)) && ($evaluation->author_id != $GLOBALS['user']->id); -?> -<article class="studip toggle <?=($is_new ? 'new' : '')?>" id="<?= $evaluation->id ?>" data-visiturl="<?=URLHelper::getScriptLink('dispatch.php/vote/visit')?>"> - <header> - <h1> - <a href="<?= ContentBoxHelper::switchhref($evaluation->id, ['contentbox_type' => 'eval']) ?>"> - <?= htmlReady($evaluation->title) ?> - </a> - </h1> - <nav> - <a href="<?= $evaluation->author ? URLHelper::getLink('dispatch.php/profile', ['username' => $evaluation->author->username]) : '' ?>"> - <?= $evaluation->author ? htmlReady($evaluation->author->getFullName()) : '' ?> - </a> | - <?= strftime("%d.%m.%Y", $evaluation->mkdate) ?> - <? if ($admin): ?> - <a title="<?= _("Evaluation bearbeiten") ?>" href="<?= URLHelper::getLink('admin_evaluation.php', ['openID' => $evaluation->id, 'rangeID' => $range_id]) ?>"> - <?= Icon::create('admin', 'clickable')->asImg() ?> - </a> - <? if (!$evaluation->enddate || $evaluation->enddate > time()): ?> - <a title="<?= _("Evaluation stoppen") ?>" href="<?= URLHelper::getLink('admin_evaluation.php', ['evalID' => $evaluation->id, 'evalAction' => 'stop']) ?>"> - <?= Icon::create('pause', 'clickable')->asImg() ?> - </a> - <? else: ?> - <a title="<?= _("Evaluation fortsetzen") ?>" href="<?= URLHelper::getLink('admin_evaluation.php', ['evalID' => $evaluation->id, 'evalAction' => 'continue']) ?>"> - <?= Icon::create('play', 'clickable')->asImg() ?> - </a> - <? endif; ?> - <a title="<?= _("Evaluation löschen") ?>" href="<?= URLHelper::getLink('admin_evaluation.php', ['evalID' => $evaluation->id, 'evalAction' => 'delete_request']) ?>"> - <?= Icon::create('trash', 'clickable')->asImg() ?> - </a> - <a title="<?= _("Evaluation exportieren") ?>" href="<?= URLHelper::getLink('admin_evaluation.php', ['evalID' => $evaluation->id, 'evalAction' => 'export_request']) ?>"> - <?= Icon::create('export', 'clickable')->asImg() ?> - </a> - <a title="<?= _("Evaluation auswerten") ?>" href="<?= URLHelper::getLink('eval_summary.php', ['eval_id' => $evaluation->id]) ?>"> - <?= Icon::create('vote', 'clickable')->asImg() ?> - </a> - <? endif; ?> - </nav> - </header> - <section> - <?= formatReady($evaluation->text); ?> - </section> - <section> - <?= \Studip\LinkButton::create(_('Anzeigen'), URLHelper::getURL('show_evaluation.php', ['evalID' => $evaluation->id]), ['data-dialog' => '', 'target' => '_blank']) ?> - </section> - <footer> - <p> - <?= _('Teilnehmende') ?>: <?= $evaluation->getNumberOfVotes() ?> - </p> - <p> - <?= _('Anonym') ?>: <?= $evaluation->anonymous ? _('Ja') : _('Nein') ?> - </p> - <p> - <?= _('Endzeitpunkt') ?>: <?= $evaluation->enddate ? strftime('%d.%m.%y, %H:%M', $evaluation->enddate) : _('Unbekannt') ?> - </p> - </footer> -</article> diff --git a/app/views/evaluation/display.php b/app/views/evaluation/display.php deleted file mode 100644 index 35d3178..0000000 --- a/app/views/evaluation/display.php +++ /dev/null @@ -1,27 +0,0 @@ -<? if ($admin || $evaluations): ?> -<article class="studip"> - <header> - <h1> - <?= Icon::create('vote', 'info')->asImg(); ?> - <?= _('Evaluationen') ?> - </h1> - <nav> - <? if ($admin): ?> - <a href="<?= URLHelper::getLink('admin_evaluation.php', ['rangeID' => $range_id]) ?>"> - <?= Icon::create('edit', 'clickable')->asImg(); ?> - </a> - <? endif; ?> - </nav> - </header> - - <? if (!$evaluations): ?> - <section> - <?= _('Keine Evaluationen vorhanden. Um neue Umfragen zu erstellen, klicken Sie rechts auf das Bearbeiten-Zeichen.') ?> - </section> - <? else: ?> - <? foreach ($evaluations as $evaluation): ?> - <?= $this->render_partial('evaluation/_evaluation.php', ['evaluation' => $evaluation]); ?> - <? endforeach; ?> - <? endif; ?> -</article> -<? endif; ?> diff --git a/app/views/fachabschluss/abschluesse/index.php b/app/views/fachabschluss/abschluesse/index.php index 6750a76..d650717 100644 --- a/app/views/fachabschluss/abschluesse/index.php +++ b/app/views/fachabschluss/abschluesse/index.php @@ -14,7 +14,7 @@ </tr> </thead> <? foreach ($abschluesse as $abschluss) : ?> - <tbody class="<?= $abschluss->count_faecher ? '' : 'empty' ?> <?= ($abschluss_id ? 'not-collapsed' : 'collapsed') ?>"> + <tbody class="<?= $abschluss->count_faecher ? '' : 'empty' ?> <?= !empty($abschluss_id) ? 'not-collapsed' : 'collapsed' ?>"> <tr class="header-row"> <td class="toggle-indicator"> <? if ($abschluss->count_faecher) : ?> @@ -62,7 +62,7 @@ $pagination->set_attribute('page', $page); $page_link = reset(explode('?', $controller->action_url('index'))) . '?page_abschluesse=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render('shared/pagechooser'); + echo $pagination->render(); ?> </td> diff --git a/app/views/fachabschluss/faecher/fachbereiche.php b/app/views/fachabschluss/faecher/fachbereiche.php index 5a4f6f1..844440c 100644 --- a/app/views/fachabschluss/faecher/fachbereiche.php +++ b/app/views/fachabschluss/faecher/fachbereiche.php @@ -10,7 +10,7 @@ </thead> <? foreach ($fachbereiche as $fachbereich): ?> <? if ($fachbereich['faecher']) : ?> - <tbody class="<?= ($fachbereich_id === $fachbereich['institut_id'] ? 'not-collapsed' : 'collapsed') ?>"> + <tbody class="<?= isset($fachbereich_id) && $fachbereich_id === $fachbereich['institut_id'] ? 'not-collapsed' : 'collapsed' ?>"> <tr class="header-row"> <td class="toggle-indicator"> <a class="mvv-load-in-new-row" @@ -18,7 +18,7 @@ </td> <td style="text-align: center;" class="dont-hide"><?= htmlReady($fachbereich['faecher']) ?> </td> </tr> - <? if ($fachbereich_id === $fachbereich['institut_id']) : ?> + <? if (isset($fachbereich_id) && $fachbereich_id === $fachbereich['institut_id']): ?> <tr class="loaded-details nohover"> <?= $this->render_partial('fachabschluss/faecher/details_fachbereich', compact('fach')) ?> </tr> diff --git a/app/views/fachabschluss/faecher/index.php b/app/views/fachabschluss/faecher/index.php index 352dcb7..dcd9134 100644 --- a/app/views/fachabschluss/faecher/index.php +++ b/app/views/fachabschluss/faecher/index.php @@ -63,7 +63,7 @@ $pagination->set_attribute('page', $page); $page_link = reset(explode('?', $controller->action_url('index'))) . '?page_faecher=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render('shared/pagechooser'); + echo $pagination->render(); ?> </td> diff --git a/app/views/file/_terms_of_use_select.php b/app/views/file/_terms_of_use_select.php index 59cb507..c1aae00 100644 --- a/app/views/file/_terms_of_use_select.php +++ b/app/views/file/_terms_of_use_select.php @@ -24,20 +24,20 @@ if (!$selected_terms_of_use_id) { aria-description="<?= htmlReady(kill_format($content_terms_of_use_entry->description)) ?>"> <label for="content_terms_of_use-<?= htmlReady($content_terms_of_use_entry->id) ?>"> - <div class="icon"> - <? if ($content_terms_of_use_entry['icon']) : ?> - <? if (filter_var($content_terms_of_use_entry['icon'], FILTER_VALIDATE_URL)): ?> - <img src="<?= htmlReady($content_terms_of_use_entry['icon']) ?>" width="32" height="32"> - <? else : ?> - <?= Icon::create($content_terms_of_use_entry['icon'], Icon::ROLE_CLICKABLE)->asImg(32) ?> - <? endif ?> - <? endif ?> - </div> + <?= Icon::create('radiobutton-unchecked')->asImg(24, ['class' => 'arrow']) ?> + <?= Icon::create('radiobutton-checked')->asImg(24, ['class' => 'check']) ?> <div class="text"> <?= htmlReady($content_terms_of_use_entry->name) ?> </div> - <?= Icon::create('arr_1down', Icon::ROLE_CLICKABLE)->asImg(24, ['class' => 'arrow']) ?> - <?= Icon::create('check-circle', Icon::ROLE_CLICKABLE)->asImg(32, ['class' => 'check']) ?> + <div class="icon"> + <? if ($content_terms_of_use_entry['icon']) : ?> + <? if (filter_var($content_terms_of_use_entry['icon'], FILTER_VALIDATE_URL)): ?> + <img src="<?= htmlReady($content_terms_of_use_entry['icon']) ?>" width="32" height="32"> + <? else : ?> + <?= Icon::create($content_terms_of_use_entry['icon'], Icon::ROLE_CLICKABLE)->asImg(32) ?> + <? endif ?> + <? endif ?> + </div> </label> <? if (trim($content_terms_of_use_entry->description)): ?> diff --git a/app/views/file/add_files_window.php b/app/views/file/add_files_window.php index 53a7b73..8b963e7 100644 --- a/app/views/file/add_files_window.php +++ b/app/views/file/add_files_window.php @@ -38,7 +38,7 @@ if ($folder_id) { <?= Icon::create('computer')->asImg(50) ?> <?= _('Mein Computer') ?> </a> - <a href="<?= $controller->link_for('file/add_url/' . $folder_id, array_merge($options, ['from_plugin' => ""])) ?>" data-dialog> + <a href="<?= $controller->link_for('file/add_url/' . $folder_id, array_merge($options, ['from_plugin' => ""])) ?>" data-dialog="size=medium"> <?= Icon::create('globe')->asImg(50) ?> <?= _('Webadresse') ?> </a> @@ -69,7 +69,7 @@ if ($folder_id) { <?= _('OER Campus') ?> </a> <? endif ?> - <? foreach (PluginManager::getInstance()->getPlugins('FilesystemPlugin') as $plugin) : ?> + <? foreach (PluginManager::getInstance()->getPlugins(FilesystemPlugin::class) as $plugin) : ?> <? if ($plugin->isSource()) : ?> <? $nav = $plugin->getFileSelectNavigation() ?> <? if ($nav): ?> diff --git a/app/views/file/add_url.php b/app/views/file/add_url.php index 1b7196b..8a172c9 100644 --- a/app/views/file/add_url.php +++ b/app/views/file/add_url.php @@ -13,25 +13,25 @@ value="<?= htmlReady(Request::get('name')) ?>"> </label> - <label> - <?= _('Zugriffsart') ?> - </label> + </fieldset> + <fieldset> + <legend><?= _('Zugriffsart') ?></legend> <label> <input type="radio" name="access_type" value="redirect" - <? if (Request::option('access_type') !== 'proxy') echo 'checked'; ?>> + <? if (Request::option('access_type') !== 'proxy') echo 'checked'; ?>> <?= _('Direktlink')?> </label> <label> <input type="radio" name="access_type" value="proxy" - <? if (Request::option('access_type') === 'proxy') echo 'checked'; ?>> + <? if (Request::option('access_type') === 'proxy') echo 'checked'; ?>> <?= _('Link über Proxy')?> </label> - <?= $this->render_partial('file/_terms_of_use_select.php', [ - 'content_terms_of_use_entries' => $content_terms_of_use_entries, - 'selected_terms_of_use_id' => $content_terms_of_use_id, - ]) ?> - </fieldset> + </fieldset> + <?= $this->render_partial('file/_terms_of_use_select.php', [ + 'content_terms_of_use_entries' => $content_terms_of_use_entries, + 'selected_terms_of_use_id' => $content_terms_of_use_id, + ]) ?> <footer data-dialog-button> <?= Studip\Button::createAccept(_('Speichern'), 'store') ?> <?= Studip\LinkButton::createCancel( diff --git a/app/views/file/choose_destination.php b/app/views/file/choose_destination.php index 49afe87..c71f072 100644 --- a/app/views/file/choose_destination.php +++ b/app/views/file/choose_destination.php @@ -33,7 +33,7 @@ $options = array_filter([ <?= Icon::create('folder-parent', Icon::ROLE_CLICKABLE)->asInput(50, ['formaction' => $controller->action_url('choose_folder/' . $parent_folder->getId()), 'to_plugin' => $options['from_plugin'] ?? null]) ?> <button class="undecorated" - formaction="<?= $controller->action_link('choose_folder/' . $parent_folder->getId()) ?>" <? if ($options['from_plugin']): ?> name="to_plugin" value="<?= htmlReady($options['from_plugin'] ?? null) ?>"<? endif; ?> + formaction="<?= $controller->action_link('choose_folder/' . $parent_folder->getId()) ?>" <? if (!empty($options['from_plugin'])): ?> name="to_plugin" value="<?= htmlReady($options['from_plugin']) ?>"<? endif; ?> data-dialog="size=medium"> <?= _('Aktueller Ordner') ?> </button> @@ -64,7 +64,7 @@ $options = array_filter([ </button> </div> - <? foreach (PluginManager::getInstance()->getPlugins('FilesystemPlugin') as $plugin) : ?> + <? foreach (PluginManager::getInstance()->getPlugins(FilesystemPlugin::class) as $plugin) : ?> <? if ($plugin->isPersonalFileArea()) : ?> <? $nav = $plugin->getFileSelectNavigation() ?> <? if ($nav) : ?> diff --git a/app/views/file/file_details.php b/app/views/file/file_details.php index 76f996a..ddbd7e4 100644 --- a/app/views/file/file_details.php +++ b/app/views/file/file_details.php @@ -1,21 +1,21 @@ <?php /** - * @var Flexi_Template $this - * @var FileController $controller - * @var Flexi_Template|null $file_info_template - * @var array $fullpath - * @var FileType $file - * @var string|null $previous_file_ref_id - * @var string|null $next_file_ref_id - * @var string|null $from_plugin - * @var bool $include_navigation + * @var Flexi\Template $this + * @var FileController $controller + * @var Flexi\Template|null $file_info_template + * @var array $fullpath + * @var FileType $file + * @var string|null $previous_file_ref_id + * @var string|null $next_file_ref_id + * @var string|null $from_plugin + * @var bool $include_navigation */ ?> <div id="file_details_window"> <?= $this->render_partial('file/_file_aside.php') ?> <div id="preview_container"> - <? if ($file_info_template instanceof Flexi_Template) : ?> + <? if ($file_info_template instanceof Flexi\Template) : ?> <?= $file_info_template->render() ?> <? endif ?> <h3><?=_('Pfad')?></h3> diff --git a/app/views/file/folder_details.php b/app/views/file/folder_details.php index 5bd6574..7d1fe04 100644 --- a/app/views/file/folder_details.php +++ b/app/views/file/folder_details.php @@ -10,7 +10,7 @@ $folder_template = $folder->getDescriptionTemplate(); <? if (!empty($folder_template)) : ?> <h3><?= _('Beschreibung') ?></h3> <article> - <? if ($folder_template instanceof Flexi_Template): ?> + <? if ($folder_template instanceof Flexi\Template): ?> <?= $folder_template->render() ?> <? else: ?> <?= $folder_template ?> diff --git a/app/views/file/new_edit_folder_form.php b/app/views/file/new_edit_folder_form.php index c1301d4..32b9800 100644 --- a/app/views/file/new_edit_folder_form.php +++ b/app/views/file/new_edit_folder_form.php @@ -27,20 +27,19 @@ id="folder-type-<?= htmlReady($folder_type['class']) ?>" <? if ($folder_type['class'] === get_class($folder)) echo 'checked'; ?>> <label for="folder-type-<?= htmlReady($folder_type['class']) ?>"> + <?= Icon::create('radiobutton-unchecked')->asImg(24, ['class' => 'arrow']) ?> + <?= Icon::create('radiobutton-checked')->asImg(24, ['class' => 'check']) ?> + <div class="text"> + <?= htmlReady($folder_type['name']) ?> + <? if ($template = $folder_type['instance']->getDescriptionTemplate()): ?> + <?= tooltipIcon($template instanceof Flexi\Template ? $template->render() : $template, false, true) ?> + <? endif ?> + </div> <div class="icon"> <? if ($folder_type['icon']) : ?> <?= $folder_type['icon']->asImg(32) ?> <? endif ?> </div> - <div class="text"> - <?= htmlReady($folder_type['name']) ?> - <? if ($template = $folder_type['instance']->getDescriptionTemplate()): ?> - <?= tooltipIcon($template instanceof Flexi_Template ? $template->render() : $template, false, true) ?> - <? endif; ?> - - </div> - <?= Icon::create('arr_1down')->asImg(24, ['class' => 'arrow']) ?> - <?= Icon::create('check-circle')->asImg(32, ['class' => 'check']) ?> </label> <? if ($folder_type['class'] === get_class($folder)) : ?> <? $folder_template = $folder->getEditTemplate() ?> diff --git a/app/views/files/_fileref_tr.php b/app/views/files/_fileref_tr.php index f282f79..fc486ae 100644 --- a/app/views/files/_fileref_tr.php +++ b/app/views/files/_fileref_tr.php @@ -75,7 +75,7 @@ if ($file->isDownloadable($GLOBALS['user']->id)) { data-sort-value="<?= htmlReady($file->getAdditionalColumnOrderWeigh($index)) ?>"> <? $content = $file->getContentForAdditionalColumn($index) ?> <? if ($content) : ?> - <?= is_a($content, "Flexi_Template") ? $content->render() : $content ?> + <?= $content instanceof Flexi\Template ? $content->render() : $content ?> <? endif ?> </td> <? endforeach ?> diff --git a/app/views/files/_folder_tr.php b/app/views/files/_folder_tr.php index 09cd295..c3f77f6 100644 --- a/app/views/files/_folder_tr.php +++ b/app/views/files/_folder_tr.php @@ -61,7 +61,7 @@ if ($folder->isReadable($GLOBALS['user']->id)) { data-sort-value="<?= htmlReady($folder->getAdditionalColumnOrderWeigh($index)) ?>"> <? $content = $folder->getContentForAdditionalColumn($index) ?> <? if ($content) : ?> - <?= is_a($content, "Flexi_Template") ? $content->render() : $content ?> + <?= $content instanceof Flexi\Template ? $content->render() : $content ?> <? endif ?> </td> <? endforeach ?> diff --git a/app/views/files/flat.php b/app/views/files/flat.php index a7d0c93..1440117 100644 --- a/app/views/files/flat.php +++ b/app/views/files/flat.php @@ -13,7 +13,7 @@ $vue_topFolder = [ 'description' => $topFolder->getDescriptionTemplate(), 'additionalColumns' => $topFolder->getAdditionalColumns(), ]; -if (is_a($vue_topFolder['description'], "Flexi_Template")) { +if ($vue_topFolder['description'] instanceof Flexi\Template) { $vue_topFolder['description'] = $vue_topFolder['description']->render(); } $vue_topFolder['buttons'] = '<span class="multibuttons">'; diff --git a/app/views/files/index.php b/app/views/files/index.php index 94ae3f4..6bf28f7 100644 --- a/app/views/files/index.php +++ b/app/views/files/index.php @@ -23,7 +23,7 @@ 'additionalColumns' => $topFolder->getAdditionalColumns(), 'buttons' => null ]; - if (is_a($vue_topFolder['description'], "Flexi_Template")) { + if ($vue_topFolder['description'] instanceof Flexi\Template) { $vue_topFolder['description'] = $vue_topFolder['description']->render(); } $vue_files = []; diff --git a/app/views/institute/extern/extern_config/persondetails.php b/app/views/institute/extern/extern_config/persondetails.php index 311551d..897703d 100644 --- a/app/views/institute/extern/extern_config/persondetails.php +++ b/app/views/institute/extern/extern_config/persondetails.php @@ -85,7 +85,7 @@ <select class="nested-select" name="semclass[]" multiple> <? foreach ($GLOBALS['SEM_CLASS'] as $key => $sem_class) : ?> <? if ($sem_class['show_browse']) : ?> - <option value="<?= $key ?>"<?= in_array($key, $page->semclass) ? ' selected' : '' ?>> + <option value="<?= $key ?>"<?= in_array($key, $page->semclass ?? []) ? ' selected' : '' ?>> <?= htmlReady($sem_class['name']) ?> </option> <? endif ?> diff --git a/app/views/institute/extern/index.php b/app/views/institute/extern/index.php index 2fdf31e..a93d969 100644 --- a/app/views/institute/extern/index.php +++ b/app/views/institute/extern/index.php @@ -22,7 +22,7 @@ else : ?> </h1> </header> <? foreach ($config_types as $type_id => $config_type): ?> - <? if ($configs[$type_id]) : ?> + <? if (!empty($configs[$type_id])) : ?> <article id="<?= $type_id ?>" <? if (Request::option('open_type') === $type_id) echo 'class="open"'; ?>> <header> <h1> diff --git a/app/views/institute/extern/upload.php b/app/views/institute/extern/upload.php index 92b2894..675f31a 100644 --- a/app/views/institute/extern/upload.php +++ b/app/views/institute/extern/upload.php @@ -16,7 +16,7 @@ <div style="font-size: smaller;"> (<?= _('Ohne Angabe wird der Name aus den importierten Daten genommen.') ?>) </div> - <input type="text" name="config_name" value="<?= htmlReady($config_name) ?>"> + <input type="text" name="config_name" value="<?= htmlReady($config_name ?? '') ?>"> </label> <label> <?= _('Konfigurationsdatei') ?> diff --git a/app/views/institute/overview/index.php b/app/views/institute/overview/index.php index cb347b0..53b66a8 100644 --- a/app/views/institute/overview/index.php +++ b/app/views/institute/overview/index.php @@ -50,19 +50,18 @@ </article> <?= $news ?> -<?= $evaluations ?> <?= $questionnaires ?> <? // display plugins -$plugins = PluginEngine::getPlugins('StandardPlugin', $institute_id); +$plugins = PluginEngine::getPlugins(StandardPlugin::class, $institute_id); $layout = $GLOBALS['template_factory']->open('shared/index_box'); foreach ($plugins as $plugin) { $template = $plugin->getInfoTemplate($institute_id); if ($template) { - echo $template->render(NULL, $layout); + echo $template->render(layout: $layout); $layout->clear_attributes(); } } diff --git a/app/views/lvgruppen/lvgruppen/details.php b/app/views/lvgruppen/lvgruppen/details.php index 073d552..6a9c3af 100644 --- a/app/views/lvgruppen/lvgruppen/details.php +++ b/app/views/lvgruppen/lvgruppen/details.php @@ -92,7 +92,7 @@ <? else : ?> <ul style="list-style-type:none;" id="mvv-lvgruppen-semester"> <? foreach ($display_semesters as $semester) : ?> - <? if ($courses[$semester->id]) : ?> + <? if (!empty($courses[$semester->id])) : ?> <li> <strong><?= htmlReady($semester->name) ?></strong> <ul style="list-style-type:none;"> diff --git a/app/views/lvgruppen/lvgruppen/index.php b/app/views/lvgruppen/lvgruppen/index.php index d5ab6a8..2ce6014 100644 --- a/app/views/lvgruppen/lvgruppen/index.php +++ b/app/views/lvgruppen/lvgruppen/index.php @@ -91,7 +91,7 @@ $pagination->set_attribute('page', $page); $page_link = reset(explode('?', $controller->action_url('index'))) . '?page_lvgruppen=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render("shared/pagechooser"); + echo $pagination->render(); ?> </td> </tr> diff --git a/app/views/materialien/files/add_dokument.php b/app/views/materialien/files/add_dokument.php index e53d917..db6c7c6 100644 --- a/app/views/materialien/files/add_dokument.php +++ b/app/views/materialien/files/add_dokument.php @@ -1,21 +1,21 @@ -<form class="default" action="<?= $controller->action_link('add_dokument', $origin, $range_type, $range_id, $mvvfile_id) ?>" method="post" data-dialog="size=auto"> - <input type="hidden" name="mvvfile_id" id="mvvfile_id" value="<?= htmlReady($mvvfile_id) ?>"> - <input type="hidden" name="range_id" id="range_id" value="<?= htmlReady($range_id) ?>"> - <input type="hidden" name="range_type" id="range_type" value="<?= htmlReady($range_type) ?>"> - - +<form class="default" action="<?= $controller->action_link('add_dokument', $origin ?? null, $range_type ?? null, $range_id ?? null, $mvvfile_id ?? null) ?>" method="post" data-dialog="size=auto"> + <input type="hidden" name="mvvfile_id" id="mvvfile_id" value="<?= htmlReady($mvvfile_id ?? '') ?>"> + <input type="hidden" name="range_id" id="range_id" value="<?= htmlReady($range_id ?? null) ?>"> + <input type="hidden" name="range_type" id="range_type" value="<?= htmlReady($range_type ?? null) ?>"> <label> <?= _('Jahr') ?> - <input name="doc_year" type="text" value="<?= htmlReady($doc_year) ?>"<?= $perm->disable('year') ?>> + <input name="doc_year" type="text" value="<?= htmlReady($doc_year ?? '') ?>"<?= $perm->disable('year') ?>> </label> - <input type="hidden" name="doc_type" value="<?= $doc_type ?>"> + <input type="hidden" name="doc_type" value="<?= htmlReady($doc_type ?? '') ?>"> <label> <?= _('Art der Datei') ?> <select name="doc_type"<?= $perm->haveFieldPerm('type') ? '' : ' disable' ?>> <? foreach ($GLOBALS['MVV_DOCUMENTS']['TYPE']['values'] as $key => $entry) : ?> - <option value="<?= $key ?>"<?= $key == $doc_type ? ' selected' : '' ?>><?= htmlReady($entry['name']) ?></option> + <option value="<?= htmlReady($key) ?>"<?= isset($doc_type) && $key == $doc_type ? ' selected' : '' ?>> + <?= htmlReady($entry['name']) ?> + </option> <? endforeach; ?> </select> </label> @@ -35,7 +35,7 @@ </tr> <tr> <td> - <div class="attachments" style="<?= (!$documents || !key_exists($key, $documents)) ? '' : 'display: none;'?>"> + <div class="attachments" style="<?= (empty($documents) || !array_key_exists($key, $documents)) ? '' : 'display: none;'?>"> <span style="cursor:pointer;" onClick="$('#fileselector_<?= $key; ?>').toggle();$(this).toggle();"> <?= Icon::create('add', Icon::ROLE_CLICKABLE, ['title' => _("Datei hinzufügen"), 'class' => 'text-bottom']); ?> <?= _("Datei hinzufügen") ?> @@ -43,11 +43,22 @@ <div id="fileselector_<?= $key; ?>" style="display:none;"> <ul class="stgfiles list-unstyled"> <li style="display: none;" class="stgfile"> - <input type="hidden" name="document_id" id="document_id" value="<?= htmlReady($document_id) ?>"> + <input type="hidden" name="document_id" id="document_id" value="<?= htmlReady($document_id ?? '') ?>"> <span class="icon"></span> <span class="name"></span> <span class="size"></span> - <a class="remove_attachment"><?= Icon::create('trash', 'clickable')->asImg(['class' => "text-bottom"]) ?></a> + <button class="refresh_attachment as-link" data-language="<?= htmlReady($key) ?>"> + <?= Icon::create('refresh')->asImg([ + 'class' => 'text-bottom', + 'title' => _('Datei aktualisieren'), + ]) ?> + </button> + <button class="remove_attachment as-link"> + <?= Icon::create('trash')->asImg([ + 'class' => 'text-bottom', + 'title' => _('Datei löschen'), + ]) ?> + </button> </li> </ul> <div id="statusbar_container"> @@ -79,7 +90,18 @@ <span class="icon"><?= Icon::create('file', Icon::ROLE_INFO, ['class' => 'text-bottom']); ?></span> <span class="name"><?= htmlReady($documents[$key]->filename) ?></span> <span class="size"></span> - <a class="remove_attachment"><?= Icon::create('trash', 'clickable')->asImg(['class' => "text-bottom"]) ?></a> + <button class="refresh_attachment as-link" data-language="<?= htmlReady($key) ?>"> + <?= Icon::create('refresh')->asImg([ + 'class' => 'text-bottom', + 'title' => _('Datei aktualisieren'), + ]) ?> + </button> + <button class="remove_attachment as-link"> + <?= Icon::create('trash')->asImg([ + 'class' => 'text-bottom', + 'title' => _('Datei löschen'), + ]) ?> + </button> </li> <? endif; ?> </ul> @@ -93,7 +115,9 @@ <?= _('Kategoriezuordnung') ?> <select name="doc_cat"> <? foreach ($GLOBALS['MVV_DOCUMENTS']['CATEGORY']['values'] as $key => $entry) : ?> - <option value="<?= $key ?>"<?= $key == $doc_cat ? ' selected' : '' ?>><?= htmlReady($entry['name']) ?></option> + <option value="<?= htmlReady($key) ?>"<?= isset($doc_cat) && $key == $doc_cat ? ' selected' : '' ?>> + <?= htmlReady($entry['name']) ?> + </option> <? endforeach; ?> </select> </label> @@ -103,13 +127,15 @@ <select id="mvv-files-tags" multiple name="doc_tags[]"> <option value=""></option> <? foreach ($GLOBALS['MVV_DOCUMENTS']['TAG']['values'] as $key => $entry) : ?> - <option value="<?= $key ?>"<?= $key == in_array($key, explode(';', $doc_tags))? ' selected' : '' ?>><?= htmlReady($entry['name']) ?></option> + <option value="<?= htmlReady($key) ?>"<?= $key == in_array($key, explode(';', $doc_tags ?? ''))? ' selected' : '' ?>> + <?= htmlReady($entry['name']) ?> + </option> <? endforeach; ?> </select> </label> <label> - <input name="doc_extvisible" type="checkbox" value="1" <?= $doc_extvisible?'checked':''; ?>> + <input name="doc_extvisible" type="checkbox" value="1" <?= !empty($doc_extvisible) ? 'checked' : '' ?>> <?= _('Sichtbarkeit nach außen') ?> </label> diff --git a/app/views/materialien/files/index.php b/app/views/materialien/files/index.php index 1b00cec..6b89847 100644 --- a/app/views/materialien/files/index.php +++ b/app/views/materialien/files/index.php @@ -111,7 +111,7 @@ // ARGH! $page_link = reset(explode('?', $controller->action_url('index'))) . '?page_files=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render("shared/pagechooser"); + echo $pagination->render(); ?> </td> </tr> diff --git a/app/views/messages/write.php b/app/views/messages/write.php index 4117628..d344143 100644 --- a/app/views/messages/write.php +++ b/app/views/messages/write.php @@ -7,7 +7,11 @@ <fieldset> <legend><?= _('Neue Nachricht') ?></legend> <div class="message-user-list"> - <label><?= _("An") ?></label> + <label> + <span class="required"> + <?= _("An") ?> + </span> + </label> <ul class="list-csv" id="adressees"> <li id="template_adressee" style="display: none;" class="adressee"> <input type="hidden" name="message_to[]" value=""> @@ -32,9 +36,7 @@ ->render(); $mps = MultiPersonSearch::get('add_adressees') - ->setLinkText(_('Mehrere Adressaten hinzufügen')) - //->setDefaultSelectedUser($defaultSelectedUser) - ->setTitle(_('Mehrere Adressaten hinzufügen')) + ->setLinkText(_('Mehrere Adressaten hinzufügen')) ->setExecuteURL($controller->url_for('messages/write')) ->setJSFunctionOnSubmit('STUDIP.Messages.add_adressees') ->setSearchObject($this->mp_search_object); @@ -53,7 +55,9 @@ </div> <div> <label> - <?= _("Betreff") ?> + <span class="required"> + <?= _("Betreff") ?> + </span> <input type="text" name="message_subject" required value="<?= htmlReady($default_message['subject']) ?>"> </label> </div> @@ -67,22 +71,26 @@ <ul class="message-options"> <? if ($GLOBALS['ENABLE_EMAIL_ATTACHMENTS']): ?> <li> - <a href="" onClick="STUDIP.Messages.toggleSetting('attachments'); return false;"> - <?= Icon::create('staple', 'clickable')->asImg(40) ?> + <a href="" onClick="STUDIP.Messages.toggleSetting('attachments'); return false;" aria-expanded="false" + role="button" title="<?= _('Anhänge verwalten') ?>" aria-controls="attachments" + id="toggle-attachments"> + <?= Icon::create('staple')->asImg(40) ?> <br> <strong><?= _("Anhänge") ?></strong> </a> </li> <? endif; ?> <li> - <a href="" onClick="STUDIP.Messages.toggleSetting('tags'); return false;"> - <?= Icon::create('star', 'clickable')->asImg(40) ?> + <a href="" onClick="STUDIP.Messages.toggleSetting('tags'); return false;" aria-expanded="false" + role="button" title="<?= _('Schlagworte verwalten') ?>" aria-controls="tags" id="toggle-tags"> + <?= Icon::create('star')->asImg(40) ?> <br> <strong><?= _("Schlagworte") ?></strong> </a> </li> <li> - <a href="" onClick="STUDIP.Messages.toggleSetting('settings'); return false;"> + <a href="" onClick="STUDIP.Messages.toggleSetting('settings'); return false;" aria-expanded="false" + role="button" title="<?= _('Optionen') ?>" aria-controls="settings" id="toggle-settings"> <?= Icon::create('admin', 'clickable')->asImg(40) ?> <br> <strong><?= _("Optionen") ?></strong> diff --git a/app/views/module/module/index.php b/app/views/module/module/index.php index 7f3df8b..6cd63e9 100644 --- a/app/views/module/module/index.php +++ b/app/views/module/module/index.php @@ -29,7 +29,7 @@ $pagination->set_attribute('page', $page); $page_link = reset(explode('?', $controller->action_url('index'))) . '?page_module=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render('shared/pagechooser'); + echo $pagination->render(); ?> </td> </tr> diff --git a/app/views/multipersonsearch/js_form.php b/app/views/multipersonsearch/js_form.php index ca1a1ab..1862c84 100644 --- a/app/views/multipersonsearch/js_form.php +++ b/app/views/multipersonsearch/js_form.php @@ -10,8 +10,12 @@ <span><?= htmlReady($description); ?></span> <input id="<?= $name . '_searchinput'; ?>" type="text" placeholder="<?= _("Suchen"); ?>" value="" name="<?= $name . '_searchinput'; ?>" style="width: 260px;" aria-label="<?= _("Suchen"); ?>"> - <?= Icon::create('search', 'clickable', ['title' => _('Suche starten')])->asImg(16, ['onclick' => 'STUDIP.MultiPersonSearch.search()']) ?> - <?= Icon::create('decline', 'clickable', ['title' => _('Suche zurücksetzen')])->asImg(16, ['onclick' => 'STUDIP.MultiPersonSearch.resetSearch()']) ?> + <button class="icon-button enter-accessible" onclick='STUDIP.MultiPersonSearch.search();return false' title="<?= _('Suche starten') ?>"> + <?= Icon::create('search')->asImg(['class' => 'text-bottom']) ?> + </button> + <button class="icon-button enter-accessible" onclick='STUDIP.MultiPersonSearch.resetSearch(); return false' title="<?= _('Suche zurücksetzen') ?>"> + <?= Icon::create('decline')->asImg(['class' => 'text-bottom']) ?> + </button> </label> <p><? foreach($quickfilter as $title => $users) : ?> <a href="#" class="quickfilter" data-quickfilter="<?= md5($title); ?>"><?= htmlReady($title); ?> (<?= count($users); ?>)</a> diff --git a/app/views/my_courses/courseexport.php b/app/views/my_courses/courseexport.php index d953187..b08c69d 100644 --- a/app/views/my_courses/courseexport.php +++ b/app/views/my_courses/courseexport.php @@ -1,3 +1,9 @@ +<?php +/** + * @var array|null $sem_courses + * @var array $sem_data + */ +?> <html> <head> <meta charset="UTF-8"> diff --git a/app/views/my_courses/groups.php b/app/views/my_courses/groups.php index 22628c5..4476aeb 100644 --- a/app/views/my_courses/groups.php +++ b/app/views/my_courses/groups.php @@ -25,7 +25,7 @@ <thead> <tr> <th><?= _('Veranstaltung') ?></th> - <th colspan="100%"><?= _('Gruppen/Farbe') ?></th> + <th colspan="100%"><?= _('Gruppe/Farbe') ?></th> </tr> </thead> <? foreach ($groups as $group_id => $group_members): ?> @@ -58,11 +58,14 @@ <? for ($i = 0; $i < 9; $i++): ?> <td class="gruppe<?= $i ?> mycourses-group-selector" onclick="this.querySelector('input').checked = true;"> <input type="radio" name="gruppe[<?= $member['seminar_id'] ?>]" value="<?= $i ?>" - aria-label="<?= _('Zugeordnet zu Gruppe ') . ($i + 1) ?>" + aria-label="<?= sprintf(_('Gruppe %u zuordnen'), $i + 1) ?>" id="course-group-<?= htmlReady($member['seminar_id']) ?>-<?= $i ?>" <? if ($my_sem[$member['seminar_id']]['gruppe'] == $i) echo 'checked'; ?>> <label for="course-group-<?= htmlReady($member['seminar_id']) ?>-<?= $i ?>"> - <?= sprintf(_('Gruppe %u zuordnen'), $i + 1) ?> + <span class="group-number"><?= $i + 1 ?></span> + <span class="checked-icon"> + <?= Icon::create('accept', Icon::ROLE_INFO)->asImg(20) ?> + </span> </label> </td> <? endfor; ?> diff --git a/app/views/my_studygroups/_course.php b/app/views/my_studygroups/_course.php index 93161e2..972a577 100644 --- a/app/views/my_studygroups/_course.php +++ b/app/views/my_studygroups/_course.php @@ -6,7 +6,7 @@ </td> <td style="text-align: left"> <a href="<?= URLHelper::getLink('seminar_main.php', ['auswahl' => $group['seminar_id']]) ?>" - <?= $group['last_visitdate'] >= $group['chdate'] ? 'style="color: red;"' : '' ?>> + <?= $group['last_visitdate'] < $group['chdate'] ? 'style="color: red;"' : '' ?>> <?= htmlReady($group['name']) ?> </a> <? if ($group['visible'] == 0) : ?> diff --git a/app/views/news/_actions.php b/app/views/news/_actions.php index 343cd10..d67de26 100644 --- a/app/views/news/_actions.php +++ b/app/views/news/_actions.php @@ -37,16 +37,19 @@ if ($new['allow_comments']) : <? if ($new->havePermission('edit')): ?> - <a href="<?= URLHelper::getLink('dispatch.php/news/edit_news/' . $new->id) ?>" data-dialog> + <a href="<?= URLHelper::getLink('dispatch.php/news/edit_news/' . $new->id) ?>" data-dialog + title="<?= _('Bearbeiten') ?>" aria-label="<?= _('Bearbeiten') ?>"> <?= Icon::create('edit') ?> </a> <? if ($new->havePermission('unassign', $range)): ?> - <a href=" <?= URLHelper::getLink('', ['remove_news' => $new->id, 'news_range' => $range]) ?>" > + <a href=" <?= URLHelper::getLink('', ['remove_news' => $new->id, 'news_range' => $range]) ?>" + title="<?= _('Nicht mehr abonnieren') ?>" aria-label="<?= _('Nicht mehr abonnieren') ?>"> <?= Icon::create('remove') ?> </a> <? endif; ?> <? if ($new->havePermission('delete')): ?> - <a href=" <?= URLHelper::getLink('', ['delete_news' => $new->id]) ?>" > + <a href=" <?= URLHelper::getLink('', ['delete_news' => $new->id]) ?>" + title="<?= _('Löschen') ?>" aria-label="<?= _('Löschen') ?>"> <?= Icon::create('trash') ?> </a> <? endif; ?> diff --git a/app/views/news/admin_news.php b/app/views/news/admin_news.php index 0376083..e12047a 100644 --- a/app/views/news/admin_news.php +++ b/app/views/news/admin_news.php @@ -1,24 +1,36 @@ -<? use Studip\Button, Studip\LinkButton; ?> +<?php +/** + * @var NewsController $controller + * @var string $area_type + * @var Trails\Flash $flash + * @var string $news_searchterm + * @var string $news_startdate + * @var string $news_enddate + * @var string|null $filter_text + * @var array<string, array<string, array{title: string, object: StudipNews}>> $news_items + * @var array $area_structure + */ +?> <? if (!empty($flash['question_text'])) : ?> -<?= QuestionBox::create( - htmlReady($flash['question_text']), - $controller->url_for('news/admin_news/' . $area_type, array_merge( - $flash['question_param'], - [ + <?= QuestionBox::create( + htmlReady($flash['question_text']), + $controller->url_for('news/admin_news/' . $area_type, array_merge( + $flash['question_param'], + [ + 'news_filter_term' => htmlReady($news_searchterm), + 'news_filter_start' => $news_startdate, + 'news_filter_end' => $news_enddate, + 'news_filter' => 'set' + ] + )), + $controller->url_for('news/admin_news/' . $area_type, [ 'news_filter_term' => htmlReady($news_searchterm), 'news_filter_start' => $news_startdate, 'news_filter_end' => $news_enddate, 'news_filter' => 'set' - ] - )), - $controller->url_for('news/admin_news/' . $area_type, [ - 'news_filter_term' => htmlReady($news_searchterm), - 'news_filter_start' => $news_startdate, - 'news_filter_end' => $news_enddate, - 'news_filter' => 'set' - ]) - ); -?> + ]) + ); + ?> <? endif ?> <form action="<?= $controller->link_for('news/admin_news/' . $area_type) ?>" id="admin_news_form" class="default" method="post"> @@ -52,9 +64,9 @@ </label> </fieldset> <footer> - <?= Button::create(_('Filter anwenden'), 'apply_news_filter', ['aria-label' => _('Liste mit Suchbegriff und/oder Zeitraum filtern')]) ?> + <?= Studip\Button::create(_('Filter anwenden'), 'apply_news_filter', ['aria-label' => _('Liste mit Suchbegriff und/oder Zeitraum filtern')]) ?> <? if ($filter_text) : ?> - <?= Button::create(_('Filter zurücksetzen'), 'reset_filter') ?> + <?= Studip\Button::create(_('Filter zurücksetzen'), 'reset_filter') ?> <? endif ?> </footer> <br> @@ -74,12 +86,13 @@ </caption> <? endif ?> <colgroup> - <col width="20"> + <col style="width: 20px"> <col> - <col width="25%"> - <col width="10%"> - <col width="10%"> - <col width="80"> + <col style="width: 25%"> + <col style="width: 10%"> + <col style="width: 10%"> + <col style="width: 5%"> + <col style="width: 80px"> </colgroup> <thead> <tr> @@ -88,6 +101,7 @@ <th><?= _('Autor') ?></th> <th><?= _('Einstelldatum') ?></th> <th><?= _('Ablaufdatum') ?></th> + <th><?= _('Aufrufe') ?></th> <th class="actions"><?= _('Aktion') ?></th> </tr> </thead> @@ -99,18 +113,18 @@ <tr> <th> <input type="checkbox" - data-proxyfor=".news_<?= $news['range_id'] ?>" + data-proxyfor=".news_<?= htmlReady($news['range_id']) ?>" aria-labelledby="<?= _('Alle auswählen') ?>"> </th> - <th colspan="5"><?= mila(htmlReady($news['title'] ?? '')) . ' ' . htmlReady($news['semester'] ?? '') ?></th> + <th colspan="6"><?= mila(htmlReady($news['title'] ?? '')) . ' ' . htmlReady($news['semester'] ?? '') ?></th> </tr> <? endif ?> <? $last_title = $title ?> <? endif ?> <tr> <td> - <input type="checkbox" class="news_<?= $news['range_id'] ?>" name="mark_news[]" - value="<?= $news['object']->news_id . '_' . $news['range_id'] ?>" + <input type="checkbox" class="news_<?= htmlReady($news['range_id']) ?>" name="mark_news[]" + value="<?= htmlReady($news['object']->news_id . '_' . $news['range_id']) ?>" aria-label="<?= _('Diese Ankündigung zum Entfernen vormerken') ?>" <?= tooltip(_("Diese Ankündigung zum Entfernen vormerken"), false) ?>> </td> <td><?= htmlReady($news['object']->topic) ?></td> @@ -119,9 +133,19 @@ $body = $parts[0]; $admin_msg = $parts[1] ?? ';' ?> - <td><?= htmlReady($news['object']->author) ?></td> + <td> + <? if ($news['object']->owner): ?> + <a href="<?= URLHelper::getLink('dispatch.php/profile', ['username' => $news['object']->owner->username]) ?>"> + <?= Avatar::getAvatar($news['object']->user_id)->getImageTag(Avatar::SMALL) ?> + <?= htmlReady($news['object']->owner->getFullName()) ?> + </a> + <? else: ?> + <?= htmlReady($news['object']->author) ?> + <? endif; ?> + </td> <td><?= strftime("%d.%m.%y", $news['object']->date) ?></td> <td><?= strftime("%d.%m.%y", $news['object']->date + $news['object']->expire) ?></td> + <td><?= $news['object']->views ?></td> <td class="actions"> <? $menu = ActionMenu::get()->setContext($news['object']->topic); @@ -158,8 +182,8 @@ </tbody> <tfoot> <tr> - <td colspan="6"> - <?= Button::create(_('Alle markierten Ankündigungen entfernen'), 'remove_marked_news') ?> + <td colspan="7"> + <?= Studip\Button::create(_('Alle markierten Ankündigungen entfernen'), 'remove_marked_news') ?> </td> </tr> </tfoot> diff --git a/app/views/news/display.php b/app/views/news/display.php index 356ec1f..5f62443 100644 --- a/app/views/news/display.php +++ b/app/views/news/display.php @@ -8,18 +8,22 @@ </h1> <nav> <? if ($perm): ?> - <a href="<?= $controller->link_for('news/edit_news/new/' . $range); ?>" data-dialog> + <a href="<?= $controller->link_for('news/edit_news/new/' . $range); ?>" data-dialog + title="<?= _('Hinzufügen') ?>" aria-label="<?= _('Hinzufügen') ?>"> <?= Icon::create('add') ?> </a> <? endif; ?> <? if ($perm && Config::get()->NEWS_RSS_EXPORT_ENABLE): ?> - <a data-dialog="size=auto;reload-on-close" title="<?=_('RSS-Feed konfigurieren') ?>" href="<?= $controller->link_for('news/rss_config/' . $range); ?>"> + <a data-dialog="size=auto;reload-on-close" title="<?=_('RSS-Feed konfigurieren') ?>" + aria-label="<?=_('RSS-Feed konfigurieren') ?>" + href="<?= $controller->link_for('news/rss_config/' . $range); ?>"> <?= Icon::create('admin') ?> </a> <? endif; ?> <? if ($rss_id): ?> - <a href="<?= URLHelper::getLink('rss.php', ['id' => $rss_id]) ?>"> - <?= Icon::create('rss')->asImg(['title' => _('RSS-Feed')]) ?> + <a href="<?= URLHelper::getLink('rss.php', ['id' => $rss_id]) ?>" + title="<?= _('RSS-Feed') ?>" aria-label="<?= _('RSS-Feed') ?>"> + <?= Icon::create('rss') ?> </a> <? endif; ?> </nav> @@ -49,7 +53,7 @@ <? endforeach; ?> <? if (!$news): ?> <section> - <?= _('Es sind keine aktuellen Ankündigungen vorhanden. Um neue Ankündigungen zu erstellen, klicken Sie rechts auf das Plus-Zeichen.') ?> + <?= _('Es sind aktuell keine Ankündigungen vorhanden. Nutzen Sie die Funktion „Hinzufügen“, um eine neue Ankündigung zu erstellen.') ?> </section> <? if ($perm && $count_all_news) : ?> <footer> diff --git a/app/views/oer/addfile/choose_file.php b/app/views/oer/addfile/choose_file.php index 6c896cf..feb5b0f 100644 --- a/app/views/oer/addfile/choose_file.php +++ b/app/views/oer/addfile/choose_file.php @@ -12,7 +12,7 @@ if ($best_nine_tags && count($best_nine_tags) > 0) { <form class="oer_search" action="<?= $controller->link_for("oer/market/search") ?>" method="GET" - data-searchresults="<?= htmlReady(json_encode($material_data)) ?>" + data-searchresults="<?= htmlReady(json_encode($material_data ?? [])) ?>" data-filteredtag="<?= htmlReady(Request::get("tag")) ?>" data-filteredcategory="<?= htmlReady(Request::get("category")) ?>" data-tags="<?= htmlReady(json_encode($tags)) ?>" diff --git a/app/views/oer/admin/hosts.php b/app/views/oer/admin/hosts.php index c60c937..0289d90 100644 --- a/app/views/oer/admin/hosts.php +++ b/app/views/oer/admin/hosts.php @@ -78,7 +78,7 @@ <form action="<?= $controller->link_for("oer/admin/add_new_host") ?>" method="post"> <h2><?= _('Werden Sie Teil des weltweiten Stud.IP Lernmarktplatzes!') ?></h2> <div> - <?= _('Der Lernmarktplatz ist ein Ort des Austauschs von kostenlosen und freien Lernmaterialien. Daher wäre es schade, wenn er nur auf Ihr einzelnes Stud.IP beschränkt wäre. Der Lernmarktplatz ist daher als dezentrales Netzwerk konzipiert, bei dem alle Nutzer aller Stud.IPs sich gegenseitig Lernmaterialien tauschen können und nach Lernmaterialien anderer Nutzer suchen können. <em>Dezentral</em> heißt dieses Netzwerk, weil es nicht einen einzigen zentralen Server gibt, der wie eine große Suchmaschine alle Informationen bereit hält. Stattdessen sind im besten Fall alle Stud.IPs mit allen anderen Stud.IPs direkt vernetzt. So ein dezentrales Netz ist sehr ausfallsicher und es passt zur Opensource-Idee von Stud.IP, weil man sich von keiner zentralen Institution abhängig macht. Aber Ihr Stud.IP muss irgendwo einen ersten Kontakt zum großen Netzwerk aller Lernmarktplätze finden, um loslegen zu können. Wählen Sie dazu irgendeinen der unten aufgeführten Server aus. Sie werden Index-Server genannt und bilden das Tor zum Rest des ganzen Netzwerks. Achten Sie darauf, dass Sie mit mindestens einem, aber auch nicht zu vielen Indexservern verbunden sind.') ?> + <?= _('Der Lernmarktplatz ist ein Ort des Austauschs von kostenlosen und freien Lernmaterialien. Daher wäre es schade, wenn er nur auf Ihr einzelnes Stud.IP beschränkt wäre. Der Lernmarktplatz ist daher als dezentrales Netzwerk konzipiert, bei dem alle Nutzer aller Stud.IPs sich gegenseitig Lernmaterialien tauschen können und nach Lernmaterialien anderer Nutzer suchen können. <em>Dezentral</em> heißt dieses Netzwerk, weil es nicht einen einzigen zentralen Server gibt, der wie eine große Suchmaschine alle Informationen bereit hält. Stattdessen sind im besten Fall alle Stud.IPs mit allen anderen Stud.IPs direkt vernetzt. So ein dezentrales Netz ist sehr ausfallsicher und es passt zur Opensource-Idee von Stud.IP, weil man sich von keiner zentralen Institution abhängig macht. Aber Ihr Stud.IP muss irgendwo einen ersten Kontakt zum großen Netzwerk aller Lernmarktplätze finden, um loslegen zu können. Wählen Sie dazu irgendeinen der folgenden Server aus. Sie werden Index-Server genannt und bilden das Tor zum Rest des ganzen Netzwerks. Achten Sie darauf, dass Sie mit mindestens einem, aber auch nicht zu vielen Indexservern verbunden sind.') ?> </div> <ul class="clean" style="text-align: center;"> diff --git a/app/views/oer/market/licenseinfo.php b/app/views/oer/market/licenseinfo.php index 4618027..eda2658 100644 --- a/app/views/oer/market/licenseinfo.php +++ b/app/views/oer/market/licenseinfo.php @@ -18,7 +18,7 @@ </p> <h2><?= _('Lizenzen auf dem Marktplatz') ?></h2> <p> - <?= _('Zum Glück gibt es kluge Menschen, die sich zum Thema <em>Lizenzen</em> kluge Gedanken gemacht haben. Daher gibt es die sogenannten CreativeCommons-Lizenzen. Die sind international gültig und besagen genau das, was oben gefordert ist. Damit haben alle, die den Marktplatz nutzen, absolute Rechtssicherheit. Diese Lizenzen kommen auch außerhalb des Marktplatzes sehr oft zum Einsatz. Musikerinnen und Musiker lizensieren ihre Musikrohdateien damit, Schriftsteller und Schriftstellerinnen ihre Gedichte und Lehrende auch ganz natürlich ihre Vorlesungsfolien. Die einzige echte Auflage ist, dass bei Weitergabe oder bei der Bearbeitung der Materialien alle Autorinnen und Autoren in irgendeiner Form transparent genannt werden müssen!') ?> + <?= _('Zum Glück gibt es kluge Menschen, die sich zum Thema <em>Lizenzen</em> kluge Gedanken gemacht haben. Daher gibt es die sogenannten CreativeCommons-Lizenzen. Die sind international gültig und besagen genau das, was im vorangegangenen Text gefordert ist. Damit haben alle, die den Marktplatz nutzen, absolute Rechtssicherheit. Diese Lizenzen kommen auch außerhalb des Marktplatzes sehr oft zum Einsatz. Musikerinnen und Musiker lizensieren ihre Musikrohdateien damit, Schriftsteller und Schriftstellerinnen ihre Gedichte und Lehrende auch ganz natürlich ihre Vorlesungsfolien. Die einzige echte Auflage ist, dass bei Weitergabe oder bei der Bearbeitung der Materialien alle Autorinnen und Autoren in irgendeiner Form transparent genannt werden müssen!') ?> </p> <p> <?= _('Dabei eine kleine Warnung: Wenn Sie vorhaben, mit ihren Vorlesungsfolien oder Übungsblättern und Musterlösungen noch Millionen Euro zu verdienen, dann stellen Sie diese Materialien nicht auf den Marktplatz, denn die Nutzung aller Materialien ist unentgeltlich (selbstverständlich verdienen auch wir nichts damit) und die Materialien können, wenn sie einmal heruntergeladen worden sind, auch noch beliebig weiter verteilt werden. Es gibt zwar eine CreativeCommons-Lizenz, die kommerzielle Nutzung ausschließt. Die ist aber explizit für den Marktplatz nicht praktikabel. Denn stellen Sie sich diese Frage: Wenn Lehrende Material vom Marktplatz innerhalb ihrer Veranstaltungen verwenden und zum Beispiel eine Infografik aus einer Präsentation an die Wand projizieren, ist das dann eine kommerzielle Nutzung? Ja, irgendwie schon, denn sie werden natürlich für ihre Lehrtätigkeit bezahlt. Wir legen besonderen Wert auf die Rechtssicherheit aller Nutzer und Nutzerinnen. Deswegen kommen auf unseren Marktplatz keine Materialien, die die kommerzielle Nutzung ausschließen. Nichtsdestotrotz haben Sie weiterhin alle Rechte, ihre Vorlesungsfolien auf anderem Wege über einen Verlag zu veröffentlichen und Tantiemen zu bekommen.') ?> diff --git a/app/views/profile/index.php b/app/views/profile/index.php index 82a001a..9b7cb4f 100644 --- a/app/views/profile/index.php +++ b/app/views/profile/index.php @@ -83,11 +83,9 @@ </section> -<?= $news ?> +<?= $news ?? '' ?> -<?= $dates ?> - -<?= $evaluations ?? '' ?> +<?= $dates ?? '' ?> <?= $questionnaires ?? '' ?> diff --git a/app/views/profile/seminare.php b/app/views/profile/seminare.php index b218eca..1d1c7c7 100644 --- a/app/views/profile/seminare.php +++ b/app/views/profile/seminare.php @@ -6,17 +6,20 @@ </header> <section> - <? foreach ($seminare as $semester => $seminar) :?> - <b><?= htmlReady($semester) ?></b><br> - - <? foreach ($seminar as $one) :?> - <a href="<?= URLHelper::getScriptLink('dispatch.php/course/details', ['sem_id' => $one->id])?>"> - <?= htmlReady($one->getFullName('number-name')) ?> - <? if ($one->start_semester !== $one->end_semester) : ?> - (<?= htmlReady($one->getFullName('sem-duration-name')) ?>) - <? endif ?> - </a><br> + <? foreach ($seminare as $semester => $seminar) : ?> + <b><?= htmlReady($semester) ?></b><br> + <ul class="clean"> + <? foreach ($seminar as $one) : ?> + <li> + <a href="<?= URLHelper::getScriptLink('dispatch.php/course/details', ['sem_id' => $one->id])?>"> + <?= htmlReady($one->getFullName('number-name')) ?> + <? if ($one->start_semester !== $one->end_semester) : ?> + (<?= htmlReady($one->getFullName('sem-duration-name')) ?>) + <? endif ?> + </a> + </li> <?endforeach?> + </ul> <?endforeach?> </section> -</article>
\ No newline at end of file +</article> diff --git a/app/views/questionnaire/_overview_questionnaire.php b/app/views/questionnaire/_overview_questionnaire.php index bb7586e..8b45d42 100644 --- a/app/views/questionnaire/_overview_questionnaire.php +++ b/app/views/questionnaire/_overview_questionnaire.php @@ -51,7 +51,7 @@ <?= htmlReady(Institute::find($assignment['range_id'])->name) ?> <? else : ?> <? - foreach (PluginManager::getInstance()->getPlugins("QuestionnaireAssignmentPlugin") as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(QuestionnaireAssignmentPlugin::class) as $plugin) { $name = $plugin->getQuestionnaireAssignmentName($assignment); if ($name) { echo htmlReady($name); diff --git a/app/views/questionnaire/_widget_questionnaire.php b/app/views/questionnaire/_widget_questionnaire.php index 22ecf72..4f625c2 100644 --- a/app/views/questionnaire/_widget_questionnaire.php +++ b/app/views/questionnaire/_widget_questionnaire.php @@ -30,7 +30,7 @@ <span title="<?= _("Anzahl der Antworten") ?>"> <?= $questionnaire->countAnswers() ?> </span> - <span title="<?= _("QR-Code zu diesem Fragebogen anzeigen") ?>"> + <span> <? $oldbase = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']) ?> <a href="<?= URLHelper::getLink( @@ -38,7 +38,8 @@ compact('range_type', 'range_id') ) ?>" class="questionnaire-qr" - data-qr-code> + title="<?= _("QR-Code zu diesem Fragebogen anzeigen") ?>" aria-label="<?= _("QR-Code zu diesem Fragebogen anzeigen") ?>" + data-qr-code> <? URLHelper::setBaseURL($oldbase) ?> <?= Icon::create("code-qr", "clickable")->asImg(20, ['class' => "text-bottom"]) ?> </a> diff --git a/app/views/questionnaire/assign.php b/app/views/questionnaire/assign.php index 4bf7fc5..5299a83 100644 --- a/app/views/questionnaire/assign.php +++ b/app/views/questionnaire/assign.php @@ -10,8 +10,8 @@ <select name="semester_id"> <? foreach ($available_semesters as $available_semester): ?> <option value="<?= htmlReady($available_semester->id) ?>" - <?= ($available_semester->id == $semester_id) - ? 'selected="selected"' + <?= isset($semester_id) && $available_semester->id == $semester_id + ? 'selected' : '' ?>> <?= htmlReady($available_semester->name) ?> @@ -24,13 +24,13 @@ (<?= _('optional') ?>) <select name="institute_id"> <option value="" - <?= ($institute_id == '' ? 'selected="selected"' : '') ?>> + <?= empty($institute_id) ? 'selected' : '' ?>> <?= _('(bitte wählen)') ?> </option> <? foreach ($available_institutes as $available_institute): ?> <option value="<?= htmlReady($available_institute['Institut_id']) ?>" - <?= ($available_institute['Institut_id'] == $institute_id) - ? 'selected="selected"' + <?= isset($institute_id) && $available_institute['Institut_id'] == $institute_id + ? 'selected' : '' ?>> <?= htmlReady($available_institute['Name']) ?> @@ -43,13 +43,13 @@ (<?= _('optional') ?>) <select name="course_type_id"> <option value="" - <?= ($course_type_id == '' ? 'selected="selected"' : '') ?>> + <?= empty($course_type_id) ? 'selected' : '' ?>> <?= dgettext('AskALotPlugin', '(bitte wählen)') ?> </option> <? foreach ($available_course_types as $available_course_type): ?> <option value="<?= htmlReady($available_course_type['id']) ?>" - <?= ($available_course_type['id'] == $course_type_id) - ? 'selected="selected"' + <?= isset($course_type_id) && $available_course_type['id'] == $course_type_id + ? 'selected' : '' ?>> <?= htmlReady($available_course_type['name']) ?> diff --git a/app/views/questionnaire/context.php b/app/views/questionnaire/context.php index 3b601c1..98e08c0 100644 --- a/app/views/questionnaire/context.php +++ b/app/views/questionnaire/context.php @@ -6,6 +6,10 @@ <fieldset> <legend><?= _("URL zum Fragebogen") ?></legend> <input type="text" aria-label="<?= _("URL zum Fragebogen (nur lesbar)") ?>" readonly value="<?= htmlReady($GLOBALS['ABSOLUTE_URI_STUDIP']."dispatch.php/questionnaire/answer/".$questionnaire->getId()) ?>"> + <a href="<?= htmlReady($GLOBALS['ABSOLUTE_URI_STUDIP'] . "dispatch.php/questionnaire/answer/" . $questionnaire->id) ?>" + data-qr-code title="<?= _('QR-Code zum Link anzeigen') ?>"> + <?= Icon::create('code-qr')->asImg(['class' => 'text-bottom']) ?> + </a> </fieldset> <fieldset> <legend><?= _("Freigaben bearbeiten") ?></legend> @@ -89,7 +93,7 @@ <? endif ?> <? - foreach (PluginManager::getInstance()->getPlugins("QuestionnaireAssignmentPlugin") as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(QuestionnaireAssignmentPlugin::class) as $plugin) { $template = $plugin->getQuestionnaireAssignmentEditTemplate($this->questionnaire); if ($template) { echo $template->render(); diff --git a/app/views/questionnaire/question_types/likert/likert_answer.php b/app/views/questionnaire/question_types/likert/likert_answer.php index 1847587..86ccc3e 100644 --- a/app/views/questionnaire/question_types/likert/likert_answer.php +++ b/app/views/questionnaire/question_types/likert/likert_answer.php @@ -25,7 +25,7 @@ $responseData = isset($response->answerdata['answers']) ? $response->answerdata[ <tr> <th><?= _('Aussage') ?></th> <? foreach ($answers as $answer) : ?> - <th><?= htmlReady($answer) ?></th> + <th class="option-cell"><?= htmlReady($answer) ?></th> <? endforeach ?> </tr> </thead> @@ -35,7 +35,7 @@ $responseData = isset($response->answerdata['answers']) ? $response->answerdata[ <? $html_id = md5(uniqid($index)) ?> <td id="<?= $html_id ?>"><?= htmlReady($statements[$index]) ?></td> <? foreach ($answers as $answer_index => $answer) : ?> - <td> + <td class="option-cell"> <input type="radio" title="<?= htmlReady($answer) ?>" aria-labelledby="<?= $html_id ?>" diff --git a/app/views/questionnaire/question_types/likert/likert_evaluation.php b/app/views/questionnaire/question_types/likert/likert_evaluation.php index fd4eb01..11ef4b4 100644 --- a/app/views/questionnaire/question_types/likert/likert_evaluation.php +++ b/app/views/questionnaire/question_types/likert/likert_evaluation.php @@ -27,7 +27,7 @@ $options = $vote->questiondata['options']; <tr> <th><?= _('Aussage') ?></th> <? foreach ($options as $option) : ?> - <th><?= htmlReady($option) ?></th> + <th class="option-cell"><?= htmlReady($option) ?></th> <? endforeach ?> </tr> </thead> diff --git a/app/views/questionnaire/question_types/rangescale/rangescale_answer.php b/app/views/questionnaire/question_types/rangescale/rangescale_answer.php index 3525b4c..4333689 100644 --- a/app/views/questionnaire/question_types/rangescale/rangescale_answer.php +++ b/app/views/questionnaire/question_types/rangescale/rangescale_answer.php @@ -25,7 +25,7 @@ $responseData = $response['answerdata'] && $response['answerdata']['answers'] ? <tr> <th><?= _('Aussage') ?></th> <? for ($i = $vote->questiondata['minimum'] ?? 1; $i <= $vote->questiondata['maximum']; $i++) : ?> - <th><?= htmlReady($i) ?></th> + <th class="option-cell"><?= htmlReady($i) ?></th> <? endfor ?> </tr> </thead> @@ -35,7 +35,7 @@ $responseData = $response['answerdata'] && $response['answerdata']['answers'] ? <? $html_id = md5(uniqid($index)) ?> <td id="<?= $html_id ?>"><?= htmlReady($statements[$index]) ?></td> <? for ($i = $vote->questiondata['minimum'] ?? 1; $i <= $vote->questiondata['maximum']; $i++) : ?> - <td> + <td class="option-cell"> <input type="radio" title="<?= htmlReady($i) ?>" aria-labelledby="<?= $html_id ?>" diff --git a/app/views/questionnaire/question_types/rangescale/rangescale_evaluation.php b/app/views/questionnaire/question_types/rangescale/rangescale_evaluation.php index 2715934..6b75ea1 100644 --- a/app/views/questionnaire/question_types/rangescale/rangescale_evaluation.php +++ b/app/views/questionnaire/question_types/rangescale/rangescale_evaluation.php @@ -27,7 +27,7 @@ $options = range($vote->questiondata['minimum'], $vote->questiondata['maximum']) <tr> <th><?= _('Aussage') ?></th> <? for ($i = $vote->questiondata['minimum'] ?? 1; $i <= $vote->questiondata['maximum']; $i++) : ?> - <th class="rangescale_center"><?= htmlReady($i) ?></th> + <th class="option-cell"><?= htmlReady($i) ?></th> <? endfor ?> </tr> </thead> diff --git a/app/views/questionnaire/widget.php b/app/views/questionnaire/widget.php index 044f246..6d7f4b3 100644 --- a/app/views/questionnaire/widget.php +++ b/app/views/questionnaire/widget.php @@ -8,15 +8,18 @@ <nav> <? if ($allowed_to_add) : ?> <? if ($statusgruppen_ids): ?> - <a href="<?= $controller->link_for('questionnaire/add_to_context') ?>" data-dialog="size=auto" title="<?= _('Fragebogen hinzufügen') ?>"> + <a href="<?= $controller->link_for('questionnaire/add_to_context') ?>" data-dialog="size=auto" + title="<?= _('Fragebogen hinzufügen') ?>" aria-label="<?= _('Fragebogen hinzufügen') ?>"> <?= Icon::create("add", "clickable")->asimg("16px", ['class' => "text-bottom"]) ?> </a> <? else: ?> - <a href="<?= $controller->link_for('questionnaire/edit', compact('range_type', 'range_id')) ?>" data-dialog title="<?= _('Fragebogen hinzufügen') ?>"> + <a href="<?= $controller->link_for('questionnaire/edit', compact('range_type', 'range_id')) ?>" data-dialog + title="<?= _('Fragebogen hinzufügen') ?>" aria-label="<?= _('Fragebogen hinzufügen') ?>"> <?= Icon::create("add", "clickable")->asimg("16px", ['class' => "text-bottom"]) ?> </a> <? endif ?> - <a href="<?= URLHelper::getLink("dispatch.php/questionnaire/" . ($range_type == 'course' || $range_type == 'institute' ? 'course' : ''). "overview") ?>" title="<?= _('Fragebögen verwalten') ?>"> + <a href="<?= URLHelper::getLink("dispatch.php/questionnaire/" . ($range_type == 'course' || $range_type == 'institute' ? 'course' : ''). "overview") ?>" + title="<?= _('Fragebögen verwalten') ?>" aria-label="<?= _('Fragebögen verwalten') ?>"> <?= Icon::create("admin", "clickable")->asimg("16px", ['class' => "text-bottom"]) ?> </a> <? endif ?> @@ -31,7 +34,7 @@ <section class="noquestionnaires"> <?= _('Es sind keine Fragebögen vorhanden.') ?> <? if ($allowed_to_add) : ?> - <?= _("Um neue Fragebögen zu erstellen, klicken Sie rechts auf das Plus.") ?> + <?= _("Nutzen Sie die Aktion „Fragebogen hinzufügen“ um einen neuen Fragebogen hinzuzufügen.") ?> <? endif ?> </section> <? endif; ?> diff --git a/app/views/resources/_common/_grouped_room_list.php b/app/views/resources/_common/_grouped_room_list.php index f3d6b62..a27af17 100644 --- a/app/views/resources/_common/_grouped_room_list.php +++ b/app/views/resources/_common/_grouped_room_list.php @@ -64,7 +64,7 @@ <? if ($user) :?> <? $perms = [ - 'show_global_admin_actions' => $show_global_admin_actions, + 'show_global_admin_actions' => $show_global_admin_actions ?? false, 'show_admin_actions' => $resource->userHasPermission($user, 'admin'), 'show_tutor_actions' => $resource->userHasPermission($user, 'tutor'), 'show_autor_actions' => $resource->userHasPermission($user, 'autor'), diff --git a/app/views/resources/_common/_room_tr.php b/app/views/resources/_common/_room_tr.php index 37f26d1..1b9f46a 100644 --- a/app/views/resources/_common/_room_tr.php +++ b/app/views/resources/_common/_room_tr.php @@ -19,7 +19,7 @@ * Defaults to false (do not show actions). * $user_has_booking_rights: Boolean: Whether the user for which this template * is rendered has booking rights on the resource (true) or not (false). - * $show_room_picture: Boolean: Whether to display the room picture or not. + * $show_picture: Boolean: Whether to display the room picture or not. * Defaults to false (do not show picture). * $additional_properties: Array: Additional properties * that shall be displayed in extra columns. diff --git a/app/views/resources/booking/_add_edit_form.php b/app/views/resources/booking/_add_edit_form.php index 571ff63..f982c37 100644 --- a/app/views/resources/booking/_add_edit_form.php +++ b/app/views/resources/booking/_add_edit_form.php @@ -537,7 +537,7 @@ <label> <input type="checkbox" value="1" name="overwrite_bookings" - <?= $overwrite_bookings ? 'checked="checked"' : '' ?>> + <?= !empty($overwrite_bookings) ? 'checked' : '' ?>> <?= _('Vorhandene Buchungen überschreiben') ?> </label> </fieldset> diff --git a/app/views/resources/room_group/permissions.php b/app/views/resources/room_group/permissions.php index 17090ce..1dd970d 100644 --- a/app/views/resources/room_group/permissions.php +++ b/app/views/resources/room_group/permissions.php @@ -28,7 +28,7 @@ <?= $this->render_partial( 'resources/resource/permissions', [ - 'custom_empty_list_message' => _('Es sind keine gemeinsamen Rechte für die oben aufgeführten Räume vorhanden.'), + 'custom_empty_list_message' => _('Es sind keine gemeinsamen Rechte für die zuvor aufgelisteten Räume vorhanden.'), 'custom_save_button_text' => _('Zuweisen'), 'custom_form_action_link' => URLHelper::getLink('dispatch.php/resources/room_group/permissions/' . $clipboard->id), 'custom_hidden_fields' => [ diff --git a/app/views/resources/room_request/decline.php b/app/views/resources/room_request/decline.php index 188c4bb..2c02126 100644 --- a/app/views/resources/room_request/decline.php +++ b/app/views/resources/room_request/decline.php @@ -30,7 +30,7 @@ <? endif ?> <? endif ?> <footer data-dialog-button> - <? if ($prev_request) : ?> + <? if (!empty($prev_request)) : ?> <?= \Studip\LinkButton::create( _('Vorherige Anfrage'), $controller->declineURL($prev_request), @@ -45,7 +45,7 @@ <? if ($show_form) : ?> <?= \Studip\Button::createAccept($delete_mode ? _('Löschen') : _('Ablehnen'), 'confirm') ?> <? endif ?> - <? if ($next_request) : ?> + <? if (!empty($next_request)) : ?> <?= \Studip\LinkButton::create( _('Nächste Anfrage'), $controller->declineURL($next_request), @@ -53,4 +53,4 @@ ) ?> <? endif ?> </footer> -</form>
\ No newline at end of file +</form> diff --git a/app/views/room_management/overview/rooms.php b/app/views/room_management/overview/rooms.php index a6323dc..cdec83e 100644 --- a/app/views/room_management/overview/rooms.php +++ b/app/views/room_management/overview/rooms.php @@ -40,7 +40,7 @@ 'user_has_booking_rights' => $room->userHasBookingRights( $user ), - 'show_room_picture' => true, + 'show_picture' => true, 'additional_columns' => [ $room->category->name, ] diff --git a/app/views/search/module/index.php b/app/views/search/module/index.php index c072cf0..8eac916 100644 --- a/app/views/search/module/index.php +++ b/app/views/search/module/index.php @@ -53,7 +53,7 @@ $pagination->set_attribute('page', $page); $page_link = reset(explode('?', $controller->action_url('index'))) . '?page_module=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render('shared/pagechooser'); + echo $pagination->render(); ?> <? endif; ?> </td> diff --git a/app/views/settings/general.php b/app/views/settings/general.php index 4bd5433..32b1a6f 100644 --- a/app/views/settings/general.php +++ b/app/views/settings/general.php @@ -93,6 +93,21 @@ $start_pages = [ '- auch wenn Sie gerade einen anderen Browsertab anschauen. Der Plopp ist ' . 'nur zu hören, wenn Sie die Benachrichtigungen über Javascript aktiviert haben.')) ?> </label> + + <label> + <?= _('Platzierung von Systembenachrichtigungen im Browserfenster') ?> + <?= tooltipIcon(_('Sie können entscheiden, an welcher Stelle Ihres Browserfensters ' . + 'Systembenachrichtigungen erscheinen sollen: mittig am oberen Rand oder rechts unten.')) ?> + <select name="system_notifications_placement" + aria-describedby="system_notifications_notifications_placement_description"> + <option value="topcenter"<?= $notifications_placement === 'topcenter' ? ' selected' : '' ?>> + <?= _('zentriert am oberen Rand') ?> + </option> + <option value="bottomright"<?= $notifications_placement === 'bottomright' ? ' selected' : '' ?>> + <?= _('am rechten unteren Rand') ?> + </option> + </select> + </label> </fieldset> <fieldset> diff --git a/app/views/settings/notification.php b/app/views/settings/notification.php index b46ea45..2fd4f9c 100644 --- a/app/views/settings/notification.php +++ b/app/views/settings/notification.php @@ -24,7 +24,7 @@ </tr> <tr> <td colspan="2"> - <?= _('Benachrichtigung für unten aufgeführte Veranstaltungen:') ?> + <?= _('Benachrichtigungen für die folgenden Veranstaltungen:') ?> </td> <? $i = 0; ?> <? foreach ($modules as $index => $data): ?> diff --git a/app/views/shared/contacts/add_ranges_to_contact.php b/app/views/shared/contacts/add_ranges_to_contact.php index 389e72c..0f4ae3f 100644 --- a/app/views/shared/contacts/add_ranges_to_contact.php +++ b/app/views/shared/contacts/add_ranges_to_contact.php @@ -16,7 +16,7 @@ <select style="display: inline-block;" name="ansp_typ">
<option value=""<?= empty($ansp_typ) ? ' selected' : '' ?>></option>
<? foreach ($GLOBALS['MVV_CONTACTS']['TYPE']['values'] as $key => $entry) : ?>
- <option value="<?= $key ?>"<?= $key == $ansp_typ ? ' selected' : '' ?>><?= htmlReady($entry['name']) ?></option>
+ <option value="<?= $key ?>"<?= isset($ansp_typ) && $key == $ansp_typ ? ' selected' : '' ?>><?= htmlReady($entry['name']) ?></option>
<? endforeach; ?>
</select>
</label>
@@ -26,7 +26,7 @@ <?= _('Kategorie') ?>
<select style="display: inline-block;" name="ansp_kat">
<? foreach (MvvContactRange::getCategoriesByRangetype($range_type) as $key => $entry) : ?>
- <option value="<?= $key ?>"<?= $key == $ansp_kat ? ' selected' : '' ?>><?= htmlReady($entry['name']) ?></option>
+ <option value="<?= $key ?>"<?= isset($ansp_kat) && $key == $ansp_kat ? ' selected' : '' ?>><?= htmlReady($entry['name']) ?></option>
<? endforeach; ?>
</select>
</label>
diff --git a/app/views/shared/contacts/details.php b/app/views/shared/contacts/details.php index f39d200..a4605ce 100644 --- a/app/views/shared/contacts/details.php +++ b/app/views/shared/contacts/details.php @@ -45,7 +45,7 @@ </td> <? if ($object_type === 'Studiengang') : ?> <td> - <?= htmlReady($GLOBALS['MVV_CONTACTS']['TYPE']['values'][$rel['type']]['name']); ?> + <?= htmlReady($GLOBALS['MVV_CONTACTS']['TYPE']['values'][$rel['type']]['name'] ?? ''); ?> </td> <? endif; ?> <td> diff --git a/app/views/shared/contacts/index.php b/app/views/shared/contacts/index.php index 6073574..6604263 100644 --- a/app/views/shared/contacts/index.php +++ b/app/views/shared/contacts/index.php @@ -84,7 +84,7 @@ $pagination->set_attribute('page', $page); $page_link = reset(explode('?', $controller->action_url('index'))) . '?page_contacts=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render('shared/pagechooser'); + echo $pagination->render(); ?> </td> </tr> diff --git a/app/views/shared/contacts/range.php b/app/views/shared/contacts/range.php index d66c494..f58b286 100644 --- a/app/views/shared/contacts/range.php +++ b/app/views/shared/contacts/range.php @@ -40,7 +40,7 @@ <td><?= htmlReady($mvv_contact->position); ?></td> <td><?= htmlReady($mvv_contact->name) ?></td> <? if($range_type !== 'Modul'): ?> - <td><?= htmlReady($GLOBALS['MVV_CONTACTS']['TYPE']['values'][$mvv_contact->type]['name']) ?></td> + <td><?= htmlReady($GLOBALS['MVV_CONTACTS']['TYPE']['values'][$mvv_contact->type]['name'] ?? '') ?></td> <? endif; ?> <td><?= htmlReady($mvv_contact->getCategoryDisplayname()); ?></td> <td ><?= htmlReady($mvv_contact->count_relations); ?></td> diff --git a/app/views/shared/log_event/show.php b/app/views/shared/log_event/show.php index ea96f89..eec2164 100644 --- a/app/views/shared/log_event/show.php +++ b/app/views/shared/log_event/show.php @@ -32,7 +32,7 @@ use Studip\Button, Studip\LinkButton; </th> </tr> <? foreach ($log_events as $log_event): ?> - <tr class="<?= TextHelper::cycle('table_row_even', 'table_row_odd') ?>"> + <tr> <td style="font-size: smaller; white-space: nowrap;"> <?= date('d.m.Y H:i:s', $log_event['time']) ?> </td> diff --git a/app/views/shared/modul/_modul.php b/app/views/shared/modul/_modul.php index c813c44..461e486 100644 --- a/app/views/shared/modul/_modul.php +++ b/app/views/shared/modul/_modul.php @@ -46,7 +46,7 @@ <td><strong><?= _('Semester der erstmaligen Durchführung') ?></strong></td> <td data-mvv-field="mvv_modul.start"><?= htmlReady($startSemester['name'] ?? '') ?></td> </tr> - <? if ($instituteName) : ?> + <? if (!empty($instituteName)) : ?> <tr> <td><strong><?= _('Fachbereich/Institut') ?></strong></td> <td data-mvv-field="mvv_modul_inst"><?= htmlReady($instituteName) ?></td> @@ -135,7 +135,7 @@ <td><strong><?= _('Modulinhalte') ?></strong></td> <td data-mvv-field="mvv_modul_deskriptor.inhalte"><?= formatReady($modulDeskriptor->inhalte) ?></td> </tr> - <? if ($type !== 3) : ?> + <? if (!isset($type) || $type !== 3) : ?> <tr> <td><strong><?= ngettext('Lehrveranstaltungsform', 'Lehrveranstaltungsformen', count($modul->modulteile)) ?></strong></td> <td data-mvv-field="mvv_modulteil_deskriptor.lernlehrform"> @@ -171,7 +171,7 @@ </tr> <tr> <td><strong><?= _('Prüfungsebene') ?></strong></td> - <td data-mvv-field="mvv_modul.pruef_ebene"><?= htmlReady($pruefungsEbene) ?></td> + <td data-mvv-field="mvv_modul.pruef_ebene"><?= htmlReady($pruefungsEbene ?? '') ?></td> </tr> <tr> <td><strong><?= _('Credit-Points') ?></strong></td> @@ -180,7 +180,7 @@ <tr> <td><strong><?= _('Modulabschlussnote') ?></strong></td> <td> - <? if ($type !== 3) : ?> + <? if (!isset($type) || $type !== 3) : ?> <? $nummer_modulteil = 1; ?> <? $note = []; ?> <? foreach ($modul->modulteile as $modulteil): ?> diff --git a/app/views/shared/modul/_modul_ohne_lv.php b/app/views/shared/modul/_modul_ohne_lv.php index 07e5310..f3c74ec 100644 --- a/app/views/shared/modul/_modul_ohne_lv.php +++ b/app/views/shared/modul/_modul_ohne_lv.php @@ -3,12 +3,12 @@ <? $modulSumme = $modul->wl_selbst + $modul->wl_pruef ?> <tr> <td style="width: 30%;"><strong><?= _('Workload selbstgestaltete Arbeit') ?></strong></td> - <td style="width: 70%;" data-mvv-field="mvv_modul.wl_selbst mvv_modul_deskriptor.kommentar_wl_selbst"><?= htmlReady($modul->wl_selbst) ?> <?= MVVController::trim($modulDeskriptor->kommentar_wl_selbst) ? sprintf(" (%s)", formatReady($modulDeskriptor->kommentar_wl_selbst)) : '' ?></td> + <td style="width: 70%;" data-mvv-field="mvv_modul.wl_selbst mvv_modul_deskriptor.kommentar_wl_selbst"><?= htmlReady($modul->wl_selbst) ?> <?= MVVController::trim($modulDeskriptor->kommentar_wl_selbst ?? '') ? sprintf(" (%s)", formatReady($modulDeskriptor->kommentar_wl_selbst)) : '' ?></td> </tr> <tr> <td style="width: 30%;"><strong><?= _('Workload Prüfung incl. Vorbereitung') ?></strong></td> - <td style="width: 70%;" data-mvv-field="mvv_modul.wl_pruef mvv_modul_deskriptor.kommentar_wl_pruef"><?= htmlReady($modul->wl_pruef) ?> <?= MVVController::trim($modulDeskriptor->kommentar_wl_pruef) ? sprintf(" (%s)", formatReady($modulDeskriptor->kommentar_wl_pruef)) : '' ?></td> + <td style="width: 70%;" data-mvv-field="mvv_modul.wl_pruef mvv_modul_deskriptor.kommentar_wl_pruef"><?= htmlReady($modul->wl_pruef) ?> <?= MVVController::trim($modulDeskriptor->kommentar_wl_pruef ?? '') ? sprintf(" (%s)", formatReady($modulDeskriptor->kommentar_wl_pruef)) : '' ?></td> </tr> <tr> @@ -17,21 +17,21 @@ </tr> </tbody> </table> -<table class="mvv-modul-details" data-mvv-id="<?= $modulDeskriptor?$modulDeskriptor->getId():''; ?>" data-mvv-type="moduldeskriptor"> +<table class="mvv-modul-details" data-mvv-id="<?= isset($modulDeskriptor) ? $modulDeskriptor->getId() : ''; ?>" data-mvv-type="moduldeskriptor"> <tbody> - <? if (trim($modulDeskriptor->pruef_vorleistung)) : ?> + <? if (trim($modulDeskriptor->pruef_vorleistung ?? '')) : ?> <tr> <td style="width: 30%;"><strong><?= _('Prüfungsvorleistung') ?></strong></td> - <td style="width: 70%;" data-mvv-field="mvv_modul_deskriptor.pruef_vorleistung" ><?= formatReady($modulDeskriptor->pruef_vorleistung) ?></td> + <td style="width: 70%;" data-mvv-field="mvv_modul_deskriptor.pruef_vorleistung" ><?= formatReady($modulDeskriptor->pruef_vorleistung ?? '') ?></td> </tr> <? endif; ?> <tr> <td style="width: 30%;"><strong><?= _('Prüfungsform') ?></strong></td> - <td style="width: 70%;" data-mvv-field="mvv_modul_deskriptor.pruef_leistung"><?= formatReady($modulDeskriptor->pruef_leistung) ?></td> + <td style="width: 70%;" data-mvv-field="mvv_modul_deskriptor.pruef_leistung"><?= formatReady($modulDeskriptor->pruef_leistung ?? '') ?></td> </tr> <tr> <td style="width: 30%;"><strong><?= _('Wiederholungsprüfung') ?></strong></td> - <td style="width: 70%;" data-mvv-field="mvv_modul_deskriptor.pruef_wiederholung"><?= formatReady($modulDeskriptor->pruef_wiederholung) ?></td> + <td style="width: 70%;" data-mvv-field="mvv_modul_deskriptor.pruef_wiederholung"><?= formatReady($modulDeskriptor->pruef_wiederholung ?? '') ?></td> </tr> </tbody> </table> diff --git a/app/views/siteinfo/help.php b/app/views/siteinfo/help.php index 54d0d09..e2a6762 100644 --- a/app/views/siteinfo/help.php +++ b/app/views/siteinfo/help.php @@ -59,7 +59,6 @@ <li>news</li> <li>vote</li> <li>test</li> - <li>evaluation</li> <li>wiki_pages</li> <li>lernmodul</li> <li>resource</li> diff --git a/app/views/start/index.php b/app/views/start/index.php index f4e7fcc..6a177f6 100644 --- a/app/views/start/index.php +++ b/app/views/start/index.php @@ -1,6 +1,7 @@ <?php /** * @var array $columns + * @var Flexi\Template $widget_layout */ ?> <h1 class="sr-only"> @@ -29,7 +30,8 @@ if (Config::get()->BANNER_ADS_ENABLE) { <li class="studip-widget-wrapper" id="<?= $widget->getPluginId() ?>"> <div class="ui-widget-content studip-widget"> <? if ($template = $widget->getPortalTemplate()): ?> - <? $template->set_layout($this->_factory->open('start/_widget')) ?> + <? $widget_layout->clear_attributes() ?> + <? $template->set_layout($widget_layout) ?> <?= $this->render_partial($template, compact('widget')) ?> <? else: ?> <?= $this->render_partial('start/_widget', compact('widget')) ?> diff --git a/app/views/studiengaenge/faecher/index.php b/app/views/studiengaenge/faecher/index.php index 1b3f325..485d922 100644 --- a/app/views/studiengaenge/faecher/index.php +++ b/app/views/studiengaenge/faecher/index.php @@ -64,7 +64,7 @@ $parts = explode('?', $controller->action_url('index')); $page_link = reset($parts) . '?page_faecher=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render("shared/pagechooser"); + echo $pagination->render(); ?> </td> </tr> diff --git a/app/views/studiengaenge/studiengaenge/index.php b/app/views/studiengaenge/studiengaenge/index.php index 26249d0..37d4d1d 100644 --- a/app/views/studiengaenge/studiengaenge/index.php +++ b/app/views/studiengaenge/studiengaenge/index.php @@ -42,7 +42,7 @@ $parts = explode('?', $controller->action_url('index')); $page_link = reset($parts) . '?page_studiengaenge=%s'; $pagination->set_attribute('pagelink', $page_link); - echo $pagination->render('shared/pagechooser'); + echo $pagination->render(); ?> </td> diff --git a/app/views/studiengaenge/versionen/abschnitte.php b/app/views/studiengaenge/versionen/abschnitte.php index 1620450..83c1173 100644 --- a/app/views/studiengaenge/versionen/abschnitte.php +++ b/app/views/studiengaenge/versionen/abschnitte.php @@ -59,8 +59,7 @@ </tr> <? endif; ?> </tbody> - <? endforeach; - TextHelper::reset_cycle(); ?> + <? endforeach; ?> <? if (count($version->abschnitte) > 0 && MvvPerm::haveFieldPermModul_zuordnungen('StgteilAbschnitt', MvvPerm::PERM_CREATE) ) : ?> <tbody> diff --git a/app/views/studiengaenge/versionen/details_abschnitt.php b/app/views/studiengaenge/versionen/details_abschnitt.php index 3b1c0e7..79f8da7 100644 --- a/app/views/studiengaenge/versionen/details_abschnitt.php +++ b/app/views/studiengaenge/versionen/details_abschnitt.php @@ -59,7 +59,7 @@ </tr> <? endif; ?> </tbody> - <? endforeach; TextHelper::reset_cycle(); ?> + <? endforeach; ?> <? if (MvvPerm::haveFieldPermModul_zuordnungen($abschnitt, MvvPerm::PERM_CREATE)) : ?> <tbody> <tr> diff --git a/app/views/studiengaenge/versionen/index.php b/app/views/studiengaenge/versionen/index.php index ebf8fb1..6460a59 100644 --- a/app/views/studiengaenge/versionen/index.php +++ b/app/views/studiengaenge/versionen/index.php @@ -29,7 +29,7 @@ $pagination->set_attribute('num_postings', $count); $pagination->set_attribute('page', $page); $pagination->set_attribute('pagelink', '?page=%s'); - echo $pagination->render('shared/pagechooser'); + echo $pagination->render(); ?> </td> </tr> diff --git a/app/views/web_migrate/index.php b/app/views/web_migrate/index.php index b26b90c..589d50a 100644 --- a/app/views/web_migrate/index.php +++ b/app/views/web_migrate/index.php @@ -51,7 +51,7 @@ <?= htmlReady($number) ?> </td> <td> - <?= htmlReady(get_class($migration)) ?> + <?= htmlReady($migration->getName()) ?> </td> <td> <? if ($migration->description()): ?> |
