Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • alexander.vorwerk/studip
  • hochschule-wismar/stud-ip
  • tleilax/studip
  • marcus/studip
  • manschwa/studip
  • eberhardt/studip
  • uol/studip
  • pluta/studip
  • thienel/extern-uni-b
  • studip/studip
  • strohm/studip
  • uni-osnabrueck/studip
  • FloB/studip
  • universit-t-rostock/studip
  • Robinyyy/studip
  • jakob.diel/studip
  • HyperSpeeed/studip
  • ann/studip
  • nod3zer0/stud-ip-siple-saml-php-plugin
  • erik.hillmann/studip
20 results
Select Git revision
Show changes
Showing
with 1106 additions and 679 deletions
......@@ -47,7 +47,6 @@ class Admin_CacheController extends AuthenticatedController
$this->sidebar->addWidget($views);
if ($this->enabled) {
$actions = new ActionsWidget();
$actions->addLink(
_('Cache leeren'),
......@@ -57,35 +56,35 @@ class Admin_CacheController extends AuthenticatedController
);
$this->sidebar->addWidget($actions);
}
}
/**
* Show all available cache types.
*/
public function settings_action()
{
if ($this->enabled) {
$this->types = CacheType::findAndMapBySQL(function (CacheType $type) {
return $type->toArray();
}, "1 ORDER BY `cache_id`");
$currentCache = Config::get()->SYSTEMCACHE;
$currentCacheClass = CacheType::findOneByClass_name($currentCache['type']);
$this->cache = $currentCacheClass->class_name;
$this->config = $currentCacheClass->class_name::getConfig();
} else {
PageLayout::postWarning(
_('Caching ist systemweit ausgeschaltet, daher kann hier nichts konfiguriert werden.'));
}
$this->render_vue_app(
Studip\VueApp::create('CacheAdministration')
->withProps([
'enabled' => (bool) $this->enabled,
'currentCache' => $currentCacheClass->class_name,
'currentConfig' => $currentCacheClass->class_name::getConfig(),
'cacheTypes' => CacheType::findAndMapBySQL(
fn(CacheType $type) => $type->toArray(),
"1 ORDER BY `cache_id`"
),
])
);
}
/**
* 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::loadSystemCache(true);
$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();
}
......
......@@ -127,7 +127,7 @@ class Admin_ConfigurationController extends AuthenticatedController
$this->configs = ConfigurationModel::searchConfiguration($range);
$this->title = sprintf(
_('Vorhandene Konfigurationsparameter für "%s"'),
$range->getFullname()
$range->getFullName()
);
$this->linkchunk = 'admin/configuration/edit_range_config/' . $range_id;
} else {
......@@ -148,7 +148,7 @@ class Admin_ConfigurationController extends AuthenticatedController
$field = Request::get('field');
$range = RangeFactory::find($range_id);
PageLayout::setTitle(_('Bearbeiten von Konfigurationsparametern für die Range: ') . $range->getFullname());
PageLayout::setTitle(_('Bearbeiten von Konfigurationsparametern für die Range: ') . $range->getFullName());
if (Request::isPost()) {
CSRFProtection::verifyUnsafeRequest();
......@@ -186,7 +186,7 @@ class Admin_ConfigurationController extends AuthenticatedController
$this->configs = ConfigurationModel::searchConfiguration($user);
$this->title = sprintf(
_('Vorhandene Konfigurationsparameter für "%s"'),
$user->getFullname()
$user->getFullName()
);
$this->linkchunk = 'admin/configuration/edit_user_config/' . $user_id;
} else {
......@@ -205,7 +205,7 @@ class Admin_ConfigurationController extends AuthenticatedController
*/
public function edit_user_config_action(User $user)
{
PageLayout::setTitle(_('Bearbeiten von Konfigurationsparametern für die Person: ') . $user->getFullname());
PageLayout::setTitle(_('Bearbeiten von Konfigurationsparametern für die Person: ') . $user->getFullName());
$field = Request::get('field');
......@@ -246,7 +246,7 @@ class Admin_ConfigurationController extends AuthenticatedController
$this->configs = ConfigurationModel::searchConfiguration($course);
$this->title = sprintf(
_('Vorhandene Konfigurationsparameter für "%s"'),
$course->getFullname()
$course->getFullName()
);
$this->linkchunk = 'admin/configuration/edit_course_config/' . $course_id;
} else {
......@@ -265,7 +265,7 @@ class Admin_ConfigurationController extends AuthenticatedController
*/
public function edit_course_config_action(Course $course)
{
PageLayout::setTitle(_('Bearbeiten von Konfigurationsparametern für die Veranstaltung: ') . $course->getFullname());
PageLayout::setTitle(_('Bearbeiten von Konfigurationsparametern für die Veranstaltung: ') . $course->getFullName());
$field = Request::get('field');
......@@ -306,7 +306,7 @@ class Admin_ConfigurationController extends AuthenticatedController
$this->configs = ConfigurationModel::searchConfiguration($institute);
$this->title = sprintf(
_('Vorhandene Konfigurationsparameter für "%s"'),
$institute->getFullname()
$institute->getFullName()
);
$this->linkchunk = 'admin/configuration/edit_institute_config/' . $institute_id;
} else {
......@@ -325,7 +325,7 @@ class Admin_ConfigurationController extends AuthenticatedController
*/
public function edit_institute_config_action(Institute $institute)
{
PageLayout::setTitle(_('Bearbeiten von Konfigurationsparametern für die Einrichtung: ') . $institute->getFullname());
PageLayout::setTitle(_('Bearbeiten von Konfigurationsparametern für die Einrichtung: ') . $institute->getFullName());
$field = Request::get('field');
......
......@@ -12,10 +12,12 @@ class Admin_CourseplanningController extends AuthenticatedController
{
parent::before_filter($action, $args);
if ($GLOBALS['perm']->have_perm('admin')) {
Navigation::activateItem('/browse/my_courses/schedule');
if (!$GLOBALS['perm']->have_perm('admin')) {
throw new AccessDeniedException();
}
Navigation::activateItem('/browse/my_courses/schedule');
$this->insts = Institute::getMyInstitutes($GLOBALS['user']->id);
if (empty($this->insts) && !$GLOBALS['perm']->have_perm('root')) {
......@@ -48,10 +50,7 @@ class Admin_CourseplanningController extends AuthenticatedController
$stgteil = StudiengangTeil::find($GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL);
$plan_title .= ' - ' . $stgteil->getDisplayName();
}
if (
isset($this->semester)
&& $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE
&& $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE !== 'all'
if (isset($this->semester) && $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE
) {
$plan_title .= ' - ' . $this->semester->name;
}
......@@ -85,7 +84,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 (empty($event['conform']) || $time_elements[0] % 2) {
Sidebar::get()->getWidget('actions')->addLink(
_('Veranstaltungen außerhalb des Rasters'),
$this->nonconformURL(),
......@@ -611,7 +610,7 @@ class Admin_CourseplanningController extends AuthenticatedController
$sidebar = Sidebar::Get();
$list = new SelectWidget(_('Semester'), $this->url_for('admin/courseplanning/set_selection/' . $this->selected_weekday), 'sem_select');
foreach ($semesters as $semester) {
if (!$GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE ||$GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE == 'all') {
if (!$GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE) {
$GLOBALS['user']->cfg->store('MY_COURSES_SELECTED_CYCLE', $semester->id);
}
$list->addElement(new SelectElement(
......@@ -726,7 +725,7 @@ class Admin_CourseplanningController extends AuthenticatedController
$sidebar = Sidebar::Get();
$list = new SelectWidget(_('Lehrendenfilter'), $this->url_for('admin/courseplanning/index'), 'teacher_filter');
$list->addElement(new SelectElement('all', _('alle'), Request::get('teacher_filter') == 'all'), 'teacher_filter-all');
$list->addElement(new SelectElement('', _('alle'), Request::get('teacher_filter') === ''), 'teacher_filter-all');
foreach ($teachers as $teacher) {
$list->addElement(new SelectElement(
......@@ -848,7 +847,7 @@ class Admin_CourseplanningController extends AuthenticatedController
if (Request::option('sem_select')) {
$GLOBALS['user']->cfg->store('MY_COURSES_SELECTED_CYCLE', Request::option('sem_select'));
if (Request::option('sem_select') !== 'all') {
if (Request::option('sem_select') !== '') {
PageLayout::postSuccess(sprintf(
_('Das %s wurde ausgewählt'),
htmlReady(Semester::find(Request::option('sem_select'))->name)
......
......@@ -22,7 +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
......@@ -170,11 +170,16 @@ class Admin_CoursesController extends AuthenticatedController
Now draw the configurable elements according
to the values inside the visibleElements array.
*/
$institute_id = null;
if (!empty($visibleElements['search'])) {
$this->setSearchWiget();
}
if (!empty($visibleElements['institute'])) {
$filter->addElement($this->getInstSelector());
$inst_selector = $filter->addElement($this->getInstSelector());
if (count($inst_selector->getOptions()) === 1) {
$institute_id = $this->insts[0]['Institut_id'];
}
}
if (!empty($visibleElements['semester'])) {
$filter->addElement($this->getSemesterSelector());
......@@ -186,7 +191,7 @@ class Admin_CoursesController extends AuthenticatedController
$filter->addElement($this->getCourseTypeWidget());
}
if (!empty($visibleElements['teacher'])) {
$filter->addElement($this->getTeacherWidget());
$filter->addElement($this->getTeacherWidget($institute_id));
}
$sidebar->addWidget($filter, 'filter');
......@@ -292,8 +297,7 @@ class Admin_CoursesController extends AuthenticatedController
PageLayout::setHelpKeyword('Basis.Veranstaltungen');
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;
}
/**
......@@ -302,17 +306,11 @@ class Admin_CoursesController extends AuthenticatedController
public function index_action()
{
$this->fields = $this->getViewFilters();
$this->sortby = $GLOBALS['user']->cfg->MEINE_SEMINARE_SORT ?? 'name';
$this->sortby = $GLOBALS['user']->cfg->MEINE_SEMINARE_SORT ?? (Config::get()->IMPORTANT_SEMNUMBER ? 'number' : 'name');
$this->sortflag = $GLOBALS['user']->cfg->MEINE_SEMINARE_SORT_FLAG ?? 'ASC';
$this->store_data = $this->getStoreData();
$this->buildSidebar();
PageLayout::addHeadElement('script', [
'type' => 'text/javascript',
], sprintf(
'window.AdminCoursesStoreData = %s;',
json_encode($this->getStoreData())
));
}
private function getStoreData(): array
......@@ -322,6 +320,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 +376,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' => []
......@@ -393,16 +394,31 @@ class Admin_CoursesController extends AuthenticatedController
$activated_fields = $this->getFilterConfig();
$GLOBALS['user']->cfg->store('MY_COURSES_ACTION_AREA', Request::option('action'));
$course_ids = [];
foreach ($courses as $course) {
if ($course->parent_course && !Request::option('course_id')) {
continue;
if ($course->parent && !Request::option('course_id')) {
if (!in_array($course->parent->id, $course_ids)) {
$data['data'][] = $this->getCourseData($course->parent, $activated_fields);
$course_ids[] = $course->parent->id;
}
if (!in_array($course->id, $course_ids)) {
$data['data'][] = $this->getCourseData($course, $activated_fields);
$course_ids[] = $course->id;
}
} else {
if (!in_array($course->id, $course_ids)) {
$data['data'][] = $this->getCourseData($course, $activated_fields);
$course_ids[] = $course->id;
}
foreach ($course->children as $childcourse) {
if (!in_array($childcourse->id, $course_ids)) {
$data['data'][] = $this->getCourseData($childcourse, $activated_fields);
$course_ids[] = $childcourse->id;
}
}
}
$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:
......@@ -411,7 +427,6 @@ class Admin_CoursesController extends AuthenticatedController
break;
case 8: //Sperrebenen
$template = $tf->open('admin/courses/lock_preselect');
$template->course = $course;
$template->all_lock_rules = new SimpleCollection(array_merge(
[[
'name' => '--' . _('keine Sperrebene') . '--',
......@@ -428,7 +443,6 @@ class Admin_CoursesController extends AuthenticatedController
break;
case 10: //Zusatzangaben
$template = $tf->open('admin/courses/aux_preselect');
$template->course = $course;
$template->aux_lock_rules = AuxLockRule::findBySQL('1 ORDER BY name ASC');
$data['buttons_top'] = $template->render();
$data['buttons_bottom'] = (string) \Studip\Button::createAccept(_('Zusatzangaben'), 'aux_button', ['formaction' => URLHelper::getURL('dispatch.php/admin/courses/set_aux_lockrule')]);
......@@ -467,17 +481,27 @@ class Admin_CoursesController extends AuthenticatedController
$data['buttons_bottom'] = (string) \Studip\Button::createAccept(
_('Teilnehmendenexport'), 'batch_export_members',
[
'formaction' => URLHelper::getURL('dispatch.php/admin/user/batch_export_members'),
'formaction' => URLHelper::getURL('dispatch.php/admin/courses/batch_export_members'),
'data-dialog' => 'size=big'
]);
break;
case 23: // Mass mail to selected courses
$data['buttons_top'] = '<label>' . _('Alle auswählen') .
'<input type="checkbox" data-proxyfor=".course-admin td:last-child :checkbox"></label>';
$data['buttons_bottom'] = (string) \Studip\Button::createAccept(
_('Nachricht an ausgewählte Veranstaltungen'), 'massmail',
[
'formaction' => URLHelper::getURL('dispatch.php/massmail/quick/courses'),
'data-dialog' => 'size=big'
]);
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 Flexi\Template) {
$data['buttons_bottom'] = $multimode->render();
} elseif ($multimode instanceof \Studip\Button) {
$data['buttons_bottom'] = (string) $multimode;
......@@ -516,14 +540,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();
......@@ -561,37 +631,36 @@ 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]).'">'
.$course->veranstaltungsnummer
$d['number'] = '<a href="'.URLHelper::getLink('dispatch.php/course/basicdata/view', ['cid' => $course->id]).'">'
. htmlReady($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>";
}
if (in_array('type', $activated_fields)) {
$semtype = $course->getSemType();
$d['type'] = $semtype['name'];
$d['type'] = htmlReady($semtype['name']);
}
if (in_array('room_time', $activated_fields)) {
$seminar = new Seminar($course);
$d['room_time'] = $seminar->getDatesHTML([
'show_room' => true,
]) ?: _('nicht angegeben');
$strings = $course->getAllDatesInSemester()->toStringArray();
$d['room_time'] = implode('<br>', $strings) ?: _('nicht angegeben');
}
if (in_array('semester', $activated_fields)) {
$d['semester'] = $course->semester_text;
$d['semester'] = htmlReady($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;
$d['institute'] = htmlReady($course->home_institut ? $course->home_institut->name : $course->institute);
}
if (in_array('requests', $activated_fields)) {
$d['requests'] = '<a href="'.URLHelper::getLink('dispatch.php/course/room_requests', ['cid' => $course->id]).'">'.count($course->room_requests)."</a>";
......@@ -605,7 +674,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)) {
......@@ -635,8 +704,8 @@ class Admin_CoursesController extends AuthenticatedController
foreach ($icons as $icon) {
$d['contents'] .= '<li class="my-courses-navigation-item '. ($icon->getImage()->signalsAttention() ? 'my-courses-navigation-important' : '').'">
<a href="'. URLHelper::getLink('seminar_main.php', ['auswahl' => $course->id, 'redirect_to' => $icon->getURL()]).'"'. ($icon->getTitle() ? ' title="'.htmlReady($icon->getTitle()).'"' : '') .'>
'. $icon->getImage()->asImg(20) .'
<a href="'. URLHelper::getLink('dispatch.php/course/go', ['to' => $course->id, 'redirect_to' => $icon->getURL()]).'"'. ($icon->getTitle() ? ' title="'.htmlReady($icon->getTitle()).'"' : '') .'>
'. $icon->getImage()->asImg() .'
</a>
</li>';
}
......@@ -648,15 +717,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:
......@@ -769,20 +841,23 @@ class Admin_CoursesController extends AuthenticatedController
$d['action'] = $template->render();
break;
case 22: //Masssenexport Teilnehmendendaten
$template = $tf->open('admin/courses/batch_export_members');
$template = $tf->open('admin/courses/export_members');
$template->course = $course;
$d['action'] = $template->render();
break;
case 22: //Masssenexport Teilnehmendendaten
$template = $tf->open('admin/courses/batch_export_members');
case 23: //Masssenexport Teilnehmendendaten
$template = $tf->open('admin/courses/massmail');
$template->course = $course;
$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;
}
}
......@@ -897,6 +972,7 @@ class Admin_CoursesController extends AuthenticatedController
if (count($filter_config) > 0) {
$filter = AdminCourseFilter::get();
PluginEngine::sendMessage(AdminCourseWidgetPlugin::class, 'applyFilters', $filter);
$filter->query->orderBy('seminare.name');
$courses = $filter->getCourses();
$view_filters = $this->getViewFilters();
......@@ -904,7 +980,6 @@ class Admin_CoursesController extends AuthenticatedController
$data = [];
foreach ($courses as $course) {
$sem = new Seminar($course);
$row = [];
if (in_array('number', $filter_config)) {
......@@ -924,11 +999,9 @@ class Admin_CoursesController extends AuthenticatedController
}
if (in_array('room_time', $filter_config)) {
$_room = $sem->getDatesExport([
'semester_id' => $this->semester->id,
'show_room' => true
]);
$row['room_time'] = $_room ?: _('nicht angegeben');
$dates = $course->getAllDatesInSemester($this->semester);
$date_strings = $dates->toStringArray(true);
$row['room_time'] = implode("\n", $date_strings) ?: _('nicht angegeben');
}
if (in_array('requests', $filter_config)) {
......@@ -971,14 +1044,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);
}
}
}
......@@ -990,7 +1063,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;
......@@ -1126,7 +1199,7 @@ class Admin_CoursesController extends AuthenticatedController
$course = Course::find($course_id);
if ($course->isOpenEnded() || $course->end_semester->visible) {
$visibility = $visibilites[$course_id] ?: 0;
$visibility = $visibilites[$course_id] ?? 0;
if ($course->visible == $visibility) {
continue;
......@@ -1257,6 +1330,73 @@ class Admin_CoursesController extends AuthenticatedController
$this->notice = $course->config->COURSE_ADMIN_NOTICE;
}
public function batch_export_members_action()
{
PageLayout::setTitle(_('Teilnehmendendaten exportieren'));
$courseIds = Request::optionArray('export_members');
$order = Config::get()->IMPORTANT_SEMNUMBER
? "ORDER BY `VeranstaltungsNummer`, `Name`"
: "ORDER BY `Name`";
$this->courses = Course::findMany($courseIds, $order);
// check if at least one course was selected (this can only happen from admin courses overview):
if (count($this->courses) === 0) {
PageLayout::postWarning('Es wurde keine Veranstaltung gewählt.');
$this->relocate('admin/courses');
}
}
/*
* Export member data of all selected courses
*/
public function do_batch_export_action()
{
if (Request::submitted('xlsx')) {
$export_format = 'xlsx';
} else if (Request::submitted('csv')) {
$export_format = 'csv';
} else {
PageLayout::postError('Nicht unterstütztes Exportformat.');
$this->relocate('admin/courses');
}
$tmp_folder = $GLOBALS['TMP_PATH'] . '/temp_folder_' . md5(uniqid());
mkdir($tmp_folder);
$courses = Course::findMany(Request::optionArray('courses'));
$header = [
_('Status'),
_('Anrede'),
_('Titel'),
_('Vorname'),
_('Nachname'),
_('Titel nachgestellt'),
_('Benutzername'),
_('Adresse'),
_('Telefonnr.'),
_('E-Mail'),
_('Anmeldedatum'),
_('Matrikelnummer'),
_('Studiengänge'),
_('Position'),
];
foreach ($courses as $course) {
if ($GLOBALS['perm']->have_studip_perm('dozent', $course->id)) {
$members = $course->getMembersData();
$filename = FileManager::cleanFileName('Teilnehmendenexport ' . $course->Name . '.' . $export_format);
$filepath = $tmp_folder . '/'. $filename;
$this->render_spreadsheet($header, $members, $export_format, $filename, $filepath);
}
}
$archive_file_path = $GLOBALS['TMP_PATH'] . '/archiv.zip';
$archive_filename = 'Export_Teilnehmendendaten.zip';
FileArchiveManager::createArchiveFromPhysicalFolder($tmp_folder, $archive_file_path);
rmdirr($tmp_folder);
$this->render_temporary_file($archive_file_path, $archive_filename, 'application/zip');
}
/**
* Return a specifically action or all available actions
......@@ -1367,12 +1507,20 @@ class Admin_CoursesController extends AuthenticatedController
22 => [
'name' => _('Teilnehmendenexport'),
'title' => _('Teilnehmendenexport'),
'url' => 'dispatch.php/admin/user/batch_export_members',
'url' => 'dispatch.php/admin/courses/batch_export_members',
'dialogform' => true,
'multimode' => true,
'partial' => 'batch_export_members.php'
'partial' => 'export_members.php'
],
23 => [
'name' => _('Nachricht schreiben'),
'title' => _('Nachricht schreiben'),
'url' => 'dispatch.php/massmail/quick/courses',
'dialogform' => true,
'multimode' => true,
'partial' => 'massmail.php'
]
];
if (!$GLOBALS['perm']->have_perm('admin')) {
......@@ -1388,7 +1536,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(),
......@@ -1428,7 +1576,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;
}
......@@ -1436,169 +1584,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
......@@ -1640,6 +1625,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.
......@@ -1652,7 +1638,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
);
}
}
......@@ -1685,6 +1672,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') {
......@@ -1772,15 +1762,30 @@ class Admin_CoursesController extends AuthenticatedController
*/
private function getTeacherWidget($institut_id = null)
{
$institut_id = $institut_id ?: $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT;
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
......@@ -1792,7 +1797,7 @@ class Admin_CoursesController extends AuthenticatedController
return $ret;
}
);
}
$list = [];
if (!$institut_id || $institut_id === 'all') {
......@@ -1830,6 +1835,7 @@ class Admin_CoursesController extends AuthenticatedController
$GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT
);
$search->setOnSubmitHandler("STUDIP.AdminCourses.App.changeFilter({search: $(this).find('input').val()}); return false;");
$search->setOnClearHandler("STUDIP.AdminCourses.App.changeFilter({search: ''});");
$sidebar->addWidget($search, 'filter_search');
}
......
......@@ -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,14 +134,7 @@ 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']);
......@@ -158,7 +147,7 @@ class Admin_Cronjobs_SchedulesController extends AuthenticatedController
if ($schedule->active) {
$schedule->next_execution = $schedule->calculateNextExecution();
}
}
$schedule->store();
PageLayout::postSuccess(_('Die Änderungen wurden gespeichert.'));
......
......@@ -95,14 +95,16 @@ 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') {
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');
......@@ -154,7 +156,13 @@ class Admin_DatafieldsController extends AuthenticatedController
} elseif ($type === 'studycourse') {
$datafield->object_class = Request::option('object_class');
} else {
$datafield->object_class = array_sum(Request::getArray('object_class')) ?: null;
$object_class = Request::getArray('object_class');
if (empty($object_class) || (count($object_class) === 1 && $object_class[0] === 'NULL')) {
$object_class = null;
} else {
$object_class = array_sum($object_class);
}
$datafield->object_class = $object_class;
}
$datafield->edit_perms = Request::get('edit_perms');
$datafield->view_perms = Request::get('visibility_perms');
......@@ -186,6 +194,7 @@ class Admin_DatafieldsController extends AuthenticatedController
$this->institutes = Institute::getMyInstitutes();
if (!$this->object_typ) {
$this->render_action('type_select');
return;
}
if (Request::isXhr() && $this->type_name) {
......
......@@ -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);
}
}
......
......@@ -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])
......@@ -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()
......
......@@ -35,7 +35,7 @@ class Admin_HolidaysController extends AuthenticatedController
URLHelper::addLinkParam('filter', $this->filter);
}
$this->setSidebar();
$this->setSidebar($action);
}
/**
......@@ -123,13 +123,33 @@ class Admin_HolidaysController extends AuthenticatedController
$this->redirect('admin/holidays');
}
public function holidays_action(): void
{
$this->holidays = Holidays::getHolidays(true, true);
$this->customized = Config::get()->CUSTOMIZED_HOLIDAYS;
}
public function store_holidays_action(): void
{
CSRFProtection::verifyUnsafeRequest();
Config::get()->store(
'CUSTOMIZED_HOLIDAYS',
Request::intArray('holidays')
);
PageLayout::postSuccess(_('Die Änderungen wurden gespeichert.'));
$this->redirect($this->holidaysURL());
}
/**
* Checks a string if it is a valid date and returns the according
* unix timestamp if valid.
*
* @param string $name Parameter name to extract from request
* @param string $time Optional time segment
* @return mixed Unix timestamp or false if not valid
* @return int|false Unix timestamp or false if not valid
*/
private function getTimeStamp($name, $time = '0:00:00')
{
......@@ -146,27 +166,46 @@ class Admin_HolidaysController extends AuthenticatedController
/**
* Adds the content to sidebar
*/
private function setSidebar()
private function setSidebar(string $action): void
{
$sidebar = Sidebar::Get();
$views = new ViewsWidget();
$views->addLink(_('Alle Einträge'),
$this->url_for('admin/holidays', ['filter' => null]))
->setActive(!$this->filter);
$views->addLink(_('Aktuelle/zukünftige Einträge'),
$this->url_for('admin/holidays', ['filter' => 'current']))
->setActive($this->filter === 'current');
$views->addLink(_('Vergangene Einträge'),
$this->url_for('admin/holidays', ['filter' => 'past']))
->setActive($this->filter === 'past');
$sidebar->addWidget($views);
$links = new ActionsWidget();
$links->addLink(_('Neue Ferien anlegen'),
$this->url_for('admin/holidays/edit', ['filter' => null]),
Icon::create('add', 'clickable'))
->asDialog('size=auto');
$sidebar->addWidget($links);
$is_vacation_view = !in_array($action, ['holidays', 'store_holidays']);
$views = $sidebar->addWidget(new ViewsWidget());
$views->addLink(
_('Ferien'),
$this->indexURL()
)->setActive($is_vacation_view);
$views->addLink(
_('Feiertage'),
$this->holidaysURL()
)->setActive(!$is_vacation_view);
if (!$is_vacation_view) {
return;
}
$views = $sidebar->addWidget(new ViewsWidget());
$views->setTitle(_('Ansichtseinstellungen'));
$views->addLink(
_('Alle Einträge'),
$this->indexURL(['filter' => null])
)->setActive(!$this->filter);
$views->addLink(
_('Aktuelle/zukünftige Einträge'),
$this->indexURL(['filter' => 'current'])
)->setActive($this->filter === 'current');
$views->addLink(
_('Vergangene Einträge'),
$this->indexURL(['filter' => 'past'])
)->setActive($this->filter === 'past');
$links = $sidebar->addWidget(new ActionsWidget());
$links->addLink(
_('Neue Ferien anlegen'),
$this->editURL(['filter' => null]),
Icon::create('add')
)->asDialog('size=auto');
}
}
......@@ -32,11 +32,6 @@ class Admin_IliasInterfaceController extends AuthenticatedController
throw new AccessDeniedException();
}
// check SOAP status
if (!Config::get()->SOAP_ENABLE) {
PageLayout::postError(sprintf(_("Das Stud.IP-Modul für die SOAP-Schnittstelle ist nicht aktiviert. Dieses Modul wird für die Nutzung der ILIAS-Schnittstelle benötigt. Ändern Sie den entsprechenden Eintrag in der %sStud.IP-Konfiguration%s."), '<a href="'.$this->url_for('admin/configuration/configuration?needle=SOAP').'">', '</a>'));
}
// check if interface is active
if (!Config::Get()->ILIAS_INTERFACE_ENABLE ) {
throw new AccessDeniedException(_('Ilias-Interface ist nicht aktiviert.'));
......@@ -56,6 +51,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
PageLayout::setHelpKeyword('Basis.Ilias');
$this->modules_available = ConnectedIlias::getSupportedModuleTypes();
$this->studip_roles = ['autor', 'tutor', 'dozent', 'admin', 'root'];
$this->sidebar = Sidebar::get();
}
......@@ -98,18 +95,19 @@ class Admin_IliasInterfaceController extends AuthenticatedController
public function save_interface_settings_action()
{
if (Request::submitted('submit')) {
$this->ilias_interface_config['edit_moduletitle'] = (boolean)Request::get('ilias_interface_edit_moduletitle');
$this->ilias_interface_config['show_offline'] = (boolean)Request::get('ilias_interface_show_offline');
$this->ilias_interface_config['search_active'] = (boolean)Request::get('ilias_interface_search_active');
$this->ilias_interface_config['show_tools_page'] = (boolean)Request::get('ilias_interface_show_tools_page');
$this->ilias_interface_config['add_statusgroups'] = (boolean)Request::get('ilias_interface_add_statusgroups');
$this->ilias_interface_config['cache'] = (boolean)Request::get('ilias_interface_cache');
$this->ilias_interface_config['edit_moduletitle'] = Request::bool('ilias_interface_edit_moduletitle', false);
$this->ilias_interface_config['show_offline'] = Request::bool('ilias_interface_show_offline', false);
$this->ilias_interface_config['search_active'] = Request::bool('ilias_interface_search_active', false);
$this->ilias_interface_config['show_course_paths'] = Request::bool('ilias_interface_show_course_paths', false);
$this->ilias_interface_config['show_tools_page'] = Request::bool('ilias_interface_show_tools_page', false);
$this->ilias_interface_config['add_statusgroups'] = Request::bool('ilias_interface_add_statusgroups', false);
$this->ilias_interface_config['cache'] = Request::bool('ilias_interface_cache', false);
$this->ilias_interface_config['allow_change_course'] = Request::get('ilias_interface_allow_change_course');
$this->ilias_interface_config['allow_add_own_course'] = Request::get('ilias_interface_allow_add_own_course');
//store config entry
Config::get()->store('ILIAS_INTERFACE_BASIC_SETTINGS', $this->ilias_interface_config);
Config::get()->store('ILIAS_INTERFACE_MODULETITLE', Request::quoted('ilias_interface_moduletitle'));
Config::get()->store('ILIAS_INTERFACE_MODULETITLE', Request::get('ilias_interface_moduletitle'));
PageLayout::postSuccess(_('Einstellungen wurden gespeichert.'));
}
$this->redirect($this->url_for('admin/ilias_interface'));
......@@ -124,7 +122,7 @@ class Admin_IliasInterfaceController extends AuthenticatedController
$this->valid_url = false;
$this->ilias_version = '';
$this->ilias_version_date = '';
$this->clients = [];
$this->ilias_clients = [];
if ($index === 'new') {
// default values
$this->ilias_config = [
......@@ -132,8 +130,11 @@ class Admin_IliasInterfaceController extends AuthenticatedController
'name' => '',
'version' => '',
'url' => _('https://<URL zur ILIAS-Installation>'),
'http_connection_timeout' => 1,
'http_request_timeout' => 3,
'client' => '',
'ldap_enable' => '',
'reconnect_accounts' => false,
'no_account_updates' => false,
'admin' => 'ilias_soap_admin',
'admin_pw' => '',
......@@ -155,7 +156,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
'author_role_name' => 'Author',
'author_role' => '',
'author_perm' => 'tutor'
'author_perm' => 'tutor',
'additional_roles' => []
];
// fetch existing indicies from previously connected ILIAS installations
......@@ -168,9 +170,11 @@ class Admin_IliasInterfaceController extends AuthenticatedController
// get ILIAS server info
if (Request::get('ilias_url')) {
$info = ConnectedIlias::getIliasInfo(Request::get('ilias_url'));
if (count($info)) {
if (is_array($info) && count($info)) {
$this->valid_url = true;
$this->ilias_config['url'] = Request::get('ilias_url');
$this->ilias_config['http_connection_timeout'] = (int) Request::get('ilias_http_connection_timeout');
$this->ilias_config['http_request_timeout'] = (int) Request::get('ilias_http_request_timeout');
if ($info['version']) {
$this->ilias_version = $info['version'];
$this->ilias_version_date = $info['version_date'];
......@@ -220,6 +224,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
if (Request::get('ilias_name')) {
$this->ilias_config['name'] = Request::get('ilias_name');
$this->ilias_config['url'] = Request::get('ilias_url');
$this->ilias_config['http_connection_timeout'] = (int) Request::get('ilias_http_connection_timeout');
$this->ilias_config['http_request_timeout'] = (int) Request::get('ilias_http_request_timeout');
}
$info = ConnectedIlias::getIliasInfo($this->ilias_config['url']);
if (count($info)) {
......@@ -241,8 +247,18 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/
public function edit_content_action($index)
{
$this->ilias_config = $this->ilias_configs[$index];
$this->ilias_index = $index;
$this->ilias_datafields = [];
$connected_ilias = new ConnectedIlias($index);
$this->ilias_config = $connected_ilias->ilias_config;
if ($admin_id = $connected_ilias->soap_client->lookupUser($this->ilias_config['admin'])) {
$user = $connected_ilias->soap_client->getUser($admin_id);
if (!empty($user) && array_key_exists('udfs', $user)) {
$this->ilias_datafields = $user['udfs'];
}
}
}
/**
......@@ -251,8 +267,11 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/
public function edit_permissions_action($index)
{
$this->ilias_config = $this->ilias_configs[$index];
$this->ilias_index = $index;
$connected_ilias = new ConnectedIlias($index);
$this->ilias_config = $connected_ilias->ilias_config;
$this->global_roles = $connected_ilias->soap_client->getRoles('global', -1);
}
/**
......@@ -278,7 +297,7 @@ class Admin_IliasInterfaceController extends AuthenticatedController
{
CSRFProtection::verifyUnsafeRequest();
if (Request::submitted('submit')) {
if (Request::submittedSome('submit', 'add_additional_role', 'remove_additional_role')) {
// set basic server settings
if (Request::getInstance()->offsetExists('ilias_name')) {
$this->ilias_configs[$index]['name'] = Request::get('ilias_name');
......@@ -286,6 +305,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
$this->ilias_configs[$index]['version'] = Request::get('ilias_version');
}
$this->ilias_configs[$index]['url'] = Request::get('ilias_url');
$this->ilias_configs[$index]['http_connection_timeout'] = (int) Request::get('ilias_http_connection_timeout');
$this->ilias_configs[$index]['http_request_timeout'] = (int) Request::get('ilias_http_request_timeout');
if (Request::getInstance()->offsetExists('ilias_client')) {
$this->ilias_configs[$index]['client'] = Request::get('ilias_client');
}
......@@ -315,6 +336,28 @@ class Admin_IliasInterfaceController extends AuthenticatedController
if (Request::getInstance()->offsetExists('ilias_matriculation')) {
$this->ilias_configs[$index]['matriculation'] = Request::get('ilias_matriculation');
}
if (Request::getInstance()->offsetExists('ilias_discipline_1') && Request::getInstance()->offsetExists('ilias_discipline_2')) {
if ($admin_id = $connected_ilias->soap_client->lookupUser($this->ilias_configs[$index]['admin'])) {
$user = $connected_ilias->soap_client->getUser($admin_id);
if (array_key_exists('udfs', $user)) {
$this->ilias_datafields = $user['udfs'];
foreach ($this->ilias_datafields as $field) {
if (Request::option('ilias_discipline_1') == $field['id']) {
$this->ilias_configs[$index]['discipline_1'] = ['id' => $field['id'], 'name' => $field['name']];
}
if (Request::option('ilias_discipline_2') == $field['id']) {
$this->ilias_configs[$index]['discipline_2'] = ['id' => $field['id'], 'name' => $field['name']];
}
}
if (!Request::option('ilias_discipline_1')) {
unset($this->ilias_configs[$index]['discipline_1']);
}
if (!Request::option('ilias_discipline_2')) {
unset($this->ilias_configs[$index]['discipline_2']);
}
}
}
}
if (Request::getInstance()->offsetExists('ilias_cat_semester')) {
$this->ilias_configs[$index]['cat_semester'] = Request::get('ilias_cat_semester');
}
......@@ -325,6 +368,7 @@ class Admin_IliasInterfaceController extends AuthenticatedController
$this->ilias_configs[$index]['course_veranstaltungsnummer'] = Request::get('ilias_course_veranstaltungsnummer');
}
$this->ilias_configs[$index]['delete_ilias_users'] = Request::get('ilias_delete_ilias_users');
$this->ilias_configs[$index]['reconnect_accounts'] = Request::bool('ilias_reconnect_accounts', false);
$this->ilias_configs[$index]['delete_ilias_courses'] = Request::get('ilias_delete_ilias_courses');
$this->ilias_configs[$index]['category_create_on_add_module'] = Request::get('ilias_category_create_on_add_module');
$this->ilias_configs[$index]['category_to_desktop'] = Request::get('ilias_category_to_desktop');
......@@ -345,10 +389,59 @@ class Admin_IliasInterfaceController extends AuthenticatedController
// set permissions settings
if (Request::getInstance()->offsetExists('ilias_author_role_name')) {
$this->global_roles = $connected_ilias->soap_client->getRoles('global', -1);
$this->ilias_configs[$index]['author_role_name'] = Request::get('ilias_author_role_name');
$this->ilias_configs[$index]['author_perm'] = Request::get('ilias_author_perm');
$this->ilias_configs[$index]['allow_change_account'] = Request::get('ilias_allow_change_account');
// remove ilias role assignment
if (
Request::submitted('remove_additional_role')
&& Request::option('studip_role')
&& array_key_exists('additional_roles', $this->ilias_configs[$index])
) {
$studip_role = Request::option('studip_role');
$ilias_role = Request::option('remove_additional_role');
if (
in_array($studip_role, $this->studip_roles)
&& array_key_exists($studip_role, $this->ilias_configs[$index]['additional_roles'])
&& array_key_exists($ilias_role, $this->ilias_configs[$index]['additional_roles'][$studip_role])
) {
unset($this->ilias_configs[$index]['additional_roles'][$studip_role][$ilias_role]);
PageLayout::postSuccess(sprintf(_('ILIAS-Rollenzuweisung der Stud.IP-Rechtestufe %s wurde entfernt.'), $studip_role));
}
}
// add ilias role assignment
if (
Request::submitted('add_additional_role')
&& Request::option('add_studip_role')
&& Request::option('add_ilias_role')
) {
$studip_role = Request::option('add_studip_role');
$ilias_role = Request::option('add_ilias_role');
$role_already_assigned = false;
if (!array_key_exists('additional_roles', $this->ilias_configs[$index])) {
$this->ilias_configs[$index]['additional_roles'] = [];
}
if (
in_array($studip_role, $this->studip_roles)
&& (array_key_exists($ilias_role, $this->global_roles))
) {
if (!array_key_exists($studip_role, $this->ilias_configs[$index]['additional_roles'])) {
$this->ilias_configs[$index]['additional_roles'][$studip_role] = [];
}
if (array_key_exists($ilias_role, $this->global_roles)) {
$this->ilias_configs[$index]['additional_roles'][$studip_role][$ilias_role] = [
'id' => $this->global_roles[$ilias_role]['id'],
'name' => $this->global_roles[$ilias_role]['name']];
PageLayout::postSuccess(sprintf(_('ILIAS-Rolle %s wird Stud.IP-Rechtestufe %s zugewiesen.'), $this->global_roles[$ilias_role]['name'], $studip_role));
} else {
PageLayout::postError(_('ILIAS-Rolle nicht gefunden.'));
}
}
}
//store config entry
Config::get()->store('ILIAS_INTERFACE_SETTINGS', $this->ilias_configs);
PageLayout::postSuccess(_('ILIAS-Berechtigungseinstellungen wurden gespeichert.'));
......@@ -412,8 +505,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/
public function soap_methods_action($index)
{
if ($this->ilias_configs[$index]['is_active']) {
$ilias = new ConnectedIlias($index);
$this->soap_methods = $ilias->getSoapMethods();
ksort($this->soap_methods);
$this->ilias_index = $index;
......@@ -423,17 +516,20 @@ class Admin_IliasInterfaceController extends AuthenticatedController
switch ($param) {
case "sid" : $this->params[$param] = $ilias->soap_client->getSID();
break;
case "user_id" : $this->params[$param] = $ilias->user->getId();
case "user_id" : $this->params[$param] = is_object($ilias->user) ? $ilias->user->getId() : '';
break;
}
}
} elseif (Request::get('ilias_call')) {
$params = [];
foreach ($this->soap_methods[Request::get('ilias_call')] as $param) {
if ($param === 'user_ids') {
$params[$param] = [Request::get('ilias_soap_param_'.$param)];
} else {
$params[$param] = Request::get('ilias_soap_param_'.$param);
}
$this->result = $ilias->soap_client->call(Request::get('ilias_call'), $params);
}
$this->result = $ilias->soap_client->call(Request::get('ilias_call'), $params);
}
}
}
<?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;
......
......@@ -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()) {
......
......@@ -64,7 +64,8 @@ class Admin_LoginStyleController extends AuthenticatedController
*/
public function add_pic_action()
{
CSRFProtection::verifyRequest();
CSRFProtection::verifyUnsafeRequest();
$success = 0;
foreach ($_FILES['pictures']['name'] as $index => $filename) {
if ($_FILES['pictures']['error'][$index] !== UPLOAD_ERR_OK) {
......@@ -174,11 +175,11 @@ class Admin_LoginStyleController extends AuthenticatedController
public function store_faq_action(LoginFaq $entry = null)
{
CSRFProtection::verifyRequest();
CSRFProtection::verifyUnsafeRequest();
$entry->setData([
'title' => trim(Request::get('title')),
'description' => trim(Request::get('description')),
'title' => Request::i18n('title'),
'description' => Studip\Markup::purifyHtml(Request::i18n('description')),
]);
if ($entry->store()) {
......@@ -190,7 +191,7 @@ class Admin_LoginStyleController extends AuthenticatedController
public function delete_faq_action(LoginFaq $entry)
{
CSRFProtection::verifyRequest();
CSRFProtection::verifyUnsafeRequest();
if ($entry->delete()) {
PageLayout::postSuccess(_('Der Hinweistext wurde gelöscht.'));
......
......@@ -23,16 +23,21 @@ class Admin_LtiController extends AuthenticatedController
$GLOBALS['perm']->check('root');
Navigation::activateItem('/admin/config/lti');
PageLayout::setTitle(_('Konfiguration der LTI-Tools'));
PageLayout::setTitle(_('LTI-Tools'));
$widget = Sidebar::get()->addWidget(new ActionsWidget());
$widget->addLink(
_('Neues LTI-Tool registrieren'),
$this->url_for('admin/lti/edit'),
$this->url_for('lti/tool/add/global'),
Icon::create('add')
)->asDialog();
$widget->addLink(
_('Daten zur LTI-Plattform anzeigen'),
$this->url_for('lti/auth/platform_data'),
Icon::create('info')
)->asDialog();
Helpbar::get()->addPlainText('', _('Hier können Sie Verknüpfungen mit externen Tools konfigurieren, sofern diese den LTI-Standard (Version 1.x) unterstützen.'));
Helpbar::get()->addPlainText('', _('Hier können Sie LTI-Tools konfigurieren. Diese müssen den LTI-Standard in Version 1.0/1.1 oder 1.3A unterstützen.'));
}
/**
......@@ -42,66 +47,4 @@ class Admin_LtiController extends AuthenticatedController
{
$this->tools = LtiTool::findAll();
}
/**
* Display dialog for editing an LTI tool.
*
* @param int $id tool id
*/
public function edit_action($id = null)
{
$this->tool = new LtiTool($id);
}
/**
* Save changes for an LTI tool.
*
* @param int $id tool id
*/
public function save_action($id)
{
CSRFProtection::verifyUnsafeRequest();
$tool = new LtiTool($id ?: null);
$tool->name = trim(Request::get('name'));
$tool->launch_url = trim(Request::get('launch_url'));
$tool->consumer_key = trim(Request::get('consumer_key'));
$tool->consumer_secret = trim(Request::get('consumer_secret'));
$tool->custom_parameters = trim(Request::get('custom_parameters'));
$tool->allow_custom_url = Request::int('allow_custom_url', 0);
$tool->deep_linking = Request::int('deep_linking', 0);
$tool->send_lis_person = Request::int('send_lis_person', 0);
$tool->oauth_signature_method = Request::get('oauth_signature_method', 'sha1');
if ($tool->store()) {
PageLayout::postSuccess(sprintf(
_('Einstellungen für "%s" wurden gespeichert.'),
htmlReady($tool->name)
));
}
$this->redirect('admin/lti');
}
/**
* Delete an LTI tool.
*
* @param int $id tool id
*/
public function delete_action($id)
{
CSRFProtection::verifyUnsafeRequest();
$tool = LtiTool::find($id);
$tool_name = $tool->name;
if ($tool && $tool->delete()) {
PageLayout::postSuccess(sprintf(
_('Das LTI-Tool "%s" wurde gelöscht.'),
htmlReady($tool_name)
));
}
$this->redirect('admin/lti');
}
}
......@@ -17,6 +17,8 @@
class Admin_OverlappingController extends AuthenticatedController
{
private ?string $view = null;
/**
* Common before filter for all actions.
*
......@@ -25,10 +27,13 @@ class Admin_OverlappingController extends AuthenticatedController
*/
public function before_filter(&$action, &$args)
{
if (!$GLOBALS['perm']->have_perm('admin')) {
throw new AccessDeniedException();
}
parent::before_filter($action, $args);
Navigation::activateItem('/browse/my_courses/overlapping');
URLHelper::bindLinkParam('view', $this->view);
if (Request::option('sem_select')) {
$GLOBALS['user']->cfg->store('MY_COURSE_SELECTED_CYCLE', Request::option('sem_select'));
}
......@@ -41,21 +46,27 @@ class Admin_OverlappingController extends AuthenticatedController
/**
* Main view: Shows selection form and result.
*
* @return void
*/
public function index_action()
{
$this->view = 'index';
$this->setSidebar();
$selection_id = Request::option('selection', $_SESSION['MVV_OVL_SELECTION_ID'] ?? null);
$selections = SimpleORMapCollection::createFromArray(
MvvOverlappingSelection::findBySQL('`selection_id` = ? AND `user_id` = ?', [
Request::option('selection'),
$selection_id,
$GLOBALS['user']->id
])
);
$this->selection_id = null;
$_SESSION['MVV_OVL_SELECTION_ID'] = $selection_id;
$this->selection_id = '';
if (count($selections)) {
$this->base_version = StgteilVersion::find($selections->first()->base_version_id);
$this->fachsems = (array) explode(',', $selections->first()->fachsems);
$this->semtypes = (array) explode(',', $selections->first()->semtypes);
$this->fachsems = $selections->first()->fachsems === '' ? [] : explode(',', $selections->first()->fachsems);
$this->semtypes = $selections->first()->semtypes === '' ? [] : explode(',', $selections->first()->semtypes);
$this->comp_versions = StgteilVersion::findMany($selections->pluck('comp_version_id'));
$this->selection_id = $selections->first()->selection_id;
if (Request::int('show_hidden') !== null) {
......@@ -67,29 +78,108 @@ class Admin_OverlappingController extends AuthenticatedController
$this->fachsems = Request::intArray('fachsems');
$this->semtypes = Request::intArray('semtypes');
}
$this->base_version_id = $this->base_version->id ?? '';
$this->comp_versions_ids = SimpleCollection::createFromArray($this->comp_versions)->pluck('id');
$this->stgteil_versions = $this->getStgteilVersions();
$this->conflicts = MvvOverlappingSelection::getConflictsBySelection(
$this->selection_id,
empty($_SESSION['MVV_OVL_HIDDEN'])
);
$version_options = [];
foreach ($this->getStgteilVersions() as $base_version) {
$version_options[$base_version->id] = $base_version->getDisplayName();
}
$this->form = \Studip\Forms\Form::create();
$this->fieldset = new \Studip\Forms\Fieldset(_('Auswahl'));
$this->fieldset->addInput(
new \Studip\Forms\SelectInput(
'base_version',
_('Studiengangteil'),
$this->base_version_id,
[
'options' => $version_options
]
)
)->setRequired();
$this->fieldset->addInput(
new \Studip\Forms\MultiselectInput(
'comp_versions',
_('Vergleichs-Studiengangteile'),
$this->comp_versions_ids,
[
'options' => $version_options
]
)
);
$fsem_options = [];
for ($fsem = 1; $fsem < 7; $fsem++) {
$fsem_options[$fsem] = sprintf(_('%s Fachsemester'),
$fsem . ModuleManagementModel::getLocaleOrdinalNumberSuffix($fsem));
}
$this->fieldset->addInput(
new \Studip\Forms\MultiselectInput(
'fachsems',
_('Fachsemester'),
$this->fachsems,
[
'options' => $fsem_options
]
)
);
$sem_class_options = [];
foreach ($GLOBALS['SEM_CLASS'] as $class_id => $class) {
if ($class['studygroup_mode']) continue;
foreach ($class->getSemTypes() as $id => $type) {
$sem_class_options[$id] = sprintf('%s (%s)', $type['name'], $class['name']);
}
}
$this->fieldset->addInput(
new \Studip\Forms\MultiselectInput(
'semtypes',
_('Veranstaltungstypen'),
$this->semtypes,
[
'options' => $sem_class_options
]
)
);
$this->fieldset->addInput(
new \Studip\Forms\CheckboxInput(
'show_hidden',
_('Ausgeblendete Veranstaltungen anzeigen'),
$_SESSION['MVV_OVL_HIDDEN'] ?? '0'
)
);
$this->form->addPart($this->fieldset);
$this->form->setURL($this->check())
->setCollapsable(true)
->setDataSecure(false)
->setSaveButtonText(_('Vergleichen'))
->setCancelButtonText(_('Zurücksetzen'));
}
/**
* Resets form and shows index view.
*
* @return void
*/
public function reset_action()
{
$this->setSidebar();
$this->setSidebar('index');
$_SESSION['MVV_OVL_HIDDEN'] = 0;
$_SESSION['MVV_OVL_SELECTION_ID'] = '';
$this->conflicts = [];
$this->render_action('index');
$this->redirect('admin/overlapping/index');
}
/**
* Calculates the conflicts and redirects to index view.
*
* @return void
*/
public function check_action()
{
$this->setSidebar();
$this->base_version = StgteilVersion::find(Request::option('base_version'));
if ($this->base_version) {
$this->comp_versions = [];
......@@ -103,12 +193,12 @@ class Admin_OverlappingController extends AuthenticatedController
$this->fachsems = Request::intArray('fachsems');
$this->semtypes = Request::intArray('semtypes');
if (Request::submitted('compare')) {
$selection_id = MvvOverlappingSelection::createSelectionId(
$this->base_version,
$this->comp_versions,
$this->fachsems,
$this->semtypes
$this->semtypes,
$this->selected_semester->id
);
// refresh conflicts
......@@ -127,7 +217,7 @@ class Admin_OverlappingController extends AuthenticatedController
$selection[$comp_version->id]->base_version_id = $this->base_version->id;
$selection[$comp_version->id]->comp_version_id = $comp_version->id;
$selection[$comp_version->id]->setFachsemester($this->fachsems);
$selection[$comp_version->id]->setCoursetypes($this->semtypes);
$selection[$comp_version->id]->setCourseTypes($this->semtypes);
$selection[$comp_version->id]->user_id = $GLOBALS['user']->id;
$selection[$comp_version->id]->store();
}
......@@ -136,7 +226,7 @@ class Admin_OverlappingController extends AuthenticatedController
$conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id);
$visible_conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id, true);
if (count($conflicts)) {
if (count($conflicts) != count($visible_conflicts)) {
if (count($conflicts) !== count($visible_conflicts)) {
PageLayout::postSuccess(
sprintf(
ngettext('1 Konflikt gefunden (1 ausgeblendet)',
......@@ -157,7 +247,6 @@ class Admin_OverlappingController extends AuthenticatedController
} else {
PageLayout::postSuccess(_('Keine Konflikte gefunden.'));
}
}
} else {
PageLayout::postError('Die Basis-Version muss angegeben werden!');
}
......@@ -168,204 +257,386 @@ class Admin_OverlappingController extends AuthenticatedController
/**
* Shows the responsible admin of the course.
*
* @param type $course_id The id of the course.
* @param string $conflict_id The id of the conflict.
* @return void
*/
public function admin_info_action($course_id)
public function admin_info_action(string $conflict_id)
{
$this->course = Course::find($course_id);
if ($this->course) {
$this->conflict = MvvOverlappingConflict::find($conflict_id);
$this->version = $this->conflict->comp_abschnitt->version;
$this->course = $this->conflict->comp_course;
if ($this->course && $this->version) {
$this->admins = InstituteMember::findByInstituteAndStatus($this->course->institut_id, 'admin');
} else {
PageLayout::postMessage(MessageBox::error(_('Unbekannte Veranstaltung.')));
}
$this->selected_view = 'admin_info';
}
/**
* Shows the course details.
*
* @param type $course_id The id of the course.
* @param string $conflict_id The id of the conflict.
* @return void
*/
public function course_info_action($course_id)
public function course_info_action(string $conflict_id)
{
$course = Course::find($course_id);
if ($course) {
Request::set('sem_id', $course->id);
$this->redirect('course/details' . '?sem_id=' . $course->id);
$this->conflict = MvvOverlappingConflict::find($conflict_id);
$this->course = $this->conflict->comp_course;
$this->version = $this->conflict->comp_abschnitt->version;
if ($this->course && $this->version) {
$response = $this->relay('course/details');
$this->content = $response->body;
} else {
PageLayout::postMessage(MessageBox::error(_('Unbekannte Veranstaltung.')));
PageLayout::postMessage(MessageBox::error(_('Unbekannte Veranstaltung oder Version.')));
}
$this->selected_view = 'course_info';
$this->render_template('admin/overlapping/info_dialog');
}
/**
* Sets a course as hidden.
*
* @param int $conflict_id The id of the conflict.
* @return void
*/
public function set_exclude_action()
public function exclude_action(int $conflict_id)
{
$selection = MvvOverlappingSelection::find(Request::int('selection_id'));
if ($selection->user_id == $GLOBALS['user']->id) {
$exclude = new MvvOverlappingExclude([$selection->selection_id, Request::option('course_id')]);
if (Request::int('excluded')) {
$success = $exclude->delete();
} else {
$conflict = MvvOverlappingConflict::find($conflict_id);
if ($conflict->selection->user_id === $GLOBALS['user']->id) {
$exclude = new MvvOverlappingExclude(
[
$conflict->selection->selection_id,
$conflict->comp_course_id
]
);
if ($exclude->isNew()) {
$success = $exclude->store();
} else {
$success = $exclude->delete();
}
$this->set_status($success ? 204 : 400);
} else {
$this->set_status(403);
}
$this->render_nothing();
$this->relocate('admin/overlapping/' . $this->view);
}
/**
* Shows detailed information about the studiengangteil version.
* Shows information of the study course version.
*
* @param type $version_id The id of the studiengangteil version.
* @param string $conflict_id The id of the conflict.
* @return void
* @throws \Flexi\TemplateNotFoundException
* @throws \Trails\Exceptions\DoubleRenderError
*/
public function version_info_action($version_id)
public function version_info_action(string $conflict_id)
{
$version = StgteilVersion::find($version_id);
if ($version) {
Request::set('version', $version->id);
$this->redirect($this->url_for('search/studiengaenge/verlauf/' . $version->stgteil_id,
['semester' => $this->selected_semester,
'version' => $version->id]));
return;
$this->conflict = MvvOverlappingConflict::find($conflict_id);
if (empty($this->conflict)) {
throw new InvalidArgumentException();
}
$this->version = $this->conflict->comp_abschnitt->version;
$this->course = $this->conflict->comp_course;
if ($this->version && $this->course) {
$response = $this->relay('search/studiengaenge/verlauf/' . $this->version->stgteil_id
. "/?semester={$this->selected_semester->id}&version={$this->version->id}");
$this->content = $response->body;
} else {
PageLayout::postMessage(MessageBox::error(_('Unbekannte Studiengangteil-Version.')));
PageLayout::postError(_('Unbekannte Studiengangteil-Version.'));
}
$this->selected_view = 'info';
$this->render_template('admin/overlapping/info_dialog');
}
/**
* Init the sidebar.
* Shows the planer view of conflicts.
*
* @param string $selection_id The id of the selection.
* @return void
*/
private function setSidebar()
public function planer_action(string $selection_id = '')
{
$sidebar = Sidebar::Get();
$this->view = 'planer';
$this->setSidebar();
$widget = new SelectWidget(
_('Semesterauswahl'),
$this->url_for('admin/overlapping/index'),
'sem_select'
$selection_id = $selection_id ?: $_SESSION['MVV_OVL_SELECTION_ID'] ?? null;
$this->fullcalendar = Studip\Fullcalendar::create(
_('Kalender'),
[
'editable' => false,
'selectable' => false,
'studip_urls' => '',
'dialog_size' => 'auto',
'minTime' => sprintf('%02u:00', 8),
'maxTime' => sprintf('%02u:00', 21),
'defaultDate' => date('Y-m-d', $this->selected_semester->vorles_beginn),
'allDaySlot' => false,
'allDayText' => '',
'header' => [
'left' => false,
'center' => $this->selected_semester->name,
'right' => false,
],
'weekNumbers' => false,
'views' => [
'timeGridWeek' => [
'columnHeaderFormat' => ['weekday' => 'short', 'omitCommas' => true],
'weekends' => true,
'slotDuration' => '00:30:00'
],
],
'defaultView' => 'timeGridWeek',
'timeGridEventMinHeight' => 20,
'eventSources' => [
[
'url' => $this->conflictsURL($selection_id),
'method' => 'GET',
'extraParams' => []
]
],
'nowIndicator' => false
],
['class' => 'resource-plan semester-plan']
);
foreach (array_reverse(Semester::getAll()) as $semester) {
$widget->addElement(new SelectElement(
$semester->id,
$semester->name,
$semester->id === $this->selected_semester->id
), 'sem_select-' . $semester->id
// get selected StgteilVersions colors
$this->selections = MvvOverlappingSelection::findBySQL(
'`selection_id` = ? ORDER BY `comp_version_id`',
[$selection_id]
);
}
$sidebar->addWidget($widget);
}
/**
* Search for base version by given search term.
* Retrieves all conflicts for the given selection.
*
* @param $selection_id The id of the selection.
* @return void
*/
public function base_version_action()
public function conflicts_action($selection_id)
{
$sword = Request::get('term');
$this->render_text(json_encode($this->getResult($sword)));
$selections = MvvOverlappingSelection::findBySQL(
'`selection_id` = ? ORDER BY `comp_version_id`',
[$selection_id]
);
$conflicting_metadates = [];
foreach ($selections as $selection) {
foreach ($selection->conflicts as $conflict) {
$event_data = $this->createEventFromConflict($conflict, true);
$base_index = $conflict->base_course->id . $event_data->begin->getTimestamp();
$conflicting_metadates[$base_index] = $event_data->toFullcalendarEvent();
$event_data = $this->createEventFromConflict($conflict);
$comp_index = $conflict->comp_course->id . $event_data->begin->getTimestamp();
$conflicting_metadates[$comp_index] = $event_data->toFullcalendarEvent();
}
}
$this->render_json(array_values($conflicting_metadates));
}
/**
* Search für comparison version by given search term.
* Shows a serialized view of the conflict.
*
* @param string $conflict_id The id of the conflict.
* @return void
*/
public function comp_versions_action()
public function course_conflict_action(string $conflict_id)
{
$sword = Request::get('term');
$version_id = Config::get()->MVV_OVERLAPPING_SHOW_VERSIONS_INSIDE_MULTIPLE_STUDY_COURSES
? Request::option('version_id')
: null;
$version_ids = $this->getRelatedVersions($version_id);
$this->render_text(json_encode($this->getResult($sword, $version_ids)));
$this->conflict = MvvOverlappingConflict::find($conflict_id);
if (empty($this->conflict)) {
throw new InvalidArgumentException();
}
$this->conflicts = SimpleORMapCollection::createFromArray([$this->conflict]);
$this->base_version = $this->conflict->base_abschnitt->version;
$this->version = $this->conflict->comp_abschnitt->version;
$this->course = $this->conflict->comp_course;
$this->selected_view = 'conflict';
}
/**
* Returns versions related to the base version.
* Shows the conflict in a dialog.
*
* @param type $version_id
* @return type
* @param string $conflict_id The id of the conflict.
* @return void
* @throws \Flexi\TemplateNotFoundException
* @throws \Trails\Exceptions\DoubleRenderError
*/
private function getRelatedVersions($version_id)
public function conflict_action(string $conflict_id)
{
$version_ids = [];
$version = StgteilVersion::find($version_id);
if ($version) {
$studiengaenge = Studiengang::findByStgTeil($version->stgteil_id);
} else {
return null;
}
foreach ($studiengaenge as $studiengang) {
if ($studiengang->typ == 'mehrfach') {
foreach ($studiengang->studiengangteile as $studiengangteil) {
$version_ids = array_merge(
$version_ids,
$studiengangteil->versionen->pluck('version_id')
$this->conflict = MvvOverlappingConflict::find($conflict_id);
if (empty($this->conflict)) {
throw new InvalidArgumentException();
}
$this->version = $this->conflict->comp_abschnitt->version;
$this->course = $this->conflict->comp_course;
PageLayout::setTitle($this->course->getFullName(
Config::get()->IMPORTANT_SEMNUMBER
? 'number-type-name'
: 'type-name'
)
);
$this->content = '';
if (empty($this->conflict->comp_course)) {
PageLayout::postError(_('Unbekannte Veranstaltung.'));
} else {
Request::set('sem_id', $this->conflict->comp_course_id);
$this->course = $this->conflict->comp_course;
$this->version = $this->conflict->comp_abschnitt->version;
$response = $this->relayWithRedirect('course/details/index');
$this->content = $response->body;
}
}
}
return count($version_ids) ? array_diff($version_ids, [$version_id]) : null;
$this->selected_view = 'conflict';
$this->render_template('admin/overlapping/info_dialog');
}
/**
* Search for studiengangteil versionen by given keyword. The result can be
* filtered by version ids.
* Creates EventData from conflicts.
*
* @param string $keyword The keyword to search for.
* @param array $version_ids An array of version ids.
* @return array An array of studiengangteil versionen.
* @param MvvOverlappingConflict $conflict The conflict object.
* @return \Studip\Calendar\EventData The event data.
*/
private function getResult($keyword, $version_ids = null) {
$version_query = '';
private function createEventFromConflict(MvvOverlappingConflict $conflict, $base = false): \Studip\Calendar\EventData
{
static $color_mapping = [];
if (!is_null($version_ids)) {
$version_query = ' AND `mvv_stgteilversion`.`version_id` IN (:version_ids) ';
$weekday_mapping =[
1 => 'mon',
2 => 'tue',
3 => 'wed',
4 => 'thu',
5 => 'fri',
6 => 'sat',
7 => 'sun',
];
if ($base) {
$version = $conflict->selection->comp_version;
$col_version_id = $conflict->selection->base_version->id;
$cycle = $conflict->base_cycle;
$course = $conflict->base_course;
} else {
$version = $conflict->selection->base_version;
$col_version_id = $conflict->selection->comp_version->id;
$cycle = $conflict->comp_cycle;
$course = $conflict->comp_course;
}
if (empty($color_mapping[$col_version_id])) {
$color_mapping[$col_version_id] = count($color_mapping) + 1;
}
$color_pos = $color_mapping[$col_version_id];
$text_color = Config::get()->PERS_TERMIN_KAT[$color_pos]['fgcolor'];
$background_color = Config::get()->PERS_TERMIN_KAT[$color_pos]['bgcolor'];
$border_color = Config::get()->PERS_TERMIN_KAT[$color_pos]['border_color'];
$begin = new DateTime();
$begin->setTimestamp($this->selected_semester->vorles_beginn);
$begin->modify(
$weekday_mapping[$cycle->weekday]
. ' this week '
. $cycle->start_time
);
$end = clone $begin;
$end->modify('today ' . $cycle->end_time);
return new \Studip\Calendar\EventData(
$begin,
$end,
Config::get()->IMPORTANT_SEMNUMBER
? $course->getFullName('number-type-name')
: $course->getFullName('type-name'),
['user-date', 'user-date-category1'],
$text_color ?? '#ffffff',
$background_color ?? '#000000',
false,
'MvvOverlappingConflict',
$conflict->id,
'MvvOverlappingSelection',
$conflict->selection->id,
'user',
$conflict->selection->user_id,
[
'show' => $this->course_conflictURL($conflict->id)
],
[],
'',
$border_color ?? '#ffffff'
);
}
$query = "SELECT `version_id`, `fach`.`name`, `mvv_stgteil`.`kp`
FROM `fach`
INNER JOIN `mvv_stgteil` USING(`fach_id`)
INNER JOIN `mvv_stgteilversion` USING(`stgteil_id`)
INNER JOIN `semester_data` AS `start_sem`
ON (`mvv_stgteilversion`.`start_sem` = `start_sem`.`semester_id`)
LEFT JOIN `semester_data` AS `end_sem`
ON (`mvv_stgteilversion`.`end_sem` = `end_sem`.`semester_id`)
WHERE (`fach`.`name` LIKE :keyword
OR `mvv_stgteil`.`zusatz` LIKE :keyword
OR `mvv_stgteilversion`.`code` LIKE :keyword)
/**
* Init the sidebar content.
*
* @return void
*/
private function setSidebar()
{
$sidebar = Sidebar::Get();
$views = new ViewsWidget();
$views->addLink(
_('Listenansicht'),
$this->indexURL()
)->setActive($this->view === 'index');
$views->addLink(
_('Planeransicht'),
$this->planerURL()
)->setActive($this->view === 'planer');
$sidebar->addWidget($views);
$semester_selector = new SelectWidget(
_('Semesterauswahl'),
$this->url_for('admin/overlapping/reset'),
'sem_select'
);
foreach (array_reverse(Semester::getAll()) as $semester) {
$semester_selector->addElement(new SelectElement(
$semester->id,
$semester->name,
$semester->id === $this->selected_semester->id
), 'sem_select-' . $semester->id
);
}
$sidebar->addWidget($semester_selector);
}
AND (`start_sem`.`beginn` <= :sem_end
OR ISNULL(`start_sem`.`beginn`))
AND (`end_sem`.`ende` >= :sem_start
OR ISNULL(`end_sem`.`ende`))
" . $version_query . "
ORDER BY `name` ASC, `kp` ASC";
/**
* Search for base version by given search term.
*/
public function base_version_action()
{
$sword = Request::get('term');
$this->render_text(json_encode($this->getResult($sword)));
}
$stat = array_keys(array_filter(
/**
* Get Studiengangteilversionen for selection, filtered by start and end semester and status (only public).
*
* @return StgteilVersion[]
*/
private function getStgteilVersions(): array
{
// get public status from config
$public_status = array_keys(array_filter(
$GLOBALS['MVV_STGTEILVERSION']['STATUS']['values'],
function ($v) {
return $v['public'];
}
));
$stmt = DBManager::get()->prepare($query);
$stmt->execute([
':keyword' => '%' . $keyword . '%',
':stat' => $stat,
return StgteilVersion::findBySQL(
"JOIN `mvv_stgteil` USING(`stgteil_id`)
JOIN `fach` USING(`fach_id`)
JOIN `semester_data` AS `start_sem`
ON `mvv_stgteilversion`.`start_sem` = `start_sem`.`semester_id`
LEFT JOIN `semester_data` AS `end_sem`
ON `mvv_stgteilversion`.`end_sem` = `end_sem`.`semester_id`
WHERE (`start_sem`.`beginn` <= :sem_end)
AND (`end_sem`.`ende` >= :sem_start OR ISNULL(`end_sem`.`ende`))
AND `mvv_stgteilversion`.`stat` IN (:status)
ORDER BY `fach`.`name`, `mvv_stgteil`.`kp`",
[
':sem_start' => $this->selected_semester->beginn,
':sem_end' => $this->selected_semester->ende,
':version_ids' => $version_ids
]);
$res = ['results' => []];
foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $version_id) {
$version = StgteilVersion::find($version_id);
$res['results'][] = [
'id' => $version->id,
'text' => $version->getDisplayName()
];
}
return $res;
':status' => $public_status
]
);
}
}
......@@ -16,7 +16,7 @@
class Admin_PluginController extends AuthenticatedController
{
private $plugin_admin;
private PluginAdministration $plugin_admin;
/**
* Common tasks for all actions.
......@@ -43,7 +43,7 @@ class Admin_PluginController extends AuthenticatedController
$settings = $current = $GLOBALS['user']->cfg->PLUGINADMIN_DISPLAY_SETTINGS;
foreach ((array)$settings as $key => $value) {
$settings[$key] = Request::option($key, $settings[$key]) ?: null;
$settings[$key] = Request::get($key, $settings[$key]) ?: null;
}
if ($settings !== $current) {
......@@ -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,9 +439,8 @@ class Admin_PluginController extends AuthenticatedController
$update_info = $this->plugin_admin->getUpdateInfo($plugins);
$update = $this->flash['update'];
$update_status = [];
// update each plugin in turn
if (!empty($update)) { // update each plugin in turn
foreach ($update as $id) {
if (isset($update_info[$id]['update'])) {
try {
......@@ -454,6 +451,7 @@ class Admin_PluginController extends AuthenticatedController
}
}
}
}
// collect and report errors
if (isset($update_errors)) {
......@@ -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)
{
......@@ -509,19 +509,20 @@ class Admin_PluginController extends AuthenticatedController
public function unregistered_action()
{
$this->unknown_plugins = $this->plugin_admin->scanPluginDirectory();
$plugins = $this->plugin_admin->scanPluginDirectory(true);
$this->unknown_plugins = $plugins;
}
/**
* 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)
{
CSRFProtection::verifyUnsafeRequest();
$unknown_plugins = $this->plugin_admin->scanPluginDirectory();
$unknown_plugins = $this->plugin_admin->scanPluginDirectory(true);
$plugin = $unknown_plugins[$number];
try {
......@@ -585,16 +586,16 @@ class Admin_PluginController extends AuthenticatedController
'manifest_info_de' => [
'label' => _('Standardbeschreibung des Plugins'),
'type' => 'info',
'value' => $this->metadata['descriptionlong'] ?? $this->metadata['description'],
'value' => $this->metadata['descriptionlong'] ?? $this->metadata['description'] ?? '',
'if' => "STUDIPFORM_SELECTEDLANGUAGES.description === 'de_DE'"
],
'manifest_info_en' => [
'label' => sprintf(_('Standardbeschreibung des Plugins (%s)'), _('Englisch')),
'type' => 'info',
'value' => $this->metadata['descriptionlong_en'] ?? $this->metadata['description_en'],
'value' => $this->metadata['descriptionlong_en'] ?? $this->metadata['description_en'] ?? null,
'if' => "STUDIPFORM_SELECTEDLANGUAGES.description === 'en_GB'"
],
'decription_mode' => [
'description_mode' => [
'label' => _('Modus der neuen Beschreibung'),
'type' => 'select',
'options' => [
......
......@@ -548,7 +548,7 @@ class Admin_RoleController extends AuthenticatedController
$actions->addLink(
_('Neue Rolle anlegen'),
$this->url_for('admin/role/add'),
Icon::create('add', 'clickable')
Icon::create('add')
)->asDialog('size=auto');
}
......
......@@ -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) {
......@@ -125,6 +125,8 @@ class Admin_SemClassesController extends AuthenticatedController
$sem_class->set('admission_type_default', Request::int("admission_type_default"));
$sem_class->set('show_raumzeit', Request::int("show_raumzeit"));
$sem_class->set('is_group', Request::int("is_group"));
$sem_class->set('unlimited_forbidden', Request::bool('unlimited_forbidden'));
$sem_class->set('admission_turnout_mandatory', Request::bool('admission_turnout_mandatory'));
$sem_class->store();
foreach (array_keys($sem_class->getModules()) as $module_name) {
if ($sem_class->isModuleMandatory($module_name) && !$old_data_sem_class->isModuleMandatory($module_name)) {
......
......@@ -68,14 +68,16 @@ class Admin_StatusgroupsController extends AuthenticatedController
$sidebar = Sidebar::get();
if ($this->tutor) {
$widget = new ActionsWidget();
$widget->addLink(_('Neue Gruppe anlegen'),
$widget->addLink(
_('Neue Gruppe anlegen'),
$this->url_for('admin/statusgroups/editGroup'),
Icon::create('add', 'clickable'))
->asDialog('size=auto');
$widget->addLink(_('Gruppenreihenfolge ändern'),
Icon::create('add')
)->asDialog('size=auto');
$widget->addLink(
_('Gruppenreihenfolge ändern'),
$this->url_for('admin/statusgroups/sortGroups'),
Icon::create('arr_2down', 'clickable'))
->asDialog();
Icon::create('arr_2down')
)->asDialog();
$sidebar->addWidget($widget);
}
// Collect all groups
......@@ -106,6 +108,7 @@ class Admin_StatusgroupsController extends AuthenticatedController
AND auth_user_md5.visible <> 'never'
ORDER BY Vorname, Nachname";
$this->searchType = new SQLSearch($query, _('Teilnehmende/n suchen'), 'username');
$this->addQuickfilter = count($this->groups) * count($this->membersOfInstitute) < 500;
}
/**
......@@ -121,8 +124,6 @@ class Admin_StatusgroupsController extends AuthenticatedController
$group = new Statusgruppen($group_id);
if ($group->isNew()) {
$group->range_id = Context::getId();
} else {
DataFieldEntry::removeAll(['', $group->statusgruppe_id]);
}
$group->name = Request::i18n('name');
......@@ -244,7 +245,7 @@ class Admin_StatusgroupsController extends AuthenticatedController
$this->check('edit');
$this->group = new Statusgruppen($group_id);
if (Request::submitted('confirm')) {
CSRFProtection::verifySecurityToken();
CSRFProtection::verifyUnsafeRequest();
// move all subgroups to the parent
$children = SimpleORMapCollection::createFromArray($this->group->children);
......@@ -270,7 +271,7 @@ class Admin_StatusgroupsController extends AuthenticatedController
$this->check('edit');
$this->group = new Statusgruppen($group_id);
if (Request::submitted('confirm')) {
CSRFProtection::verifySecurityToken();
CSRFProtection::verifyUnsafeRequest();
$this->group->sortMembersAlphabetic();
$this->redirect('admin/statusgroups/index#group-' . $group_id);
}
......
<?php
class Admin_TagsController extends AuthenticatedController
{
/**
* Common tasks for all actions.
*/
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
$GLOBALS['perm']->check('root');
Navigation::activateItem('/admin/locations/tags');
PageLayout::setTitle(_('Schlagwortverwaltung'));
}
public function index_action()
{
Tag::deleteBySQL('LEFT JOIN `tags_relations` ON (`tags`.`id` = `tags_relations`.`tag_id`) WHERE `tags_relations`.`range_id` IS NULL');
$this->page = Request::int('page', 0);
$this->tags = Tag::findBySQL('1 ORDER BY `name` ASC LIMIT :offset, :limit', [
'offset' => $this->page * Config::get()->ENTRIES_PER_PAGE,
'limit' => Config::get()->ENTRIES_PER_PAGE
]);
$this->all_tags = Tag::countBySql('1');
}
public function edit_action(Tag $tag)
{
PageLayout::setTitle(sprintf(_('Schlagwort „%s“ bearbeiten'), $tag->name));
$form = \Studip\Forms\Form::fromSORM(
$tag,
[
'legend' => _('Grunddaten'),
'fields' => [
'name' => [
'label' =>_('Name'),
'validate' => function ($value) use ($tag) {
$output = '';
if ($value !== mb_strtolower($value)) {
$output .= _('Schlagwörter sollen keine Großbuchstaben entahlten').' ';
}
foreach (['\n', '#', '|', ' '] as $forbidden) {
if (str_contains($value, $forbidden)) {
$output .= _('Schlagwörter dürfen keine Zeilenumbrüche, Leerzeichen, Doppelkreuze (#) oder Pipe-Zeichen (|) enthalten.').' ';
break;
}
}
if (Tag::findOneByName($value) && $value !== $tag->name) {
$output .= _('Dieses Schlagwort ist schon vergeben.').' ';
}
return $output !== '' ? $output : true;
}
],
'active' => _('Aktiv')
]
]
)->autoStore()->setURL($this->indexURL());
$this->render_form($form);
}
public function view_objects_action(Tag $tag)
{
$this->tag = $tag;
PageLayout::setTitle(sprintf(_("Verknüpfte Objekte mit Schlagwort „%s“"), $tag->name));
}
}