Forked from
Stud.IP / Stud.IP
4207 commits behind the upstream repository.
-
Jan-Hendrik Willms authoredJan-Hendrik Willms authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
courses.php 59.39 KiB
<?php
/**
* courses.php - Controller for admin and seminar related
* pages under "Meine Veranstaltungen"
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* @author David Siegfried <david@ds-labs.de>
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 or later
* @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
class Admin_CoursesController extends AuthenticatedController
{
/**
* This helper method retrieves the values of datafields when
* the user started a search for courses matching a specific value of
* one or more datafields.
* This method also checks if a datafield is activated by the user
* and will reject any value for datafields that aren't activated by the user.
*
* @return Array Associative array, consisting of datafield names
* (as array keys) and values for those datafields.
*/
private function getDatafieldFilters()
{
//first get the active datafields of the user:
$userSelectedElements = $this->getActiveElements();
$activeDatafields = $userSelectedElements['datafields'];
if (!$activeDatafields) {
return [];
}
//Ok, we have a list of active datafields whose value may be searched for.
//We must check for the request parameters (df_$DATAFIELD_ID)
//and return their IDs with a value.
$searchedDatafields = [];
foreach ($activeDatafields as $activeField) {
$requestParamValue = Request::get('df_'.$activeField);
if ($requestParamValue) {
$searchedDatafields[$activeField] = $requestParamValue;
}
}
return $searchedDatafields;
}
/**
* This method returns the appropriate widget for the given datafield.
*
* @param DataField datafield The datafield whose widget is requested.
*
* @return SidebarWidget|null Returns a SidebarWidget derivative or null in case of an error.
*/
private function getDatafieldWidget(DataField $datafield)
{
if ($datafield->accessAllowed()) {
//The current user is allowed to see this datafield.
//Now we must distinguish between the different types of data fields:
$type = $datafield->type;
if ($type == 'bool') {
//bool fields just need a checkbox for the states TRUE and FALSE
$checkboxWidget = new OptionsWidget($datafield->name);
$checkboxWidget->addCheckbox(
_('Feld gesetzt'),
Request::bool('df_'.$datafield->id, false),
URLHelper::getURL(
'dispatch.php/admin/courses/index',
['df_'.$datafield->id => '1']
),
URLHelper::getURL(
'dispatch.php/admin/courses/index'
)
);
return $checkboxWidget;
} elseif ($type == 'selectbox' || $type == 'radio' || $type == 'selectboxmultiple') {
$options = array_map('trim', explode("\n", DBManager::get()->fetchColumn(
'SELECT typeparam FROM datafields WHERE datafield_id = ?',
[$datafield->id]
)));
if ($options) {
$options = array_merge(
[' ' => '(' . _('keine Auswahl') . ')'],
$options
);
$selectWidget = new SelectWidget(
$datafield->name,
'?',
'df_' . $datafield->id
);
foreach($options as $option) {
$selectWidget->addElement(
new SelectElement($option, $option, Request::get('df_'.$datafield->id) == $option)
);
}
return $selectWidget;
}
return null;
} else {
//all other fields get a text field
$textWidget = new SearchWidget();
$textWidget->setTitle($datafield->name);
$textWidget->addNeedle(
'',
'df_'.$datafield->id
);
return $textWidget;
}
}
}
/**
* This method is responsible for building the sidebar.
*
* Depending on the sidebar elements the user has selected some of those
* elements are shown or not. To find out what elements
* the user has selected the user configuration is accessed.
*
* @param string courseTypeFilterConfig The selected value for the course type filter field, defaults to null.
* @return null This method does not return any value.
*/
private function buildSidebar($courseTypeFilterConfig = null)
{
/*
Depending on the elements the user has selected
some of the following elements may not be presented
in the sidebar.
*/
$visibleElements = $this->getActiveElements();
$sidebar = Sidebar::get();
/*
Order of elements:
* Navigation
* selected filters (configurable)
* selected actions widget
* actions
* view filter (configurable)
* export
*/
/*
Now draw the configurable elements according
to the values inside the visibleElements array.
*/
if ($visibleElements['search']) {
$this->setSearchWiget();
}
if ($visibleElements['institute']) {
$this->setInstSelector();
}
if ($visibleElements['semester']) {
$this->setSemesterSelector();
}
if ($visibleElements['stgteil']) {
$this->setStgteilSelector();
}
if ($visibleElements['courseType']) {
$this->setCourseTypeWidget($courseTypeFilterConfig);
}
if ($visibleElements['teacher']) {
$this->setTeacherWidget();
}
//if there are datafields in the list, draw their input fields, too:
if ($visibleElements['datafields']) {
//The datafields entry contains an array with datafield-IDs.
//We must fetch them from the database and show an appropriate widget
//for each datafield.
$visibleDatafieldIds = $visibleElements['datafields'];
$datafields = DataField::getDataFields('sem');
if ($datafields) {
foreach ($datafields as $datafield) {
if (in_array($datafield->id, $visibleDatafieldIds)) {
$widget = $this->getDatafieldWidget($datafield);
if ($widget) {
$sidebar->addWidget($widget);
}
}
}
}
}
//this shall be visible in every case:
$this->setActionsWidget($this->selected_action);
//actions: always visible, too
if ($GLOBALS['perm']->have_perm($this->sem_create_perm)) {
$actions = new ActionsWidget();
$actions->addLink(
_('Neue Veranstaltung anlegen'),
URLHelper::getURL('dispatch.php/course/wizard'),
Icon::create('seminar+add')
)->asDialog('size=50%');
$actions->addLink(
_('Diese Seitenleiste konfigurieren'),
URLHelper::getURL('dispatch.php/admin/courses/sidebar'),
Icon::create('admin')
)->asDialog();
$sidebar->addWidget($actions, 'links');
}
//the view filter's visibility is configurable:
if (in_array('viewFilter', $visibleElements)) {
$this->setViewWidget($this->view_filter);
}
//"export as Excel" is always visible:
if ($this->sem_create_perm) {
$params = [];
if ($GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT) {
$params['search'] = $GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT;
}
$export = new ExportWidget();
$export->addLink(
_('Als Excel exportieren'),
URLHelper::getURL('dispatch.php/admin/courses/export_csv', $params),
Icon::create('file-excel')
)->asDialog('size=auto');
$sidebar->addWidget($export);
}
}
/**
* Common tasks for all actions
*
* @param String $action Called action
* @param Array $args Possible arguments
*/
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
if ($GLOBALS['perm']->have_perm('admin')) {
Navigation::activateItem('/browse/my_courses/list');
} else {
Navigation::activateItem('/browse/admincourses');
}
// we are defintely not in an lecture or institute
closeObject();
//delete all temporary permission changes
if (is_array($_SESSION)) {
foreach (array_keys($_SESSION) as $key) {
if (mb_strpos($key, 'seminar_change_view_') !== false) {
unset($_SESSION[$key]);
}
}
}
$this->insts = Institute::getMyInstitutes($GLOBALS['user']->id);
if (empty($this->insts) && !$GLOBALS['perm']->have_perm('root')) {
PageLayout::postError(_('Sie wurden noch keiner Einrichtung zugeordnet'));
}
if (!$GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT) {
$GLOBALS['user']->cfg->store('MY_INSTITUTES_DEFAULT', $this->insts[0]['Institut_id']);
}
// Semester selection
if ($GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE) {
$this->semester = Semester::find($GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE);
}
if (Request::submitted('search')) {
$GLOBALS['user']->cfg->store('ADMIN_COURSES_SEARCHTEXT', Request::get('search'));
}
if (Request::get('reset-search')) {
$GLOBALS['user']->cfg->delete('ADMIN_COURSES_SEARCHTEXT');
}
if (Request::submitted('teacher_filter')) {
$GLOBALS['user']->cfg->store('ADMIN_COURSES_TEACHERFILTER', Request::option('teacher_filter'));
}
PageLayout::setHelpKeyword('Basis.Veranstaltungen');
PageLayout::setTitle(_('Verwaltung von Veranstaltungen und Einrichtungen'));
// Add admission functions.
PageLayout::addScript('studip-admission.js');
}
/**
* Show all courses with more options
*/
public function index_action()
{
$this->sem_create_perm = in_array(Config::get()->SEM_CREATE_PERM, ['root', 'admin', 'dozent'])
? Config::get()->SEM_CREATE_PERM
: 'dozent';
// get courses only if institutes available
$this->actions = $this->getActions();
$config_my_course_type_filter = $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER;
// Get the view filter
$this->view_filter = $this->getFilterConfig();
if (Request::get('sortFlag')) {
$GLOBALS['user']->cfg->store('MEINE_SEMINARE_SORT_FLAG', Request::get('sortFlag') === 'asc' ? 'DESC' : 'ASC');
}
if (Request::option('sortby')) {
$GLOBALS['user']->cfg->store('MEINE_SEMINARE_SORT', Request::option('sortby'));
}
$this->selected_action = $GLOBALS['user']->cfg->MY_COURSES_ACTION_AREA;
if (is_null($this->selected_action) || (!is_numeric($this->selected_action) && !class_exists($this->selected_action))) {
$this->selected_action = 1;
}
$this->sortby = $GLOBALS['user']->cfg->MEINE_SEMINARE_SORT;
$this->sortFlag = $GLOBALS['user']->cfg->MEINE_SEMINARE_SORT_FLAG ?: 'ASC';
$this->courses = $this->getCourses([
'sortby' => $this->sortby,
'sortFlag' => $this->sortFlag,
'view_filter' => $this->view_filter,
'typeFilter' => $config_my_course_type_filter,
'datafields' => $this->getDatafieldFilters()
], Request::get('display') === 'all');
if (in_array('contents', $this->view_filter)) {
$this->nav_elements = MyRealmModel::calc_nav_elements([$this->courses]);
}
// get all available teacher for infobox-filter
// filter by selected teacher
$_SESSION['MY_COURSES_LIST'] = array_map(function ($c, $id) {
return [
'Name' => $c['Name'],
'Seminar_id' => $id
];
}, array_values($this->courses), array_keys($this->courses));
$this->all_lock_rules = new SimpleCollection(array_merge(
[[
'name' => '--' . _("keine Sperrebene") . '--',
'lock_id' => 'none'
]],
LockRule::findAllByType('sem')
));
$this->aux_lock_rules = array_merge(
[[
'name' => '--' . _("keine Zusatzangaben") . '--',
'lock_id' => 'none'
]],
AuxLockRules::getAllLockRules()
);
//build the sidebar:
$this->buildSidebar($config_my_course_type_filter);
}
/**
* The sidebar action is responsible for showing a dialog
* that lets the user configure what elements of the sidebar are visible
* and which will be invisible.
*
* @return null This method does not return any value.
*/
public function sidebar_action()
{
if (Request::get('updateConfig', false)) {
/*
The user has changed the configuration.
Collect the activated elements:
*/
$searchActive = Request::get('searchActive');
$instituteActive = Request::get('instituteActive');
$semesterActive = Request::get('semesterActive');
$stgteilActive = Request::get('stgteilActive');
$courseTypeActive = Request::get('courseTypeActive');
$teacherActive = Request::get('teacherActive');
$viewFilterActive = Request::get('viewFilterActive');
$activeDatafields = Request::getArray('activeDatafields');
/*
Update or create an entry for the current user
in the user configuration table.
*/
$activeArray = [];
if ($searchActive) {
$activeArray['search'] = true;
}
if ($instituteActive) {
$activeArray['institute'] = true;
}
if ($semesterActive) {
$activeArray['semester'] = true;
}
if ($stgteilActive) {
$activeArray['stgteil'] = true;
}
if ($courseTypeActive) {
$activeArray['courseType'] = true;
}
if ($teacherActive) {
$activeArray['teacher'] = true;
}
if ($viewFilterActive) {
$activeArray['viewFilter'] = true;
}
if ($activeDatafields) {
$activeArray['datafields'] = $activeDatafields;
}
//store the configuration value:
$this->setActiveElements($activeArray);
$this->redirect('admin/courses/index');
} else {
/*
The user accesses the page to check the current configuration.
*/
$this->datafields = DataField::getDataFields('sem');
$this->userSelectedElements = $this->getActiveElements();
//add the last activity for each Course object:
$this->lastActivities = [];
}
}
/**
* Export action
*/
public function export_csv_action()
{
$filter_config = Request::getArray('fields');
if (count($filter_config) > 0) {
$sortby = $GLOBALS['user']->cfg->getValue('MEINE_SEMINARE_SORT');
$config_my_course_type_filter = $GLOBALS['user']->cfg->getValue('MY_COURSES_TYPE_FILTER');
$courses = $this->getCourses([
'sortby' => $sortby,
'sortFlag' => 'asc',
'typeFilter' => $config_my_course_type_filter,
'view_filter' => $filter_config,
], true);
$view_filters = $this->getViewFilters();
$data = [];
foreach ($courses as $course_id => $course) {
$sem = new Seminar(Course::buildExisting($course));
$row = [];
if (in_array('number', $filter_config)) {
$row['number'] = $course['VeranstaltungsNummer'];
}
if (in_array('name', $filter_config)) {
$row['name'] = $course['Name'];
}
if (in_array('type', $filter_config)) {
$row['type'] = sprintf(
'%s: %s',
$sem->getSemClass()['name'],
$sem->getSemType()['name']
);
}
if (in_array('room_time', $filter_config)) {
$_room = $sem->getDatesExport([
'semester_id' => $this->semester->id,
'show_room' => true
]);
$row['room_time'] = $_room ?: _('nicht angegeben');
}
if (in_array('requests', $filter_config)) {
$row['requests'] = $course['requests'];
}
if (in_array('teachers', $filter_config)) {
$row['teachers'] = implode(', ', array_map(function ($d) {
return $d['fullname'];
}, $course['dozenten']));
}
if (in_array('members', $filter_config)) {
$row['members'] = $course['teilnehmer'];
}
if (in_array('waiting', $filter_config)) {
$row['waiting'] = $course['waiting'];
}
if (in_array('preliminary', $filter_config)) {
$row['preliminary'] = $course['prelim'];
}
if (in_array('last_activity', $filter_config)) {
$row['last_activity'] = strftime('%x', $course['last_activity']);
}
if (in_array('semester', $filter_config)) {
$row['semester'] = $course->semester_text;
}
foreach (PluginManager::getInstance()->getPlugins('AdminCourseContents') as $plugin) {
foreach ($plugin->adminAvailableContents() as $index => $label) {
if (in_array($plugin->getPluginId() . "_" . $index, $filter_config)) {
$content = $plugin->adminAreaGetCourseContent(Course::find($course_id), $index);
$row[$plugin->getPluginId() . "_" . $index] = strip_tags(is_a($content, 'Flexi_Template')
? $content->render()
: $content
);
}
}
}
$data[$course_id] = $row;
}
$captions = [];
foreach ($filter_config as $index) {
$captions[$index] = $view_filters[$index];
}
foreach (PluginManager::getInstance()->getPlugins('AdminCourseContents') as $plugin) {
foreach ($plugin->adminAvailableContents() as $index => $label) {
if (in_array($plugin->getPluginId() . "_" . $index, $filter_config)) {
$captions[$plugin->getPluginId() . "_" . $index] = $label;
}
}
}
$tmpname = md5(uniqid('Veranstaltungsexport'));
if (array_to_csv($data, $GLOBALS['TMP_PATH'] . '/' . $tmpname, $captions)) {
$this->redirect(FileManager::getDownloadURLForTemporaryFile(
$tmpname,
'Veranstaltungen_Export.csv'
));
return;
}
} else {
PageLayout::setTitle(_('Spalten zum Export auswählen'));
$this->fields = $this->getViewFilters();
$this->selection = $this->getFilterConfig();
}
}
/**
* Set the selected institute or semester
*/
public function set_selection_action()
{
if (Request::option('institute')) {
$GLOBALS['user']->cfg->store('ADMIN_COURSES_TEACHERFILTER', null);
$GLOBALS['user']->cfg->store('MY_COURSES_SELECTED_STGTEIL', null);
$inst = explode('_', Request::option('institute'));
$GLOBALS['user']->cfg->store('MY_INSTITUTES_DEFAULT', $inst[0]);
if ($inst[1] == 'withinst') {
$GLOBALS['user']->cfg->store('MY_INSTITUTES_INCLUDE_CHILDREN', 1);
} else {
$GLOBALS['user']->cfg->store('MY_INSTITUTES_INCLUDE_CHILDREN', 0);
}
PageLayout::postSuccess(_('Die gewünschte Einrichtung wurde ausgewählt!'));
}
if (Request::option('sem_select')) {
$GLOBALS['user']->cfg->store('MY_COURSES_SELECTED_CYCLE', Request::option('sem_select'));
if (Request::option('sem_select') !== "all") {
$sem_name = Semester::find(Request::option('sem_select'))->name;
PageLayout::postSuccess(sprintf(_('Das %s wurde ausgewählt'), htmlReady($sem_name)));
} else {
PageLayout::postSuccess(_('Semesterfilter abgewählt'));
}
}
if (Request::option('stgteil_select')) {
$GLOBALS['user']->cfg->store('MY_COURSES_SELECTED_STGTEIL', Request::option('stgteil_select'));
if (Request::option('stgteil_select') !== "all") {
PageLayout::postSuccess(sprintf(
_('Der Studiengangteil %s wurde ausgewählt'),
htmlReady(StudiengangTeil::find(Request::option('stgteil_select'))->getDisplayName())
));
} else {
PageLayout::postSuccess(_('Studiengangteilfilter abgewählt'));
}
}
$this->redirect('admin/courses/index');
}
/**
* Set the lockrules of courses
*/
public function set_lockrule_action()
{
if (!$GLOBALS['perm']->have_perm('admin')) {
throw new AccessDeniedException();
}
$result = false;
$courses = Request::getArray('lock_sem');
$errors = [];
if (!empty($courses)) {
foreach ($courses as $course_id => $value) {
if ($GLOBALS['perm']->have_studip_perm('dozent', $course_id)) {
// force to pre selection
if (Request::get('lock_sem_all') && Request::submitted('all')) {
$value = Request::get('lock_sem_all');
}
$course = Course::find($course_id);
if ($value == 'none') {
$value = null;
}
if ($course->lock_rule == $value) {
continue;
}
$course->setValue('lock_rule', $value);
if (!$course->store()) {
$errors[] = $course->name;
} else {
$result = true;
}
}
}
if ($result) {
PageLayout::postSuccess(_('Die gewünschten Änderungen wurden erfolgreich durchgeführt!'));
}
if ($errors) {
PageLayout::postError(
_('Bei den folgenden Veranstaltungen ist ein Fehler aufgetreten'),
array_map('htmlReady', $errors)
);
}
}
$this->redirect('admin/courses/index');
}
/**
* Lock or unlock courses
*/
public function set_locked_action()
{
$admission_locked = Request::getArray('admission_locked');
$all_courses = Request::getArray('all_sem');
$course_set_id = CourseSet::getGlobalLockedAdmissionSetId();
foreach ($all_courses as $course_id) {
if ($GLOBALS['perm']->have_studip_perm('dozent', $course_id)) {
$set = CourseSet::getSetForCourse($course_id);
if (!is_null($set)) {
if (!$set->hasAdmissionRule('LockedAdmission')) {
continue;
}
if ($set->hasAdmissionRule('LockedAdmission') && !isset($admission_locked[$course_id])) {
if (CourseSet::removeCourseFromSet($set->getId(), $course_id)) {
$log_msg = _('Veranstaltung wurde entsperrt');
}
}
}
if (is_null($set) && isset($admission_locked[$course_id])) {
if (CourseSet::addCourseToSet($course_set_id, $course_id)) {
$log_msg = sprintf(_('Veranstaltung wurde gesperrt, set_id: %s'), $course_set_id);
}
}
if ($log_msg) {
StudipLog::log('SEM_CHANGED_ACCESS', $course_id, NULL, $log_msg);
}
}
}
PageLayout::postSuccess(_('Die gewünschten Änderungen wurden ausgeführt!'));
$this->redirect('admin/courses/index');
}
/**
* Set the visibility of a course
*/
public function set_visibility_action()
{
$result = false;
$visibilites = Request::intArray('visibility');
$all_courses = Request::getArray('all_sem');
$errors = [];
if (!empty($all_courses)) {
foreach ($all_courses as $course_id) {
if ($GLOBALS['perm']->have_studip_perm('tutor', $course_id)) {
$course = Course::find($course_id);
if ($course->isOpenEnded() || $course->end_semester->visible) {
$visibility = $visibilites[$course_id] ?: 0;
if ($course->visible == $visibility) {
continue;
}
$course->visible = $visibility;
if (!$course->store()) {
$errors[] = $course->name;
} else {
$result = true;
StudipLog::log($visibility ? 'SEM_VISIBLE' : 'SEM_INVISIBLE', $course->id);
}
}
}
}
if ($result) {
PageLayout::postSuccess(_('Die Sichtbarkeit wurde bei den gewünschten Veranstatungen erfolgreich geändert!'));
}
if ($errors) {
PageLayout::postError(
_('Bei den folgenden Veranstaltungen ist ein Fehler aufgetreten'),
array_map('htmlReady', $errors)
);
}
}
$this->redirect('admin/courses/index');
}
/**
* Set the additional course informations
*/
public function set_aux_lockrule_action()
{
$result = false;
$courses = Request::getArray('lock_sem');
$lock_sem_forced = Request::getArray('lock_sem_forced');
$errors = [];
if (!empty($courses)) {
foreach ($courses as $course_id => $value) {
if ($GLOBALS['perm']->have_studip_perm('tutor', $course_id)) {
// force to pre selection
if (Request::submitted('all')) {
$value = Request::get('lock_sem_all');
$value_forced = Request::int('aux_all_forced');
} else {
$value_forced = $lock_sem_forced[$course_id];
}
$course = Course::find($course_id);
if (!$value) {
$value_forced = 0;
}
$course->setValue('aux_lock_rule', $value);
$course->setValue('aux_lock_rule_forced', $value_forced);
$ok = $course->store();
if ($ok === false) {
$errors[] = $course->name;
} elseif ($ok) {
$result = true;
}
}
}
if ($result) {
PageLayout::postSuccess(_('Die gewünschten Änderungen wurden erfolgreich durchgeführt!'));
}
if ($errors) {
PageLayout::postError(
_('Bei den folgenden Veranstaltungen ist ein Fehler aufgetreten'),
array_map('htmlReady', $errors)
);
}
}
$this->redirect('admin/courses/index');
}
/**
* Set the selected view filter and store the selection in configuration
*/
public function set_view_filter_action($filter = null, $state = true)
{
// store view filter in configuration
if (!is_null($filter)) {
$filters = $this->getFilterConfig();
if ($state) {
$filters = array_diff($filters, [$filter]);
} else {
$filters[] = $filter;
}
$this->setFilterConfig($filters);
}
$this->redirect('admin/courses/index');
}
/**
* Set the selected action type and store the selection in configuration
*/
public function set_action_type_action()
{
// select the action area
if (Request::option('action_area')) {
$GLOBALS['user']->cfg->store('MY_COURSES_ACTION_AREA', Request::option('action_area'));
PageLayout::postSuccess(_('Der Aktionsbereich wurde erfolgreich übernommen!'));
}
$this->redirect('admin/courses/index');
}
/**
* Set the selected course type filter and store the selection in configuration
*/
public function set_course_type_action()
{
if (Request::option('course_type')) {
$GLOBALS['user']->cfg->store('MY_COURSES_TYPE_FILTER', Request::option('course_type'));
PageLayout::postSuccess(_('Der gewünschte Veranstaltungstyp wurde übernommen!'));
}
$this->redirect('admin/courses/index');
}
/**
* Marks a course as complete/incomplete.
*
* @param String $course_id Id of the course
*/
public function toggle_complete_action($course_id)
{
if (!$GLOBALS['perm']->have_studip_perm('tutor', $course_id)) {
throw new AccessDeniedException();
}
$course = Course::find($course_id);
$course->completion = ($course->completion + 1) % 3;
$course->store();
if (Request::isXhr()) {
$this->render_json((int)$course->completion);
} else {
$this->redirect('admin/courses/index#course-' . $course_id);
}
}
/**
* Changes the notice for a course.
*
* @param string $course_id
*/
public function notice_action(Course $course)
{
if (Request::isPost()) {
$course->config->store('COURSE_ADMIN_NOTICE', trim(Request::get('notice')));
if (Request::isXhr()) {
$this->response->add_header('X-Dialog-Notice', json_encode([
'id' => $course->id,
'notice' => $course->config->COURSE_ADMIN_NOTICE,
]));
$this->render_nothing();
} else {
$this->redirect($this->indexURL("#course-{$course->id}"));
}
return;
}
$this->course = $course;
$this->notice = $course->config->COURSE_ADMIN_NOTICE;
}
public function get_subcourses_action($course_id)
{
// get courses only if institutes available
$this->actions = $this->getActions();
// Get the view filter
$this->view_filter = $this->getFilterConfig();
$this->selected_action = $GLOBALS['user']->cfg->MY_COURSES_ACTION_AREA;
if (is_null($this->selected_action) || (!is_numeric($this->selected_action) && !class_exists($this->selected_action))) {
$this->selected_action = 1;
}
$this->courses = $this->getCourses([
'sortby' => $this->sortby,
'sortFlag' => $this->sortFlag,
'view_filter' => $this->view_filter,
'datafields' => $this->getDatafieldFilters(),
'parent_course' => $course_id
]);
$this->parent = $course_id;
}
/**
* Return a specifically action or all available actions
* @param null $selected
* @return array
*/
private function getActions($selected = null)
{
// array for the avaiable modules
$sem_filter = $this->semester ? $this->semester->beginn : 'all';
$actions = [
1 => [
'name' => _('Grunddaten'),
'title' => _('Grunddaten'),
'url' => 'dispatch.php/course/basicdata/view?cid=%s',
'attributes' => ['data-dialog' => 'size=big'],
],
2 => [
'name' => _('Studienbereiche'),
'title' => _('Studienbereiche'),
'url' => 'dispatch.php/course/study_areas/show/?cid=%s&from=admin/courses',
'attributes' => ['data-dialog' => 'size=big'],
],
3 => [
'name' => _('Zeiten/Räume'),
'title' => _('Zeiten/Räume'),
'url' => 'dispatch.php/course/timesrooms/index?cid=%s',
'attributes' => ['data-dialog' => 'size=big'],
'params' => [
'newFilter' => $sem_filter,
'cmd' => 'applyFilter'
],
],
8 => [
'name' => _('Sperrebene'),
'title' => _('Sperrebenen'),
'url' => 'dispatch.php/admin/courses/set_lockrule',
'multimode' => true,
'partial' => 'lock.php',
],
9 => [
'name' => _('Sichtbarkeit'),
'title' => _('Sichtbarkeit'),
'url' => 'dispatch.php/admin/courses/set_visibility',
'multimode' => true,
'partial' => 'visibility.php',
],
10 => [
'name' => _('Zusatzangaben'),
'title' => _('Zusatzangaben'),
'url' => 'dispatch.php/admin/courses/set_aux_lockrule',
'multimode' => true,
'partial' => 'aux-select.php',
],
11 => [
'name' => _('Veranstaltung kopieren'),
'title' => _('Kopieren'),
'url' => 'dispatch.php/course/wizard/copy/%s',
'attributes' => ['data-dialog' => 'size=big'],
],
14 => [
'name' => 'Zugangsberechtigungen',
'title' => _('Zugangsberechtigungen'),
'url' => 'dispatch.php/course/admission?cid=%s',
'attributes' => ['data-dialog' => 'size=big'],
],
16 => [
'name' => _('Löschen'),
'title' => _('Löschen'),
'url' => 'dispatch.php/course/archive/confirm',
'multimode' => true,
'partial' => 'add_to_archive.php',
],
17 => [
'name' => _('Gesperrte Veranstaltungen'),
'title' => _('Einstellungen speichern'),
'url' => 'dispatch.php/admin/courses/set_locked',
'multimode' => true,
'partial' => 'admission_locked.php',
],
18 => [
'name' => _('Startsemester'),
'title' => _('Startsemester'),
'url' => 'dispatch.php/course/timesrooms/editSemester?cid=%s&origin=admin_courses',
'attributes' => ['data-dialog' => 'size=400'],
],
19 => [
'name' => _('LV-Gruppen'),
'title' => _('LV-Gruppen'),
'url' => 'dispatch.php/course/lvgselector?cid=%s&from=admin/courses',
'attributes' => ['data-dialog' => 'size=big'],
],
20 => [
'name' => _('Notiz'),
'title' => _('Notiz'),
'url' => $this->noticeURL('%s'),
'attributes' => ['data-dialog' => 'size=auto'],
'partial' => 'notice-action.php',
],
];
if (!$GLOBALS['perm']->have_perm('admin')) {
unset($actions[8]);
if (!Config::get()->ALLOW_DOZENT_DELETE) {
unset($actions[16]);
}
}
if (!$GLOBALS['perm']->have_perm('dozent')) {
unset($actions[11]);
unset($actions[16]);
}
if (Config::get()->RESOURCES_ENABLE && Config::get()->RESOURCES_ALLOW_ROOM_REQUESTS) {
$actions[4] = [
'name' => 'Raumanfragen',
'title' => _('Raumanfragen'),
'url' => 'dispatch.php/course/room_requests/index?cid=%s&origin=admin_courses',
'attributes' => ['data-dialog' => 'size=big'],
];
}
ksort($actions);
foreach (PluginManager::getInstance()->getPlugins('AdminCourseAction') as $plugin) {
$actions[get_class($plugin)] = [
'name' => $plugin->getPluginName(),
'title' => $plugin->getPluginName(),
'url' => $plugin->getAdminActionURL(),
'attributes' => ['data-dialog' => 'size=big'],
'multimode' => $plugin->useMultimode()
];
}
if (is_null($selected)) {
return $actions;
}
return $actions[$selected];
}
/**
* Set and return all needed view filters
* @return array
*/
private function getViewFilters()
{
$views = [
'number' => _('Nr.'),
'name' => _('Name'),
'type' => _('Veranstaltungstyp'),
'room_time' => _('Raum/Zeit'),
'semester' => _('Semester'),
'requests' => _('Raumanfragen'),
'teachers' => _('Lehrende'),
'members' => _('Teilnehmende'),
'waiting' => _('Personen auf Warteliste'),
'preliminary' => _('Vorläufige Anmeldungen'),
'contents' => _('Inhalt'),
'last_activity' => _('Letzte Aktivität'),
];
foreach (PluginManager::getInstance()->getPlugins('AdminCourseContents') as $plugin) {
foreach ($plugin->adminAvailableContents() as $index => $label) {
$views[$plugin->getPluginId() . "_" . $index] = $label;
}
}
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 ($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") {
list($class_filter,$type_filter) = explode('_', $params['typeFilter']);
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', $params['sortFlag'], $params['sortFlag'], $params['sortFlag']), $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 = array_map('reset', $courses);
$visit_data = get_objects_visits(array_keys($seminars), 0, null, null, array_keys(MyRealmModel::getDefaultModules()));
if (!empty($seminars)) {
foreach ($seminars as $seminar_id => $seminar) {
$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'])) {
$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'] = new SimpleCollection(ToolActivation::findbyRange_id($seminar_id, "ORDER BY position"));
$seminars[$seminar_id]['navigation'] = MyRealmModel::getAdditionalNavigations(
$seminar_id,
$seminars[$seminar_id],
$seminars[$seminar_id]['sem_class'],
$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['course_set']) {
$set = new CourseSet($seminar['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
*
* @param String $course_id Id of the course
* @return array of user infos [user_id, username, Nachname, fullname]
*/
private function getTeacher($course_id)
{
$teachers = CourseMember::findByCourseAndStatus($course_id, 'dozent');
$collection = SimpleCollection::createFromArray($teachers);
return $collection->map(function (CourseMember $teacher) {
return [
'user_id' => $teacher->user_id,
'username' => $teacher->username,
'Nachname' => $teacher->nachname,
'fullname' => $teacher->getUserFullname('no_title_rev'),
];
});
}
/**
* Adds view filter to the sidebar
* @param array $configs
*/
private function setViewWidget($configs = [])
{
$configs = $configs ?: [];
$checkbox_widget = new OptionsWidget();
$checkbox_widget->setTitle(_('Darstellungsfilter'));
foreach ($this->getViewFilters() as $index => $label) {
$state = in_array($index, $configs);
$checkbox_widget->addCheckbox(
$label,
$state,
$this->url_for('admin/courses/set_view_filter/' . $index . '/' . $state)
);
}
Sidebar::get()->addWidget($checkbox_widget, 'views');
}
/**
* Adds the institutes selector to the sidebar
*/
private function setInstSelector()
{
$sidebar = Sidebar::Get();
$list = new SelectWidget(
_('Einrichtung'),
$this->url_for('admin/courses/set_selection'),
'institute'
);
$list->class = 'institute-list';
if ($GLOBALS['perm']->have_perm('root') || (count($this->insts) > 1)) {
$list->addElement(new SelectElement(
'all',
$GLOBALS['perm']->have_perm('root') ? _('Alle') : _('Alle meine Einrichtungen'),
$GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === 'all'),
'select-all'
);
}
foreach ($this->insts as $institut) {
$list->addElement(
new SelectElement(
$institut['Institut_id'],
(!$institut['is_fak'] ? ' ' : '') . $institut['Name'],
$GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === $institut['Institut_id']
),
'select-' . $institut['Institut_id']
);
//check if the institute is a faculty.
//If true, then add another option to display all courses
//from that faculty and all its institutes.
//$institut is an array, we can't use the method isFaculty() here!
if ($institut['fakultaets_id'] == $institut['Institut_id']) {
$list->addElement(
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)
),
'select-' . $institut['Name'] . '-with_institutes'
);
}
}
$sidebar->addWidget($list, 'filter_institute');
}
/**
* Adds the semester selector to the sidebar
*/
private function setSemesterSelector()
{
$semesters = array_reverse(Semester::getAll());
$sidebar = Sidebar::Get();
$list = new SelectWidget(_('Semester'), $this->url_for('admin/courses/set_selection'), 'sem_select');
$list->addElement(new SelectElement('all', _('Alle')), 'sem_select-all');
foreach ($semesters as $semester) {
$list->addElement(new SelectElement(
$semester->id,
$semester->name,
$semester->id === $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE
), 'sem_select-' . $semester->id);
}
$sidebar->addWidget($list, 'filter_semester');
}
/**
* Adds the studiengangteil selector to the sidebar
*/
private function setStgteilSelector()
{
$stgteile = StudiengangTeil::getAllEnriched('fach_name','ASC', ['mvv_fach_inst.institut_id' => $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT]);
$sidebar = Sidebar::Get();
$list = new SelectWidget(_('Studiengangteil'), $this->url_for('admin/courses/set_selection'), 'stgteil_select');
$list->addElement(new SelectElement('all', _('Alle')), 'stgteil_select-all');
foreach ($stgteile as $stgteil) {
$list->addElement(new SelectElement(
$stgteil->id,
$stgteil->getDisplayName(),
$stgteil->id === $GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL
), 'stgteil_select-' . $stgteil->id);
}
$sidebar->addWidget($list, 'filter_stgteil');
}
/**
* Adds HTML-Selector to the sidebar
* @param null $selected_action
* @return string
*/
private function setActionsWidget($selected_action = null)
{
$actions = $this->getActions();
$sidebar = Sidebar::Get();
$list = new SelectWidget(_('Aktionsbereichauswahl'), $this->url_for('admin/courses/set_action_type'), 'action_area');
foreach ($actions as $index => $action) {
$list->addElement(new SelectElement($index, $action['name'], $selected_action == $index), 'action-aria-' . $index);
}
$sidebar->addWidget($list, 'editmode');
}
/**
* Returns a course type widthet depending on all available courses and theirs types
* @param string $selected
* @param array $params
* @return ActionsWidget
*/
private function setCourseTypeWidget($selected = 'all')
{
$sidebar = Sidebar::get();
$this->url = $this->url_for('admin/courses/set_course_type');
$this->types = [];
$this->selected = $selected;
$list = new SelectWidget(
_('Veranstaltungstypfilter'),
$this->url_for('admin/courses/set_course_type'),
'course_type'
);
$list->addElement(new SelectElement(
'all', _('Alle'), $selected === 'all'
), 'course-type-all');
foreach ($GLOBALS['SEM_CLASS'] as $class_id => $class) {
if ($class['studygroup_mode']) {
continue;
}
$element = new SelectElement(
$class_id,
$class['name'],
$selected === (string)$class_id
);
$list->addElement(
$element->setAsHeader(),
'course-type-' . $class_id
);
foreach ($class->getSemTypes() as $id => $result) {
$element = new SelectElement(
$class_id . '_' . $id,
$result['name'],
$selected === $class_id . '_' . $id
);
$list->addElement(
$element->setIndentLevel(1),
'course-type-' . $class_id . '_' . $id
);
}
}
$sidebar->addWidget($list, 'filter-course-type');
}
/**
* Returns a widget to selected a specific teacher
* @param array $teachers
* @return ActionsWidget|null
*/
private function setTeacherWidget()
{
if (!$GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT || $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === "all") {
return;
}
$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'
ORDER BY auth_user_md5.Nachname ASC, auth_user_md5.Vorname ASC
", [
'institut_id' => $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT
],
function ($data) {
$ret['user_id'] = $data['user_id'];
unset($data['user_id']);
$ret['fullname'] = User::build($data)->getFullName("full_rev");
return $ret;
}
);
$sidebar = Sidebar::Get();
$list = new SelectWidget(_('Lehrendenfilter'), $this->url_for('admin/courses/index'), 'teacher_filter');
$list->addElement(new SelectElement('all', _('alle'), Request::get('teacher_filter') == 'all'), 'teacher_filter-all');
foreach ($teachers as $teacher) {
$list->addElement(new SelectElement(
$teacher['user_id'],
$teacher['fullname'],
$GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER === $teacher['user_id']
), 'teacher_filter-' . $teacher['user_id']);
}
$sidebar->addWidget($list, 'filter_teacher');
}
/**
* Adds a search widget to the sidebar
*/
private function setSearchWiget()
{
$sidebar = Sidebar::Get();
$search = new SearchWidget(URLHelper::getURL('dispatch.php/admin/courses'));
$search->addNeedle(_('Freie Suche'), 'search', true, null, null, $GLOBALS['user']->cfg->ADMIN_COURSES_SEARCHTEXT);
$sidebar->addWidget($search, 'filter_search');
}
/**
* Returns the filter configuration.
*
* @return array containing the filter configuration
*/
private function getFilterConfig()
{
$available_filters = array_keys($this->getViewFilters());
$temp = $GLOBALS['user']->cfg->MY_COURSES_ADMIN_VIEW_FILTER_ARGS;
if ($temp) {
$config = json_decode($temp, true);
if (!is_array($config)) {
$config = [];
}
$config = array_intersect($config, $available_filters);
} else {
$config = [];
}
if (!$config) {
$config = $this->setFilterConfig($available_filters);
}
return $config;
}
/**
* Sets the filter configuration.
*
* @param Array $config Filter configuration
* @return array containing the filter configuration
*/
private function setFilterConfig($config)
{
$config = $config ?: array_keys($this->getViewFilters());
$GLOBALS['user']->cfg->store('MY_COURSES_ADMIN_VIEW_FILTER_ARGS', json_encode($config));
return $config;
}
/**
* Returns the default element configuration.
*
* @return array containing the default element configuration
*/
private function getActiveElementsDefault()
{
return [
'search' => true,
'institute' => true,
'semester' => true,
'stgteil' => true,
'courseType' => true,
'teacher' => true,
'viewFilter' => true
];
}
/**
* Returns the active element configuration of the current user.
*
* @return array containing the active element configuration
*/
private function getActiveElements()
{
$active_elements = $GLOBALS['user']->cfg->ADMIN_COURSES_SIDEBAR_ACTIVE_ELEMENTS;
if ($active_elements) {
return json_decode($active_elements, true);
} else {
return $this->getActiveElementsDefault();
}
}
/**
* Sets the active element configuration for the current user.
*
* @param Array $active_elements element configuration
*/
private function setActiveElements($active_elements)
{
if ($active_elements == $this->getActiveElementsDefault()) {
$GLOBALS['user']->cfg->delete('ADMIN_COURSES_SIDEBAR_ACTIVE_ELEMENTS');
} else {
$GLOBALS['user']->cfg->store('ADMIN_COURSES_SIDEBAR_ACTIVE_ELEMENTS', json_encode($active_elements));
}
}
}