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
  • 1204-oer-vorschlage-von-studierenden-polishing
  • 5.0
  • 5.1
  • 5.2
  • 5.3
  • 5.4
  • 5.5
  • 6.0
  • 805-oer-campus-loschen-von-filtern-nicht-barrierefrei
  • 853-oer-post-upload-dialog-zum-teilen-von-dateien
  • biest-00671-2
  • biest-01110
  • biest-02063
  • biest-02192-and-02196
  • biest-02386
  • biest-02988
  • biest-03109
  • biest-03119
  • biest-03798
  • biest-05112
  • biest-05260
  • biest-05480
  • biest-05488
  • biest-1054
  • biest-1171-vue-widget
  • biest-1310
  • biest-1347
  • biest-1844
  • biest-1874
  • biest-1910
  • biest-1917
  • biest-2055
  • biest-2094
  • biest-2131
  • biest-2217
  • biest-236-2
  • biest-2381
  • biest-2553
  • biest-2553-2
  • biest-2652
  • biest-2721
  • biest-2742
  • biest-2748
  • biest-2813
  • biest-2865-2
  • biest-2981
  • biest-3026
  • biest-3095
  • biest-3098
  • biest-3131
  • biest-3427
  • biest-3457
  • biest-3545
  • biest-3568
  • biest-3580
  • biest-3641
  • biest-3641-2
  • biest-3664
  • biest-3665
  • biest-3721
  • biest-3721-2
  • biest-3759
  • biest-3785
  • biest-3894
  • biest-3999-cache-for-sidebar-widget
  • biest-4463
  • biest-4564-2
  • biest-4577
  • biest-4760
  • biest-4859
  • biest-5044
  • biest-5149
  • biest-5156
  • biest-5238
  • biest-5342
  • biest-5559
  • biest-5563
  • biest-5602
  • biest-5606
  • biest-5623
  • biest-5629
  • biest-832-50
  • biest-832-51
  • biest-887-50
  • biest-889
  • biest-cicd-performance
  • cherry-pick-088c7a54
  • cherry-pick-435a68d4
  • courseware-chunk
  • default-stockimages
  • devops-4082
  • devops-5331
  • docs
  • feature/peerreview-6
  • icon-creation
  • issue-2373
  • issue-2388
  • issue-2484
  • issue-2532
  • issue-2664
  • v5.0
  • v5.0.1
  • v5.0.2
  • v5.0.3
  • v5.0.4
  • v5.0.5
  • v5.0.6
  • v5.0.7
  • v5.0.8
  • v5.0.9
  • v5.1
  • v5.1.1
  • v5.1.2
  • v5.1.3
  • v5.1.4
  • v5.1.5
  • v5.1.6
  • v5.1.7
  • v5.1.8
  • v5.1.9
  • v5.2
  • v5.2.1
  • v5.2.2
  • v5.2.3
  • v5.2.4
  • v5.2.5
  • v5.2.6
  • v5.2.7
  • v5.2.8
  • v5.2.9
  • v5.3
  • v5.3.1
  • v5.3.10
  • v5.3.2
  • v5.3.3
  • v5.3.4
  • v5.3.5
  • v5.3.6
  • v5.3.7
  • v5.3.8
  • v5.3.9
  • v5.4
  • v5.4.1
  • v5.4.2
  • v5.4.3
  • v5.4.4
  • v5.4.5
  • v5.4.6
  • v5.4.7
  • v5.5
  • v5.5.1
  • v5.5.2
  • v5.5.3
  • v5.5.4
154 results

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
  • 122-use-symfony-console
  • 416-fix-cw-block-feedback
  • 434-fix-missing-jsonapi-relationships
  • 446-fix-courseware-copy
  • 5.0
  • 5.2
  • 614-add-error-logging-v51
  • TIC#9569
  • a11ydialog
  • add-eslint-to-webpack
  • better-bookmarks
  • biest-1335
  • biest-1454
  • biest-1514
  • biest-887-simple
  • biest/4317-circular-dependencies-in-plugins
  • biest/issue-5051
  • biest/unlock-blocks
  • database-seeders-and-factories
  • feature-feedback-jsonapi
  • feature/balloon-plus
  • feature/broadcasting
  • feature/peer-review-2
  • feature/peerreview
  • feature/plugins-cli
  • feature/stock-images-unsplash
  • feature/vite
  • fix/typo-in-1a70031
  • main
  • merge-362-into-5.0
  • oauth2
  • step-2484-peerreview
  • step-3263
  • tests/simplify-jsonapi-tests
  • tic-2588
35 results
Show changes
Showing
with 1859 additions and 2108 deletions
<?php
require_once 'app/controllers/calendar/calendar.php';
require_once 'app/controllers/authenticated_controller.php';
class Calendar_GroupController extends Calendar_CalendarController
{
public function before_filter(&$action, &$args)
{
$this->base = 'calendar/group/';
parent::before_filter($action, $args);
}
protected function createSidebar($active = 'week', $calendar = null)
{
parent::createSidebar($active, $calendar);
$sidebar = Sidebar::Get();
$actions = new ActionsWidget();
$actions->addLink(_('Termin anlegen'),
$this->url_for('calendar/group/edit'),
Icon::create('add'),
['data-dialog' => 'size=auto']);
$actions->addLink(_('Kalender freigeben'),
$this->url_for('calendar/single/manage_access/' . $GLOBALS['user']->id,
['group_filter' => $this->range_id]),
Icon::create('community'),
['id' => 'calendar-open-manageaccess',
'data-dialog' => '', 'data-dialogname' => 'manageaccess']);
$sidebar->addWidget($actions);
}
protected function getTitle($group)
{
$title = sprintf(_('Terminkalender der Gruppe "%s"'), $group->name);
return $title;
}
public function index_action()
{
// switch to the view the user has selected in his personal settings
$default_view = $this->settings['view'] ?: 'week';
$this->redirect($this->url_for('calendar/group/' . $default_view));
}
public function edit_action($range_id = null, $event_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
// get group and the calendars of the members
// the first calendar is the calendar of the actual user
$this->calendar = new SingleCalendar($GLOBALS['user']->id);
$group = $this->getGroup($this->calendar);
$this->attendee_ids = [];
if ($group) {
$calendar_owners = CalendarUser::getOwners($GLOBALS['user']->id,
Calendar::PERMISSION_WRITABLE)->pluck('owner_id');
$members = $group->members->pluck('user_id');
$user_id = Request::option('user_id');
$this->attendee_ids = array_intersect($calendar_owners, $members);
$this->attendee_ids[] = $GLOBALS['user']->id;
if ($user_id && in_array($user_id, $this->attendee_ids)) {
$this->attendee_ids = [$user_id];
}
}
$this->event = $this->calendar->getEvent($event_id);
if ($this->event->isNew()) {
$this->event = $this->calendar->getNewEvent();
if (Request::get('isdayevent')) {
$this->event->setStart(mktime(0, 0, 0, date('n', $this->atime),
date('j', $this->atime), date('Y', $this->atime)));
$this->event->setEnd(mktime(23, 59, 59, date('n', $this->atime),
date('j', $this->atime), date('Y', $this->atime)));
} else {
$this->event->setStart($this->atime);
$this->event->setEnd($this->atime + 3600);
}
$this->event->setAuthorId($GLOBALS['user']->id);
$this->event->setEditorId($GLOBALS['user']->id);
$this->event->setAccessibility('PRIVATE');
if ($this->attendee_ids) {
foreach ($this->attendee_ids as $attendee_id) {
$attendee_event = clone $this->event;
$attendee_event->range_id = $attendee_id;
$this->attendees[] = $attendee_event;
}
}
if (!Request::isXhr()) {
PageLayout::setTitle($this->getTitle($this->calendar, _('Neuer Termin')));
}
} else {
// open read only events and course events not as form
// show information in dialog instead
if (!$this->event->havePermission(Event::PERMISSION_WRITABLE)
|| $this->event instanceof CourseEvent) {
$this->redirect($this->url_for('calendar/single/event/' . implode('/',
[$this->range_id, $this->event->event_id])));
return null;
}
$this->attendees = $this->event->attendees;
if (!Request::isXhr()) {
PageLayout::setTitle($this->getTitle($this->calendar, _('Termin bearbeiten')));
}
}
if (Config::get()->CALENDAR_GROUP_ENABLE
&& $this->calendar->getRange() == Calendar::RANGE_USER) {
$search_obj = new SQLSearch("SELECT auth_user_md5.user_id, {$GLOBALS['_fullname_sql']['full_rev']} as fullname, username, perms "
. "FROM calendar_user "
. "LEFT JOIN auth_user_md5 ON calendar_user.owner_id = auth_user_md5.user_id "
. "LEFT JOIN user_info ON (auth_user_md5.user_id = user_info.user_id) "
. 'WHERE calendar_user.user_id = '
. DBManager::get()->quote($GLOBALS['user']->id)
. ' AND calendar_user.permission > ' . Event::PERMISSION_READABLE
. ' AND (username LIKE :input OR Vorname LIKE :input '
. "OR CONCAT(Vorname,' ',Nachname) LIKE :input "
. "OR CONCAT(Nachname,' ',Vorname) LIKE :input "
. "OR Nachname LIKE :input OR {$GLOBALS['_fullname_sql']['full_rev']} LIKE :input "
. ") ORDER BY fullname ASC",
_('Nutzer suchen'), 'user_id');
$this->quick_search = QuickSearch::get('user_id', $search_obj)
->fireJSFunctionOnSelect('STUDIP.Messages.add_adressee');
// $default_selected_user = array($this->calendar->getRangeId());
$this->mps = MultiPersonSearch::get('add_adressees')
->setLinkText(_('Mehrere Teilnehmende hinzufügen'))
// ->setDefaultSelectedUser($default_selected_user)
->setTitle(_('Mehrere Teilnehmende hinzufügen'))
->setExecuteURL($this->url_for($this->base . 'edit'))
->setJSFunctionOnSubmit('STUDIP.Messages.add_adressees')
->setSearchObject($search_obj);
$owners = SimpleORMapCollection::createFromArray(
CalendarUser::findByUser_id($this->calendar->getRangeId()))
->pluck('owner_id');
foreach (Calendar::getGroups($GLOBALS['user']->id) as $group) {
$this->mps->addQuickfilter(
$group->name,
$group->members->filter(
function ($member) use ($owners) {
if (in_array($member->user_id, $owners)) {
return $member;
}
})->pluck('user_id')
);
}
}
$stored = false;
if (Request::submitted('store')) {
$stored = $this->storeEventData($this->event, $this->calendar);
}
if ($stored !== false) {
// switch back to group context
$this->range_id = $group->getId();
if ($stored === 0) {
if (Request::isXhr()) {
header('X-Dialog-Close: 1');
exit;
} else {
PageLayout::postSuccess(_('Der Termin wurde nicht geändert.'));
$this->relocate('calendar/group/' . $this->last_view, ['atime' => $this->atime]);
}
} else {
PageLayout::postSuccess(_('Der Termin wurde gespeichert.'));
$this->relocate('calendar/group/' . $this->last_view, ['atime' => $this->atime]);
}
} else {
$this->createSidebar('edit', $this->calendar);
$this->createSidebarFilter();
$this->render_template('calendar/single/edit', $this->layout);
}
}
public function day_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
// get group and the calendars of the members
// the first calendar is the calendar of the actual user
$this->calendars[0] = SingleCalendar::getDayCalendar(
$GLOBALS['user']->id, $this->atime);
$group = $this->getGroup($this->calendars[0]);
foreach ($group->members as $member) {
$calendar = new SingleCalendar($member->user_id);
if ($calendar->havePermission(Calendar::PERMISSION_READABLE)) {
$this->calendars[] = SingleCalendar::getDayCalendar($calendar,
$this->atime, null, $this->restrictions);
}
}
PageLayout::setTitle($this->getTitle($group)
. ' - ' . _('Tagesansicht'));
Navigation::activateItem('/calendar/calendar');
$this->last_view = 'day';
$this->createSidebar('day');
$this->createSidebarFilter();
}
/**
* Returns the Statusgruppe for the given calendar.
*
* @param SingleCalendar The calendar of the group owner.
* @return Statusgruppen The found group.
* @throws AccessDeniedException If the group does not exists or the owner
* of the calendar is not the owner of the group.
*/
private function getGroup($calendar)
{
$group = Statusgruppen::find($this->range_id);
if (!$group) {
throw new AccessDeniedException();
}
// is the user the owner of this group
if ($group->range_id != $calendar->getRangeId()) {
// not the owner...
throw new AccessDeniedException();
}
return $group;
}
public function week_action($range_id = null)
{
$this->calendars = [];
$this->range_id = $range_id ?: $this->range_id;
$timestamp = mktime(12, 0, 0, date('n', $this->atime),
date('j', $this->atime), date('Y', $this->atime));
$monday = $timestamp - 86400 * (strftime('%u', $timestamp) - 1);
$day_count = $this->settings['type_week'] == 'SHORT' ? 5 : 7;
// one calendar for each day for the actual user
for ($i = 0; $i < $day_count; $i++) {
// one calendar holds the events of one day
$this->calendars[0][$i] =
SingleCalendar::getDayCalendar($GLOBALS['user']->id,
$monday + $i * 86400, null, $this->restrictions);
}
// check and get the group
$group = $this->getGroup($this->calendars[0][0]);
$n = 1;
foreach ($group->members as $member) {
$calendar = new SingleCalendar($member->user_id);
if ($calendar->havePermission(Calendar::PERMISSION_READABLE)) {
for ($i = 0; $i < $day_count; $i++) {
$this->calendars[$n][$i] =
SingleCalendar::getDayCalendar($member->user_id,
$monday + $i * 86400, null, $this->restrictions);
}
$n++;
}
}
PageLayout::setTitle($this->getTitle($group)
. ' - ' . _('Wochenansicht'));
Navigation::activateItem('/calendar/calendar');
$this->last_view = 'week';
$this->createSidebar('week');
$this->createSidebarFilter();
}
public function month_action($range_id = null)
{
$this->calendars = [];
$this->range_id = $range_id ?: $this->range_id;
$month_start = mktime(12, 0, 0, date('n', $this->atime), 1, date('Y', $this->atime));
$month_end = mktime(12, 0, 0, date('n', $this->atime), date('t', $this->atime), date('Y', $this->atime));
$adow = strftime('%u', $month_start) - 1;
$cor = date('n', $this->atime) == 3 ? 1 : 0;
$this->first_day = $month_start - $adow * 86400;
$this->last_day = ((42 - ($adow + date('t', $this->atime))) % 7 + $cor) * 86400 + $month_end;
// one calendar each day for the actual user
for ($start_day = $this->first_day; $start_day <= $this->last_day; $start_day += 86400) {
$this->calendars[0][] = SingleCalendar::getDayCalendar(
$GLOBALS['user']->id, $start_day, null, $this->restrictions);
}
// check and get the group
$group = $this->getGroup($this->calendars[0][0]);
$n = 1;
// get the calendars of the group members
foreach ($group->members as $member) {
$calendar = new SingleCalendar($member->user_id);
if ($calendar->havePermission(Calendar::PERMISSION_READABLE)) {
for ($start_day = $this->first_day; $start_day <= $this->last_day; $start_day += 86400) {
$this->calendars[$n][] =
SingleCalendar::getDayCalendar($member->user_id,
$start_day, null, $this->restrictions);
}
$n++;
}
}
PageLayout::setTitle($this->getTitle($group)
. ' - ' . _('Monatssicht'));
Navigation::activateItem('/calendar/calendar');
$this->last_view = 'month';
$this->createSidebar('month');
$this->createSidebarFilter();
}
public function year_action($range_id = null)
{
$this->calendars = [];
$this->count_lists = [];
$this->range_id = $range_id ?: $this->range_id;
$start = mktime(0, 0, 0, 1, 1, date('Y', $this->atime));
$end = mktime(23, 59, 59, 12, 31, date('Y', $this->atime));
$this->calendars[0] = new SingleCalendar(
$GLOBALS['user']->id, $start, $end);
$this->count_lists[0] = $this->calendars[0]->getListCountEvents();
// check and get the group
$group = $this->getGroup($this->calendars[0]);
$n = 1;
// get the calendars of the group members
foreach ($group->members as $member) {
$calendar = new SingleCalendar($member->user_id);
if ($calendar->havePermission(Calendar::PERMISSION_READABLE)) {
$this->calendars[$n] = $calendar->setStart($start)->setEnd($end);
$this->count_lists[$n] = $this->calendars[$n]->getListCountEvents();
$n++;
}
}
PageLayout::setTitle($this->getTitle($group)
. ' - ' . _('Jahresansicht'));
Navigation::activateItem("/calendar/calendar");
$this->last_view = 'year';
$this->createSidebar('year');
$this->createSidebarFilter();
}
}
<?php
# Lifter010: TODO
/**
* This controller displays an institute-calendar for seminars
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* @author Till Glöggler <tgloeggl@uos.de>
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
* @since 2.0
*/
class Calendar_InstscheduleController extends AuthenticatedController
{
/**
* this action is the main action of the schedule-controller, setting the environment for the timetable,
* accepting a comma-separated list of days.
*
* @param string $days a list of an arbitrary mix of the numbers 0-6, separated with a comma (e.g. 1,2,3,4,5 (for Monday to Friday, the default))
*/
function index_action($days = false)
{
if ($GLOBALS['perm']->have_perm('admin')) {
$inst_mode = true;
}
$my_schedule_settings = $GLOBALS['user']->cfg->SCHEDULE_SETTINGS;
// set the days to be displayed
if ($days === false) {
if (Request::getArray('days')) {
$this->days = array_keys(Request::getArray('days'));
} else {
$this->days = CalendarScheduleModel::getDisplayedDays($my_schedule_settings['glb_days']);
}
} else {
$this->days = explode(',', $days);
}
// try to find the correct institute-id
$institute_id = Request::option('institute_id', Context::getId());
if (!$institute_id) {
$institute_id = $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT;
}
if (!$institute_id || (in_array(get_object_type($institute_id), words('inst fak')) === false)) {
throw new Exception(sprintf(_('Kann Einrichtungskalendar nicht anzeigen!'
. 'Es wurde eine ungültige Instituts-Id übergeben (%s)!', $institute_id)));
}
// load semester-data and current semester
$this->semesters = Semester::findAllVisible(false);
if (Request::option('semester_id')) {
$this->current_semester = Semester::find(Request::option('semester_id'));
} else {
$this->current_semester = Semester::findCurrent();
}
$this->entries = (array)CalendarInstscheduleModel::getInstituteEntries($GLOBALS['user']->id,
$this->current_semester, 8, 20, $institute_id, $this->days);
Navigation::activateItem('/course/main/schedule');
PageLayout::setHelpKeyword('Basis.TerminkalenderStundenplan');
PageLayout::setTitle(Context::getHeaderLine().' - '._('Veranstaltungs-Stundenplan'));
$zoom = Request::int('zoom', 0);
$this->controller = $this;
$this->calendar_view = new CalendarWeekView($this->entries, 'instschedule');
$this->calendar_view->setHeight(40 + (20 * $zoom));
$this->calendar_view->setRange($my_schedule_settings['glb_start_time'], $my_schedule_settings['glb_end_time']);
$this->calendar_view->groupEntries(); // if enabled, group entries with same start- and end-date
URLHelper::addLinkParam('zoom', $zoom);
URLHelper::addLinkParam('semester_id', $this->current_semester['semester_id']);
$style_parameters = [
'whole_height' => $this->calendar_view->getOverallHeight(),
'entry_height' => $this->calendar_view->getHeight()
];
$factory = new Flexi_TemplateFactory($this->dispatcher->trails_root . '/views');
PageLayout::addStyle($factory->render('calendar/stylesheet', $style_parameters));
if (Request::option('printview')) {
PageLayout::addStylesheet('print.css');
// remove all stylesheets that are not used for printing to have a more reasonable printing preview
PageLayout::addHeadElement('script', [], "$('head link[media=screen]').remove();");
} else {
PageLayout::addStylesheet('print.css', ['media' => 'print']);
}
Helpbar::Get()->addPlainText(_('Information'), _('Der Stundenplan zeigt die regelmäßigen Veranstaltungen dieser Einrichtung.'), Icon::create('info'));
$views = new ViewsWidget();
$views->addLink(_('klein'), URLHelper::getURL('', ['zoom' => 0]))->setActive($zoom == 0);
$views->addLink(_('mittel'), URLHelper::getURL('', ['zoom' => 2]))->setActive($zoom == 2);
$views->addLink(_('groß'), URLHelper::getURL('', ['zoom' => 4]))->setActive($zoom == 4);
$views->addLink(_('extra groß'), URLHelper::getURL('', ['zoom' => 7]))->setActive($zoom == 7);
Sidebar::Get()->addWidget($views);
$actions = new ActionsWidget();
$actions->addLink(_('Druckansicht'),
$this->url_for('calendar/instschedule/index/'. implode(',', $this->days),
['printview' => 'true',
'semester_id' => $this->current_semester['semester_id']]),
Icon::create('print'),
['target' => '_blank']);
// Only admins should have the ability to change their schedule settings here - they have no other schedule
if ($GLOBALS['perm']->have_perm('admin')) {
$actions->addLink(_("Darstellung ändern"),
$this->url_for('calendar/schedule/settings'),
Icon::create('admin'),
['data-dialog' => '']
);
// only show this setting if we have indeed a faculty where children might exist
if (Context::get()->isFaculty()) {
if ($GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN) {
$actions->addLink(_("Untergeordnete Institute ignorieren"),
$this->url_for('calendar/instschedule/include_children/0'),
Icon::create('checkbox-checked')
);
} else {
$actions->addLink(_("Untergeordnete Institute einbeziehen"),
$this->url_for('calendar/instschedule/include_children/1'),
Icon::create('checkbox-unchecked')
);
}
}
}
Sidebar::Get()->addWidget($actions);
$semesterSelector = new SemesterSelectorWidget($this->url_for('calendar/instschedule'), 'semester_id', 'post');
$semesterSelector->includeAll(false);
Sidebar::Get()->addWidget($semesterSelector);
}
/**
* Returns an HTML fragment of a grouped entry in the schedule of an institute.
*
* @param string $start the start time of the group, e.g. "1000"
* @param string $end the end time of the group, e.g. "1200"
* @param string $seminars the IDs of the courses
* @param string $day numeric day to show
*
* @return void
*/
function groupedentry_action($start, $end, $seminars, $day)
{
$this->response->add_header('Content-Type', 'text/html; charset=utf-8');
// strucutre of an id: seminar_id-cycle_id
// we do not need the cycle id here, so we trash it.
$seminar_list = [];
foreach (explode(',', $seminars) as $seminar) {
$zw = explode('-', $seminar);
$this->seminars[$zw[0]] = Seminar::getInstance($zw[0]);
}
$this->start = mb_substr($start, 0, 2) .':'. mb_substr($start, 2, 2);
$this->end = mb_substr($end, 0, 2) .':'. mb_substr($end, 2, 2);
$day_names = [_("Montag"),_("Dienstag"),_("Mittwoch"),
_("Donnerstag"),_("Freitag"),_("Samstag"),_("Sonntag")];
$this->day = $day_names[(int)$day];
$this->render_template('calendar/instschedule/_entry_details');
}
/**
* Toggle config setting to include children in schedule for the current faculty
*
* @param int $include_childs 0 / false to exclude children 1 / true to include them
*/
function include_children_action($include_childs)
{
$GLOBALS['user']->cfg->store('MY_INSTITUTES_INCLUDE_CHILDREN', $include_childs ? 1 : 0);
$this->redirect('calendar/instschedule/index');
}
}
<?php
# Lifter010: TODO
/**
* This class displays a seminar-schedule for
* users on a seminar-based view and for admins on an institute based view
* schedule.php - Calender schedule controller
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* @author Till Glöggler <tgloeggl@uos.de>
* @author Moritz Strohm <strohm@data-quest.de>
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
* @since 2.0
* @package calender
* @since 6.0
*/
// Needs to be required due to the use of constants
require_once 'lib/classes/calendar/CalendarScheduleModel.php';
require_once 'lib/dates.inc.php';
class Calendar_ScheduleController extends AuthenticatedController
{
/**
* Callback function being called before an action is executed. If this
* function does not return FALSE, the action will be called, otherwise
* an error will be generated and processing will be aborted. If this function
* already #rendered or #redirected, further processing of the action is
* withheld.
*
* @param string Name of the action to perform.
* @param array An array of arguments to the action.
*
*/
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
$zoom = Request::int('zoom');
$this->my_schedule_settings = UserConfig::get($GLOBALS['user']->id)->SCHEDULE_SETTINGS;
// bind zoom, show_hidden and semester_id for all actions, even preserving them after redirect
if (isset($zoom)) {
URLHelper::addLinkParam('zoom', Request::int('zoom'));
$this->my_schedule_settings['zoom'] = Request::int('zoom');
UserConfig::get($GLOBALS['user']->id)->store('SCHEDULE_SETTINGS', $this->my_schedule_settings);
}
URLHelper::bindLinkParam('semester_id', $this->current_semester['semester_id']);
URLHelper::bindLinkParam('show_hidden', $this->show_hidden);
PageLayout::setHelpKeyword('Basis.MyStudIPStundenplan');
PageLayout::setTitle(_('Mein Stundenplan'));
if (!Context::isCourse() && Navigation::hasItem('/calendar')) {
Navigation::activateItem('/calendar');
}
/**
* this action is the main action of the schedule-controller, setting the environment
* for the timetable, accepting a comma-separated list of days.
*
* @param string $days a list of an arbitrary mix of the numbers 0-6, separated
* with a comma (e.g. 1,2,3,4,5 (for Monday to Friday, the default))
*/
public function index_action($days = false)
{
$schedule_settings = CalendarScheduleModel::getScheduleSettings();
$inst_mode = false;
$institute_id = null;
if ($GLOBALS['perm']->have_perm('admin')) {
$inst_mode = true;
}
if ($inst_mode) {
// try to find the correct institute-id
$institute_id = Request::option('institute_id', Context::getId());
if (!$institute_id) {
$institute_id = UserConfig::get($GLOBALS['user']->id)->MY_INSTITUTES_DEFAULT;
}
if (!$institute_id || !in_array(get_object_type($institute_id), ['fak', 'inst'])) {
throw new Exception('Cannot display institute-calender. No valid ID given!');
}
Navigation::activateItem('/browse/my_courses/schedule');
} else {
Navigation::activateItem('/calendar/schedule');
}
// check, if the hidden seminar-entries shall be shown
$show_hidden = Request::int('show_hidden', 0);
// load semester-data and current semester
$this->semesters = array_reverse(Semester::findAllVisible(false));
if (Request::option('semester_id')) {
$this->current_semester = Semester::find(Request::option('semester_id'));
$schedule_settings['semester_id'] = Request::option('semester_id');
UserConfig::get($GLOBALS['user']->id)->store('SCHEDULE_SETTINGS',
$schedule_settings);
} else {
$this->current_semester = !empty($schedule_settings['semester_id']) ?
Semester::find($schedule_settings['semester_id']) :
Semester::findCurrent();
}
public function index_action()
{
PageLayout::setTitle(_('Stundenplan'));
// check type-safe if days is false otherwise sunday (0) cannot be chosen
if ($days === false) {
if (Request::getArray('days')) {
$this->days = array_keys(Request::getArray('days'));
} else {
$this->days = CalendarScheduleModel::getDisplayedDays($schedule_settings['glb_days']);
}
} else {
$this->days = explode(',', $days);
if (Navigation::hasItem('/calendar/schedule')) {
Navigation::activateItem('/calendar/schedule');
}
$this->controller = $this;
$show_hidden = Request::bool('show_hidden', false);
$this->calendar_view = $inst_mode
? CalendarScheduleModel::getInstCalendarView($institute_id, $show_hidden, $this->current_semester, $this->days)
: CalendarScheduleModel::getUserCalendarView($GLOBALS['user']->id, $show_hidden, $this->current_semester, $this->days);;
//Handle the selected semester and create a Fullcalendar instance.
// have we chosen an entry to display?
if (!empty($this->flash['entry'])) {
if ($inst_mode) {
$this->show_entry = $this->flash['entry'];
} else if ($this->flash['entry']['id'] == null) {
$this->show_entry = $this->flash['entry'];
} else {
foreach ($this->calendar_view->getColumns() as $entry_days) {
foreach ($entry_days->getEntries() as $entry) {
if ($this->flash['entry']['cycle_id']) {
if ($this->flash['entry']['id'] . '-' . $this->flash['entry']['cycle_id'] == $entry['id']) {
$this->show_entry = $entry;
$entry_ids = explode('-', $this->show_entry['id']);
$this->show_entry['id'] = reset($entry_ids);
}
} else {
if ($entry['id'] == $this->flash['entry']['id']) {
$this->show_entry = $entry;
}
}
$this->semester = null;
if (Request::submitted('semester_id')) {
$this->semester = Semester::find(Request::option('semester_id'));
if ($this->semester) {
//Store the new semester-ID in the session:
$_SESSION['schedule_semester_id'] = $this->semester->id;
}
}
if (!$this->semester) {
//Load the semester from the session:
$semester_id = $_SESSION['schedule_semester_id'] ?? '';
if ($semester_id) {
$this->semester = Semester::find($semester_id);
} else {
$this->semester = Semester::findCurrent();
}
}
$style_parameters = [
'whole_height' => $this->calendar_view->getOverallHeight(),
'entry_height' => $this->calendar_view->getHeight()
];
if ($this->semester) {
PageLayout::setTitle(
studip_interpolate(
_('Stundenplan %{semester}'),
['semester' => $this->semester->name]
)
);
}
$factory = new Flexi_TemplateFactory($this->dispatcher->trails_root . '/views');
PageLayout::addStyle($factory->render('calendar/stylesheet', $style_parameters), 'screen, print');
//Build the sidebar:
if (Request::option('printview')) {
$this->calendar_view->setReadOnly();
PageLayout::addStylesheet('print.css');
$sidebar = Sidebar::get();
// remove all stylesheets that are not used for printing to have a more reasonable printing preview
PageLayout::addHeadElement('script', [], "$('head link[media=screen]').remove();");
//Add the semester selector widget first:
$semester_widget = new SemesterSelectorWidget(
$this->indexURL(['show_hidden' => $show_hidden ?: null])
);
$semester_widget->setSelection($this->semester->id ?? '');
$sidebar->addWidget($semester_widget);
//Then add the actions for the action widget:
$actions = new ActionsWidget();
$actions->addLink(
_('Neuer Termin'),
$this->url_for('calendar/schedule/entry/add'),
Icon::create('add'),
['data-dialog' => 'size=auto']
);
if ($show_hidden) {
$actions->addLink(
_('Ausgeblendete Veranstaltungen verstecken'),
$this->indexURL(['semester_id' => Request::get('semester_id')]),
Icon::create('visibility-visible')
)->asButton();
} else {
PageLayout::addStylesheet('print.css', ['media' => 'print']);
}
$this->show_hidden = $show_hidden;
$inst = get_object_name($institute_id, 'inst');
$this->inst_mode = $inst_mode;
$this->institute_name = $inst['name'];
$this->institute_id = $institute_id;
$this->show_settings = Request::bool('show_settings', false);
$actions->addLink(
_('Ausgeblendete Veranstaltungen anzeigen'),
$this->indexURL([
'show_hidden' => true,
'semester_id' => Request::get('semester_id'),
]),
Icon::create('visibility-invisible')
)->asButton();
}
$actions->addLink(
_('Drucken'),
'#',
Icon::create('print'),
['onclick' => 'window.print(); return false;']
);
$actions->addLink(
_('Einstellungen'),
$this->url_for('calendar/schedule/settings'),
Icon::create('settings'),
['data-dialog' => 'size=auto;reload-on-close']
);
$sidebar->addWidget($actions);
$schedule_settings = UserConfig::get(User::findCurrent()->id)->getValue('SCHEDULE_SETTINGS');
$size = $schedule_settings['size'] ?? 'medium';
if (Request::submitted('size')) {
$size = Request::option('size');
if (in_array($size, ['small', 'medium', 'large'])) {
//Set the new size in the schedule settings:
$schedule_settings['size'] = $size;
UserConfig::get(User::findCurrent()->id)->store('SCHEDULE_SETTINGS', $schedule_settings);
} else {
$size = 'medium';
}
}
$views = new ViewsWidget();
$views->setTitle(_('Größe'));
$views->addLink(
_('Klein'),
$this->url_for('calendar/schedule/index', ['size' => 'small'])
)->setActive($size === 'small');
$views->addLink(
_('Mittel'),
$this->url_for('calendar/schedule/index', ['size' => 'medium'])
)->setActive($size === 'medium');
$views->addLink(
_('Groß'),
$this->url_for('calendar/schedule/index', ['size' => 'large'])
)->setActive($size === 'large');
$sidebar->addWidget($views);
$fullcalendar = \Studip\Calendar\Helper::getScheduleFullcalendar(
$this->semester->id ?? '',
Request::bool('show_hidden', false)
);
$fullcalendar->setResponsiveDefaultView('timeGridDay');
$this->fullcalendar = $fullcalendar->render();
}
public function new_entry_action()
public function data_action()
{
$this->layout = null;
//Fullcalendar sets the week time range in which to put the course dates
//of the semester. Therefore, start and end are handled in here.
$begin = Request::getDateTime('start', \DateTime::RFC3339);
$end = Request::getDateTime('end', \DateTime::RFC3339);
if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) {
//No time range specified.
throw new InvalidArgumentException('Invalid parameters!');
}
$result = [];
$semester_id = Request::option('semester_id');
$semester = Semester::find($semester_id);
$show_hidden = Request::bool('show_hidden', false);
if ($semester) {
//Get all regular course dates for that semester:
$cycle_dates = SeminarCycleDate::findBySql(
'JOIN `termine` USING (`metadate_id`)
JOIN `seminare` USING (`seminar_id`)
WHERE
`seminar_id` IN (
SELECT `seminar_id` FROM `seminar_user`
WHERE `user_id` = :user_id
UNION
SELECT `course_id` FROM `schedule_courses`
WHERE `user_id` = :user_id
)
AND
(
`termine`.`date` BETWEEN :begin AND :end
OR `termine`.`end_time` BETWEEN :begin AND :end
)
GROUP BY `metadate_id`',
[
'user_id' => $GLOBALS['user']->id,
'begin' => $semester->beginn,
'end' => $semester->ende
]
);
if (!Request::isXhr()) {
$this->render_nothing();
}
}
foreach ($cycle_dates as $cycle_date) {
//Calculate a fake begin and end that lies in the week
//fullcalendar has specified.
$fake_begin = clone $begin;
$fake_end = clone $begin;
if ($cycle_date->weekday > 1) {
$fake_begin = $fake_begin->add(new DateInterval('P' . ($cycle_date->weekday - 1) . 'D'));
$fake_end = $fake_end->add(new DateInterval('P' . ($cycle_date->weekday - 1) . 'D'));
}
$start_time_parts = explode(':', $cycle_date->start_time);
$end_time_parts = explode(':', $cycle_date->end_time);
$fake_begin->setTime(
$start_time_parts[0],
$start_time_parts[1],
$start_time_parts[2]
);
$fake_end->setTime(
$end_time_parts[0],
$end_time_parts[1],
$end_time_parts[2]
);
/**
* this action is called whenever a new entry shall be modified or added to the schedule
*
* @param string $id optional, if id given, the entry with this id is updated
*/
public function addEntry_action($id = null)
{
if ($id) {
$data['id'] = $id;
}
$schedule_course = ScheduleCourseDate::findOneBySQL(
'`metadate_id` = :cycle_date_id AND `user_id` = :user_id',
[
'cycle_date_id' => $cycle_date->id,
'user_id' => $GLOBALS['user']->id
]
);
$is_hidden = $schedule_course && !$schedule_course->visible;
if (!$show_hidden && $is_hidden) {
//The regular date belongs to a course that has been hidden in the schedule.
//The flag to include hidden courses is not set which means that the regular
//date shall not be included.
continue;
}
//Get the course colour:
$course_membership = CourseMember::findOneBySQL(
'`seminar_id` = :course_id AND `user_id` = :user_id',
[
'course_id' => $cycle_date->seminar_id,
'user_id' => $GLOBALS['user']->id
]
);
$error = false;
$data['start'] = (int)str_replace(':', '', Request::get('entry_start'));
$data['end'] = (int)str_replace(':', '', Request::get('entry_end'));
$data['day'] = Request::int('entry_day');
$event_classes = ['schedule', 'course'];
$event_title = $cycle_date->course->getFullName('number-name');
if ($data['start'] >= $data['end']
|| !Request::int('entry_day')
|| !$this->validate_datetime(Request::get('entry_start'))
|| !$this->validate_datetime(Request::get('entry_end'))) {
$error = true;
}
if ($course_membership) {
$event_classes[] = sprintf('course-color-%u', $course_membership->gruppe);
if ($error) {
PageLayout::postError(
_('Eintrag konnte nicht gespeichert werden, da die Start- und/oder Endzeit ungültig ist!')
$lecturer_names = array_map(
fn($lecturer) => $lecturer->user->nachname,
CourseMember::findByCourseAndStatus($course_membership->seminar_id, 'dozent')
);
sort($lecturer_names);
$event_title = studip_interpolate(
'%{course_name} (%{lecturer_names})',
[
'course_name' => $cycle_date->course->getFullName(),
'lecturer_names' => implode(', ', $lecturer_names)
]
);
} elseif ($schedule_course) {
$event_classes[] = 'marked-course';
$event_title = studip_interpolate(
_('%{course_name} (vorgemerkt)'),
['course_name' => $cycle_date->course->getFullName()]
);
} else {
$data['title'] = Request::get('entry_title');
$data['content'] = Request::get('entry_content');
$data['user_id'] = $GLOBALS['user']->id;
if (Request::get('entry_color')) {
$data['color'] = Request::get('entry_color');
} else {
$data['color'] = DEFAULT_COLOR_NEW;
}
// Add the room, if available:
$room_name = $cycle_date->getMostBookedRoom()?->getFullName()
?? $cycle_date->getMostUsedFreetextRoomName();
if ($room_name) {
$event_title .= "\n" . $room_name;
}
$event_icon = 'seminar';
if ($schedule_course && !$course_membership) {
$event_icon = 'tag';
} elseif ($show_hidden && $is_hidden) {
$event_icon = 'visibility-invisible';
$event_classes[] = 'hidden-course';
}
$event = new \Studip\Calendar\EventData(
$fake_begin,
$fake_end,
$event_title,
$event_classes,
'',
'',
false,
'SeminarCycleDate',
$cycle_date->id,
'',
'',
'course',
$cycle_date->seminar_id,
[
'show' => $this->url_for('calendar/schedule/course_info/' . $cycle_date->id)
],
[],
Icon::create($event_icon ?: '', Icon::ROLE_INFO)->asImagePath()
);
CalendarScheduleModel::storeEntry($data);
$result[] = $event->toFullcalendarEvent();
}
}
$this->redirect('calendar/schedule');
//Add all schedule entries to the result set:
$weekly_dates = ScheduleEntry::findByUser_id($GLOBALS['user']->id);
foreach ($weekly_dates as $date) {
$event_data = $date->toEventData($GLOBALS['user']->id);
//Disable fullcalendar drag & drop actions:
$event_data->editable = false;
$result[] = $event_data->toFullcalendarEvent();
}
$this->render_json($result);
}
/**
* this action keeps the entry of the submitted_id and enables displaying of the entry-dialog.
* If no id is submitted, an empty entry_dialog is displayed.
* This action handles adding and editing schedule entries.
*
* @param string $id the id of the entry to edit (if any), false otherwise.
* @param string $cycle_id an optional cycle's ID
* @param string $entry_id The ID of the entry to be modified. In case the ID is set to "add", a new entry
* will be created. In all other cases, an existing entry will be loaded.
*/
public function entry_action($id = null, $cycle_id = null)
public function entry_action(string $entry_id)
{
if (Request::isXhr()) {
$this->response->add_header('Content-Type', 'text/html; charset=utf-8');
$this->layout = null;
$this->entry = [
'id' => $id,
'cycle_id' => $cycle_id
];
if ($cycle_id) {
$seminar_ids = CalendarScheduleModel::getSeminarEntry($id, $GLOBALS['user']->id, $cycle_id);
$this->show_entry = array_pop($seminar_ids);
$this->show_entry['id'] = $id;
$this->render_template('calendar/schedule/_entry_course');
} else if ($id) {
$entry_columns = CalendarScheduleModel::getScheduleEntries($GLOBALS['user']->id, 0, 0, $id);
$entries = [];
$entry_columns = array_pop($entry_columns);
if ($entry_columns) {
$entries = $entry_columns->getEntries();
}
$this->show_entry = array_pop($entries);
$this->render_template('calendar/schedule/_entry_schedule');
$this->entry = null;
if ($entry_id === 'add') {
//Add mode
$this->entry = new ScheduleEntry();
$this->entry->user_id = $GLOBALS['user']->id;
if (!Request::submitted('save')) {
//Provide good default values:
$this->entry->colour_id = 1;
if (Request::submitted('start')) {
//String format
$this->entry->dow = Request::int('dow',date('N'));
$this->entry->setFormattedStart(Request::get('start', date('H:00', strtotime('+1 hour'))));
$this->entry->setFormattedEnd(Request::get('end', date('H:00', strtotime('+2 hours'))));
} elseif (Request::submitted('begin')) {
//Fullcalendar: Timestamps
$begin = Request::get('begin');
$end = Request::get('end');
if ($begin && $end) {
$this->entry->dow = intval(date('N', $begin));
$this->entry->setFormattedStart(date('H:i', $begin));
$this->entry->setFormattedEnd(date('H:i', $end));
}
} else {
$this->flash['entry'] = [
'id' => $id,
'cycle_id' => $cycle_id
];
$begin = time() + 3600;
$end = $begin + 3600;
$this->entry->dow = intval(date('N', $begin));
$this->entry->setFormattedStart(date('H:00', $begin));
$this->entry->setFormattedEnd(date('H:00', $end));
}
}
PageLayout::setTitle(_('Neuer Termin'));
} else {
//Edit mode
$this->entry = ScheduleEntry::find($entry_id);
if (!$this->entry) {
PageLayout::postError(_('Der Termin wurde nicht gefunden.'));
}
if (!$this->entry->isWritable($GLOBALS['user']->id)) {
throw new AccessDeniedException(_('Sie dürfen diesen Termin nicht bearbeiten!'));
}
PageLayout::setTitle($this->entry->toString());
}
$this->redirect('calendar/schedule/');
if (Request::submitted('save')) {
CSRFProtection::verifyUnsafeRequest();
$this->saveEntry($entry_id);
} elseif (Request::submitted('delete')) {
CSRFProtection::verifyUnsafeRequest();
$this->deleteEntry();
}
}
/**
* Return an HTML fragment containing a form to edit an entry
*
* @param string the ID of a course
* @param string an optional cycle's ID
* @return void
* Handles storing a schedule entry.
*/
public function entryajax_action($id, $cycle_id = null)
public function save_entry_action(string $entry_id)
{
$this->response->add_header('Content-Type', 'text/html; charset=utf-8');
if ($cycle_id) {
$seminar_ids = CalendarScheduleModel::getSeminarEntry($id, $GLOBALS['user']->id, $cycle_id);
$this->show_entry = array_pop($seminar_ids);
$this->show_entry['id'] = $id;
$this->render_template('calendar/schedule/_entry_course');
$this->entry = null;
if ($entry_id === 'add') {
//Add mode
$this->entry = new ScheduleEntry();
$this->entry->user_id = $GLOBALS['user']->id;
PageLayout::setTitle(_('Neuer Termin'));
} else {
$entry_columns = CalendarScheduleModel::getScheduleEntries($GLOBALS['user']->id, 0, 0, $id);
$entries = array_pop($entry_columns)->getEntries();
$this->show_entry = array_pop($entries);
$this->render_template('calendar/schedule/_entry_schedule');
//Edit mode
$this->entry = ScheduleEntry::find($entry_id);
if (!$this->entry) {
PageLayout::postError(_('Der Termin wurde nicht gefunden.'));
}
if (!$this->entry->isWritable($GLOBALS['user']->id)) {
throw new AccessDeniedException(_('Sie dürfen diesen Termin nicht bearbeiten!'));
}
PageLayout::setTitle($this->entry->toString());
}
/**
* Returns an HTML fragment of a grouped entry in the schedule of an institute.
*
* @param string $start the start time of the group, e.g. "1000"
* @param string $end the end time of the group, e.g. "1200"
* @param string $seminars the IDs of the courses
* @param string $day numeric day to show
*
* @return void
*/
public function groupedentry_action($start, $end, $seminars, $day)
{
$this->response->add_header('Content-Type', 'text/html; charset=utf-8');
$seminars = explode(',', $seminars);
foreach ($seminars as $seminar) {
$zw = explode('-', $seminar);
$this->seminars[$zw[0]] = Seminar::getInstance($zw[0]);
}
$this->timespan = mb_substr($start, 0, 2) . ':' . mb_substr($start, 2, 2)
. ' - ' . mb_substr($end, 0, 2) . ':' . mb_substr($end, 2, 2);
$this->start = $start;
$this->end = $end;
$day_names = [
_('Montag'),
_('Dienstag'),
_('Mittwoch'),
_('Donnerstag'),
_('Freitag'),
_('Samstag'),
_('Sonntag')
];
$this->day = (int)$day;
$this->day_name = $day_names[$this->day];
$this->entry->dow = Request::int('dow', date('N'));
$this->entry->setFormattedStart(Request::get('start'));
$this->entry->setFormattedEnd(Request::get('end'));
$this->entry->colour_id = Request::get('colour_id') ?? '';
$this->entry->label = Request::get('label', '');
$this->entry->content = Request::get('content', '');
if ($this->entry->start_time >= $this->entry->end_time) {
PageLayout::postError(_('Der Startzeitpunkt darf nicht nach dem Endzeitpunkt liegen!'));
$this->redirect('calendar/schedule/entry/' . $entry_id);
return;
}
$this->render_template('calendar/schedule/_entry_inst');
if ($this->entry->store() !== false) {
if ($entry_id === 'add') {
PageLayout::postSuccess(_('Der Termin wurde hinzugefügt.'));
} else {
PageLayout::postSuccess(_('Der Termin wurde bearbeitet.'));
}
if (Request::isDialog()) {
$this->response->add_header('X-Dialog-Close', '1');
} else {
$this->redirect('calendar/schedule/index');
}
} else {
if ($entry_id === 'add') {
PageLayout::postError(_('Der Termin konnte nicht hinzugefügt werden.'));
} else {
PageLayout::postError(_('Der Termin konnte nicht bearbeitet werden.'));
}
$this->redirect('calendar/schedule/entry/' . $entry_id);
}
$this->render_nothing();
}
/**
* delete the entry of the submitted id (only entry belonging to the current
* use can be deleted)
*
* @param string $id the id of the entry to delete
* @return void
* Handles deleting a schedule entry.
*/
public function delete_action($id)
public function delete_entry_action(string $entry_id)
{
CalendarScheduleModel::deleteEntry($id);
$this->redirect('calendar/schedule');
CSRFProtection::verifyUnsafeRequest();
$this->entry = ScheduleEntry::find($entry_id);
if (!$this->entry) {
PageLayout::postError(_('Der Termin wurde nicht gefunden.'));
}
if (!$this->entry->isWritable($GLOBALS['user']->id)) {
throw new AccessDeniedException(_('Sie dürfen diesen Termin nicht bearbeiten!'));
}
if ($this->entry->delete()) {
PageLayout::postSuccess(_('Der Termin wurde gelöscht.'));
} else {
PageLayout::postError(_('Der Termin konnte nicht gelöscht werden.'));
}
if (Request::isDialog()) {
$this->response->add_header('X-Dialog-Close', '1');
} else {
$this->redirect('calendar/schedule/index');
}
$this->render_nothing();
}
/**
* store the color-settings for the seminar
* Displays information about a course in the schedule.
*
* @param string $seminar_id
* @param string $cycle_id
* @return void
* @param string $cycle_date_id The ID of the cycle date of the course.
*/
public function editseminar_action($seminar_id, $cycle_id)
public function course_info_action(string $cycle_date_id)
{
$data = [
'id' => $seminar_id,
'cycle_id' => $cycle_id,
'color' => Request::get('entry_color')
];
CalendarScheduleModel::storeSeminarEntry($data);
$this->cycle_date = SeminarCycleDate::find($cycle_date_id);
if (!$this->cycle_date) {
PageLayout::postError(_('Der Veranstaltungstermin wurde nicht gefunden.'));
return;
}
$this->course = $this->cycle_date->course ?? null;
if (!$this->course) {
PageLayout::postError(_('Die Veranstaltung wurde nicht gefunden.'));
return;
}
$this->membership = CourseMember::findOneBySQL(
'`seminar_id` = :course_id AND `user_id` = :user_id',
[
'course_id' => $this->course->id,
'user_id' => $GLOBALS['user']->id
]
);
$this->schedule_course_entry = ScheduleCourseDate::findOneBySQL(
'`metadate_id` = :cycle_date_id AND `user_id` = :user_id',
[
'cycle_date_id' => $this->cycle_date->id,
'user_id' => $GLOBALS['user']->id
]
);
$this->redirect('calendar/schedule');
PageLayout::setTitle($this->course->getFullName());
}
/**
* Adds the appointments of a course to your schedule.
* Hides a course in the schedule.
*
* @param string $seminar_id the ID of the course
* @return void
* @param string $cycle_date_id The ID of the cycle date to hide.
*/
public function addvirtual_action($seminar_id)
public function hide_course_action(string $cycle_date_id)
{
$sem = Seminar::getInstance($seminar_id);
foreach ($sem->getCycles() as $cycle) {
$data = [
'id' => $seminar_id,
'cycle_id' => $cycle->getMetaDateId(),
'color' => false
];
CSRFProtection::verifyUnsafeRequest();
$success = false;
$cycle_date = SeminarCycleDate::find($cycle_date_id);
if ($cycle_date) {
$this->membership = CourseMember::findOneBySQL(
'`seminar_id` = :course_id AND `user_id` = :user_id',
[
'course_id' => $cycle_date->seminar_id,
'user_id' => $GLOBALS['user']->id
]
);
CalendarScheduleModel::storeSeminarEntry($data);
//Hide the cycle date.
if ($this->membership) {
//Hide the cycle date in the schedule by creating a new schedule course entry
//with the visibility set to 0:
$entry = ScheduleCourseDate::findOneBySQL(
'`user_id` = :user_id AND `metadate_id` = :cycle_date_id',
['user_id' => $GLOBALS['user']->id, 'cycle_date_id' => $cycle_date->id]
);
if (!$entry) {
$entry = new ScheduleCourseDate();
$entry->user_id = $GLOBALS['user']->id;
$entry->course_id = $cycle_date->seminar_id;
$entry->metadate_id = $cycle_date->id;
}
$entry->visible = false;
$success = $entry->store() !== false;
} else {
//Remove the entry of the marked cycle date from the schedule.
$success = ScheduleCourseDate::deleteBySQL(
'`user_id` = :user_id AND `metadate_id` = :cycle_date_id',
['user_id' => $GLOBALS['user']->id, 'cycle_date_id' => $cycle_date->id]
) > 0;
if (!$success) {
//Variant 2: The whole course has been added to the schedule via the "mark course in schedule"
//action on the course details page. In that case, only one schedule course date exists for
//the whole course instead of having one schedule course date for each regular date.
$success = ScheduleCourseDate::deleteBySQL(
'`user_id` = :user_id AND `course_id` = :course_id',
['user_id' => $GLOBALS['user']->id, 'course_id' => $cycle_date->seminar_id]
) > 0;
}
}
}
if ($success) {
if (Request::isDialog()) {
$this->response->add_header('X-Dialog-Close', '1');
} else {
$this->redirect('calendar/schedule/index');
}
$this->redirect('calendar/schedule');
}
$this->render_nothing();
}
/**
* Set the visibility of the course.
* Makes a hidden course visible again in the schedule.
*
* @param string $seminar_id the ID of the course
* @param string $cycle_id the ID of the cycle
* @param string $visible visibility; either '1' or '0'
* @param string $ajax if you give this optional param, it signals an Ajax request
* @return void
* @param string $cycle_date_id The ID of the cycle date of the course.
*/
public function adminbind_action($seminar_id, $cycle_id, $visible, $ajax = null)
public function show_course_action(string $cycle_date_id)
{
CalendarScheduleModel::adminBind($seminar_id, $cycle_id, $visible);
if (!$ajax) {
$this->redirect('calendar/schedule');
CSRFProtection::verifyUnsafeRequest();
$success = false;
$cycle_date = SeminarCycleDate::find($cycle_date_id);
if ($cycle_date) {
//Make a hidden cycle date visible again.
$entry = ScheduleCourseDate::findOneBySQL(
'`user_id` = :user_id AND `metadate_id` = :cycle_date_id',
['user_id' => $GLOBALS['user']->id, 'cycle_date_id' => $cycle_date->id]
);
if ($entry) {
$entry->visible = true;
$success = $entry->store() !== false;
} else {
$this->render_nothing();
$success = true;
}
//In case no entry exists, the cycle date is not hidden since an entry in schedule_courses
//must exist with its visible set to zero to make a cycle date disappear from the schedule.
}
if ($success) {
if (Request::isDialog()) {
$this->response->add_header('X-Dialog-Close', '1');
} else {
$this->redirect('calendar/schedule/index');
}
}
$this->render_nothing();
}
/**
* Hide the give appointment.
* Saves the data that are specific to displaying a course in the schedule.
* Currently, this means saving only the colour of the course.
*
* @param string $seminar_id the ID of the course
* @param string $cycle_id the ID of the cycle
* @param string $ajax if you give this optional param, it signals an Ajax request
* @return void
* @param string $course_id The ID of the course.
*/
function unbind_action($seminar_id, $cycle_id = null, $ajax = null)
public function save_course_info_action(string $course_id)
{
CalendarScheduleModel::unbind($seminar_id, $cycle_id);
if (!$ajax) {
$this->redirect('calendar/schedule');
CSRFProtection::verifyUnsafeRequest();
$success = false;
$course = Course::find($course_id);
if ($course) {
$this->membership = CourseMember::findOneBySQL(
'`seminar_id` = :course_id AND `user_id` = :user_id',
[
'course_id' => $course->id,
'user_id' => $GLOBALS['user']->id
]
);
if (!$this->membership) {
throw new AccessDeniedException();
}
//Save the selected group.
$selected_groups = Request::getArray('gruppe');
if (array_key_exists($course->id, $selected_groups)) {
$this->membership->gruppe = $selected_groups[$course->id] ?? '0';
}
$success = $this->membership->store() !== false;
}
if ($success) {
PageLayout::postSuccess(_('Die Farbe der Veranstaltung wurde geändert.'));
} else {
$this->render_nothing();
PageLayout::postError(_('Die Farbe der Veranstaltung konnte nicht geändert werden.'));
}
if ($success) {
if (Request::isDialog()) {
$this->response->add_header('X-Dialog-Close', '1');
} else {
$this->redirect('calendar/schedule/index');
}
}
$this->render_nothing();
}
/**
* Show the given appointment.
*
* @param string $seminar_id the ID of the course
* @param string $cycle_id the ID of the cycle
* @param string $ajax if you give this optional param, it signals an Ajax request
* @return void
*/
public function bind_action($seminar_id, $cycle_id, $ajax = null)
public function mark_course_action(string $course_id)
{
CalendarScheduleModel::bind($seminar_id, $cycle_id);
if (!$ajax) {
$this->redirect('calendar/schedule');
$course = Course::find($course_id);
if ($course->isStudygroup()) {
throw new AccessDeniedException();
}
$entry = ScheduleCourseDate::findOneBySQL(
'`course_id` = :course_id AND `user_id` = :user_id',
[
'course_id' => $course_id,
'user_id' => $GLOBALS['user']->id
]
);
if ($entry) {
PageLayout::postInfo(_('Die Veranstaltung wurde bereits zum Stundenplan hinzugefügt.'));
} else {
$this->render_nothing();
$entry = new ScheduleCourseDate();
$entry->course_id = $course->id;
$entry->user_id = $GLOBALS['user']->id;
$entry->metadate_id = '';
$entry->visible = true;
if ($entry->store() !== false) {
PageLayout::postSuccess(_('Die Veranstaltung wurde zum Stundenplan hinzugefügt.'));
} else {
PageLayout::postError(_('Die Veranstaltung konnte nicht zum Stundenplan hinzugefügt werden.'));
}
}
$this->redirect('calendar/schedule/index');
}
/**
* Show the settings' form.
*
* @return void
* Shows the settings dialog for the schedule.
*/
public function settings_action()
{
$this->settings = UserConfig::get($GLOBALS['user']->id)->SCHEDULE_SETTINGS;
$user_config = UserConfig::get(User::findCurrent()->id);
$this->schedule_settings = $user_config->getValue('SCHEDULE_SETTINGS');
//Provide good defaults:
$default_config = [
'start_time' => '08:00',
'end_time' => '20:00',
'visible_days' => [1, 2, 3, 4, 5]
];
if (
empty($this->schedule_settings['start_time'])
&& empty($this->schedule_settings['end_time'])
&& empty($this->schedule_settings['visible_days'])
) {
//Use the defaults:
$this->schedule_settings = $default_config;
}
}
/**
* Store the settings
*
* @param string the start time of the calendar to show, e.g. "1000"
* @param string the end time of the calendar to show, e.g. "1200"
* @param string the days to show
* @param string the ID of the semester
* @return void
* Saves the schedule settings from the settings dialog.
*/
public function storesettings_action($start_hour = false, $end_hour = false, $days = false, $semester_id = false)
public function save_settings_action()
{
if ($start_hour === false) {
$start_hour = Request::int('start_hour');
$end_hour = Request::int('end_hour');
$days = Request::getArray('days');
}
if ($start_hour > $end_hour) {
$end_hour = $start_hour + 1;
PageLayout::postError(_('Die Endzeit darf nicht vor der Startzeit liegen!'));
}
$this->my_schedule_settings = [
'glb_start_time' => $start_hour,
'glb_end_time' => $end_hour,
'glb_days' => $days,
'converted' => true
];
if ($semester_id) {
$this->my_schedule_settings['semester_id'] = $semester_id;
} else if ($semester = UserConfig::get($GLOBALS['user']->id)->SCHEDULE_SETTINGS['semester_id']) {
$this->my_schedule_settings['semester_id'] = $semester;
}
UserConfig::get($GLOBALS['user']->id)->store('SCHEDULE_SETTINGS', $this->my_schedule_settings);
if (Context::isInstitute()) {
$this->redirect('calendar/instschedule');
CSRFProtection::verifyUnsafeRequest();
$start_time = Request::get('start_time', '08:00');
$end_time = Request::get('end_time', '20:00');
$visible_days = Request::intArray('visible_days');
if ($start_time >= $end_time) {
PageLayout::postError(_('Die Startuhrzeit muss vor der Enduhrzeit liegen.'));
$this->redirect('calendar/schedule/settings');
return;
}
if (empty($visible_days)) {
PageLayout::postError(_('Es wurde kein Wochentag ausgewählt.'));
$this->redirect('calendar/schedule/settings');
return;
}
//Update the settings:
$schedule_settings = UserConfig::get(User::findCurrent()->id)->getValue('SCHEDULE_SETTINGS');
$schedule_settings['start_time'] = $start_time;
$schedule_settings['end_time'] = $end_time;
$schedule_settings['visible_days'] = $visible_days;
UserConfig::get(User::findCurrent()->id)->store('SCHEDULE_SETTINGS', $schedule_settings);
PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.'));
if (Request::isDialog()) {
$this->response->add_header('X-Dialog-Close', '1');
} else {
$this->redirect('calendar/schedule');
$this->redirect('calendar/schedule/index');
}
$this->render_nothing();
}
}
<?php
/*
* This is the controller for the single calendar view
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* @author Peter Thienel <thienel@data-quest.de>
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
*/
require_once 'app/controllers/calendar/calendar.php';
class Calendar_SingleController extends Calendar_CalendarController
{
public function before_filter(&$action, &$args) {
$this->base = 'calendar/single/';
parent::before_filter($action, $args);
}
protected function createSidebar($active = null, $calendar = null)
{
parent::createSidebar($active, $calendar);
$sidebar = Sidebar::Get();
if ($calendar->havePermission(Calendar::PERMISSION_WRITABLE)) {
$actions = new ActionsWidget();
$actions->addLink(
_('Termin anlegen'),
$this->url_for('calendar/single/edit'),
Icon::create('add'),
['data-dialog' => 'size=auto']
);
if ($calendar->havePermission(Calendar::PERMISSION_OWN)) {
if (Config::get()->CALENDAR_GROUP_ENABLE) {
$actions->addLink(
_('Kalender freigeben'),
$this->url_for('calendar/single/manage_access'),
Icon::create('community'),
[
'id' => 'calendar-open-manageaccess',
'data-dialog' => '',
'data-dialogname' => 'manageaccess'
]
);
}
$actions->addLink(
_('Veranstaltungstermine'),
$this->url_for('calendar/single/seminar_events'),
Icon::create('seminar'),
['data-dialog' => 'size=auto']
);
}
$sidebar->addWidget($actions);
}
if ($calendar->havePermission(Calendar::PERMISSION_OWN)) {
$export = new ExportWidget();
$export->addLink(_('Termine exportieren'),
$this->url_for('calendar/single/export_calendar'),
Icon::create('download'),
['data-dialog' => 'size=auto']
)->setActive($active == 'export_calendar');
$export->addLink(
_('Termine importieren'),
$this->url_for('calendar/single/import'),
Icon::create('upload'),
['data-dialog' => 'size=auto']
)->setActive($active == 'import');
$export->addLink(
_('Kalender teilen'),
$this->url_for('calendar/single/share'),
Icon::create('group2'),
['data-dialog' => 'size=auto']
)->setActive($active == 'share');
$sidebar->addWidget($export);
}
}
public function day_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = SingleCalendar::getDayCalendar($this->range_id,
$this->atime, null, $this->restrictions);
PageLayout::setTitle($this->getTitle($this->calendar, _('Tagesansicht')));
$this->last_view = 'day';
$this->createSidebar('day', $this->calendar);
$this->createSidebarFilter();
}
public function week_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$timestamp = mktime(12, 0, 0, date('n', $this->atime), date('j', $this->atime), date('Y', $this->atime));
$monday = $timestamp - 86400 * (strftime('%u', $timestamp) - 1);
$day_count = $this->settings['type_week'] == 'SHORT' ? 5 : 7;
$this->calendars = [];
for ($i = 0; $i < $day_count; $i++) {
$this->calendars[$i] =
SingleCalendar::getDayCalendar(
$this->range_id,
$monday + $i * 86400,
null,
$this->restrictions
);
}
PageLayout::setTitle($this->getTitle($this->calendars[0], _('Wochenansicht')));
$this->last_view = 'week';
$this->createSidebar('week', $this->calendars[0]);
$this->createSidebarFilter();
}
public function month_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$month_start = mktime(12, 0, 0, date('n', $this->atime), 1, date('Y', $this->atime));
$month_end = mktime(12, 0, 0, date('n', $this->atime), date('t', $this->atime), date('Y', $this->atime));
$adow = strftime('%u', $month_start) - 1;
$cor = date('n', $this->atime) == 3 ? 1 : 0;
$this->first_day = $month_start - $adow * 86400;
$this->last_day = ((42 - ($adow + date('t', $this->atime))) % 7 + $cor) * 86400 + $month_end;
$this->calendars = [];
for ($start_day = $this->first_day; $start_day <= $this->last_day; $start_day += 86400) {
$this->calendars[] = SingleCalendar::getDayCalendar($this->range_id,
$start_day, null, $this->restrictions);
}
PageLayout::setTitle($this->getTitle($this->calendars[0], _('Monatsansicht')));
$this->last_view = 'month';
$this->createSidebar('month', $this->calendars[0]);
$this->createSidebarFilter();
}
public function year_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$start = mktime(0, 0, 0, 1, 1, date('Y', $this->atime));
$end = mktime(23, 59, 59, 12, 31, date('Y', $this->atime));
$this->calendar = new SingleCalendar($this->range_id, $start, $end);
$this->count_list = $this->calendar->getListCountEvents(null, null,
$this->restrictions);
PageLayout::setTitle($this->getTitle($this->calendar, _('Jahresansicht')));
$this->last_view = 'year';
$this->createSidebar('year', $this->calendar);
$this->createSidebarFilter();
}
public function event_action($range_id = null, $event_id = null)
{
PageLayout::setTitle(_('Termindaten'));
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = new SingleCalendar($this->range_id);
$this->event = $this->calendar->getEvent($event_id);
$this->createSidebar('edit', $this->calendar);
$this->createSidebarFilter();
}
public function delete_action($range_id, $event_id)
{
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = new SingleCalendar($this->range_id);
if ($this->calendar->deleteEvent($event_id, true)) {
PageLayout::postSuccess(_('Der Termin wurde gelöscht.'));
}
$this->redirect($this->url_for('calendar/single/' . $this->last_view));
}
public function delete_recurrence_action($range_id, $event_id, $atime)
{
$this->range_id = $range_id ?: $this->range_id;
$calendar = new SingleCalendar($this->range_id);
$event = $calendar->getEvent($event_id);
if ($event->getRecurrence('rtype') != 'SINGLE') {
$exceptions = $event->getExceptions();
$exceptions[] = $atime;
$event->setExceptions($exceptions);
if ($event->store() !== false) {
PageLayout::postSuccess(strftime(_('Termin am %x aus Serie gelöscht.'), $atime));
}
}
$this->redirect($this->url_for('calendar/single/' . $this->last_view));
}
public function export_event_action($event_id, $range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$calendar = new SingleCalendar($this->range_id);
$event = $calendar->getEvent($event_id);
if (!$event->isNew()) {
$calender_writer = new CalendarWriterICalendar();
$export = new CalendarExportFile($calender_writer);
$export->exportFromObjects($event);
$export->sendFile();
}
$this->render_nothing();
}
public function export_calendar_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = new SingleCalendar($this->range_id);
if (Request::submitted('export')) {
$calender_writer = new CalendarWriterICalendar();
$export = new CalendarExportFile($calender_writer);
if (Request::get('event_type') == 'user') {
$types = ['CalendarEvent'];
} else if (Request::get('event_type') == 'course') {
$types = ['CourseEvent', 'CourseCancelledEvent'];
} else {
$types = ['CalendarEvent', 'CourseEvent', 'CourseCancelledEvent'];
}
if (Request::get('export_time') == 'date') {
$exstart = $this->parseDateTime(Request::get('export_start'));
$exend = $this->parseDateTime(Request::get('export_end'));
} else {
$exstart = 0;
$exend = Calendar::CALENDAR_END;
}
$export->exportFromDatabase($this->calendar->getRangeId(), $exstart, $exend, $types);
$export->sendFile();
$this->render_nothing();
exit;
}
PageLayout::setTitle($this->getTitle($this->calendar, _('Termine exportieren')));
$this->createSidebar('export_calendar', $this->calendar);
$this->createSidebarFilter();
}
public function import_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = new SingleCalendar($this->range_id);
if ($this->calendar->havePermission(Calendar::PERMISSION_OWN)) {
if (Request::submitted('import')) {
CSRFProtection::verifySecurityToken();
$calender_parser = new CalendarParserICalendar();
$import = new CalendarImportFile($calender_parser, $_FILES['importfile']);
if (Request::get('import_as_private_imp')) {
$import->changePublicToPrivate();
}
$import->importIntoDatabase($range_id);
$import_count = $import->getCount();
PageLayout::postMessage(MessageBox::success(
sprintf('Es wurden %s Termine importiert.', $import_count)));
$this->redirect($this->url_for('calendar/single/' . $this->last_view));
}
}
PageLayout::setTitle($this->getTitle($this->calendar, _('Termine importieren')));
$this->createSidebar('import', $this->calendar);
$this->createSidebarFilter();
}
public function share_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = new SingleCalendar($this->range_id);
$this->short_id = null;
if ($this->calendar->havePermission(Calendar::PERMISSION_OWN)) {
if (Request::submitted('delete_id')) {
CSRFProtection::verifySecurityToken();
IcalExport::deleteKey($GLOBALS['user']->id);
PageLayout::postSuccess(_('Die Adresse, unter der Ihre Termine abrufbar sind, wurde gelöscht'));
}
if (Request::submitted('new_id')) {
CSRFProtection::verifySecurityToken();
$this->short_id = IcalExport::setKey($GLOBALS['user']->id);
PageLayout::postSuccess(_('Eine Adresse, unter der Ihre Termine abrufbar sind, wurde erstellt.'));
} else {
$this->short_id = IcalExport::getKeyByUser($GLOBALS['user']->id);
}
$text = "";
if (Request::submitted('submit_email')) {
$email_reg_exp = '/^([-.0-9=?A-Z_a-z{|}~])+@([-.0-9=?A-Z_a-z{|}~])+\.[a-zA-Z]{2,6}$/i';
if (preg_match($email_reg_exp, Request::get('email')) !== 0) {
$subject = '[' .Config::get()->UNI_NAME_CLEAN . ']' . _('Exportadresse für Ihre Termine');
$text .= _('Diese Email wurde vom Stud.IP-System verschickt. Sie können auf diese Nachricht nicht antworten.') . "\n\n";
$text .= _('Über diese Adresse erreichen Sie den Export für Ihre Termine:') . "\n\n";
$text .= $GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/ical/index/'
. IcalExport::getKeyByUser($GLOBALS['user']->id);
StudipMail::sendMessage(Request::get('email'), $subject, $text);
PageLayout::postSuccess(_('Die Adresse wurde verschickt!'));
} else {
PageLayout::postError(_('Bitte geben Sie eine gültige Email-Adresse an.'));
}
$this->short_id = IcalExport::getKeyByUser($GLOBALS['user']->id);
}
}
PageLayout::setTitle($this->getTitle($this->calendar, _('Kalender teilen oder einbetten')));
$this->createSidebar('share', $this->calendar);
$this->createSidebarFilter();
}
public function manage_access_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = new SingleCalendar($this->range_id);
$all_calendar_users =
CalendarUser::getUsers($this->calendar->getRangeId());
$this->filter_groups = Statusgruppen::findByRange_id(
$this->calendar->getRangeId());
$this->users = [];
$this->group_filter_selected = Request::option('group_filter', 'list');
if ($this->group_filter_selected != 'list') {
$contact_group = Statusgruppen::find($this->group_filter_selected);
$calendar_users = [];
foreach ($contact_group->members as $member) {
$calendar_users[] = new CalendarUser([$this->calendar->getRangeId(), $member->user_id]);
}
$this->calendar_users = SimpleORMapCollection::createFromArray($calendar_users);
} else {
$this->group_filter_selected = 'list';
$this->calendar_users = $all_calendar_users;
}
$this->own_perms = [];
foreach ($this->calendar_users as $calendar_user) {
$other_user = CalendarUser::find([$calendar_user->user_id, $this->calendar->getRangeId()]);
if ($other_user) {
$this->own_perms[$calendar_user->user_id] = $other_user->permission;
} else {
$this->own_perms[$calendar_user->user_id] = Calendar::PERMISSION_FORBIDDEN;
}
$this->users[mb_strtoupper(SimpleCollection::translitLatin1($calendar_user->nachname[0]))][] = $calendar_user;
}
ksort($this->users);
$this->users = array_map(function ($g) {
return SimpleCollection::createFromArray($g)->orderBy('nachname, vorname');
}, $this->users);
$this->mps = MultiPersonSearch::get('calendar-manage_access')
->setTitle(_('Personhinzufügen'))
->setLinkText(_('Person hinzufügen'))
->setDefaultSelectedUser($all_calendar_users->pluck('user_id'))
->setJSFunctionOnSubmit('STUDIP.CalendarDialog.closeMps')
->setExecuteURL($this->url_for('calendar/single/add_users/' . $this->calendar->getRangeId()))
->setSearchObject(new StandardSearch('user_id'));
PageLayout::setTitle($this->getTitle($this->calendar, _('Kalender freigeben')));
$this->createSidebar('manage_access', $this->calendar);
$this->createSidebarFilter();
}
public function add_users_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = new SingleCalendar($this->range_id);
if (Request::isXhr()) {
$added_users = Request::optionArray('added_users');
} else {
$mps = MultiPersonSearch::load('calendar-manage_access');
$added_users = $mps->getAddedUsers();
$mps->clearSession();
}
$added = 0;
foreach ($added_users as $user_id) {
$user_to_add = User::find($user_id);
if ($user_to_add) {
$calendar_user = new CalendarUser(
[$this->calendar->getRangeId(), $user_to_add->id]);
if ($calendar_user->isNew()) {
$calendar_user->permission = Calendar::PERMISSION_READABLE;
$added += $calendar_user->store();
}
}
}
if ($added) {
PageLayout::postSuccess(sprintf(
ngettext(
'Eine Person wurde mit der Berechtigung zum Lesen des Kalenders hinzugefügt.',
'%s Personen wurden mit der Berechtigung zum Lesen des Kalenders hinzugefügt.',
$added
),
$added
));
}
if (Request::isXhr()) {
$this->response->add_header('X-Dialog-Close', 1);
$this->response->set_status(200);
$this->render_nothing();
} else {
$this->redirect($this->url_for('calendar/single/manage_access/' . $this->calendar->getRangeId()));
}
}
public function remove_user_action($range_id = null, $user_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$user_id = $user_id ?: Request::option('user_id');
$this->calendar = new SingleCalendar($this->range_id);
$calendar_user = new CalendarUser([$this->calendar->getRangeId(), $user_id]);
if (!$calendar_user->isNew()) {
$name = $calendar_user->user->getFullname();
$calendar_user->delete();
}
if (Request::isXhr()) {
$this->response->set_status(200);
$this->render_nothing();
} else {
PageLayout::postSuccess(sprintf(_('Person %s wurde entfernt.', htmlReady($name))));
$this->redirect($this->url_for('calendar/single/manage_access/' . $this->calendar->getRangeId()));
}
}
public function store_permissions_action($range_id = null)
{
$this->range_id = $range_id ?: $this->range_id;
$this->calendar = new SingleCalendar($this->range_id);
$deleted = 0;
$read = 0;
$write = 0;
$submitted_permissions = Request::intArray('perm');
foreach ($submitted_permissions as $user_id => $new_perm) {
$calendar_user = new CalendarUser([$this->calendar->getRangeId(), $user_id]);
if (!$calendar_user->isNew() && $new_perm == 1) {
$deleted += $calendar_user->delete();
$new_perm = 0;
}
if ($new_perm >= Calendar::PERMISSION_READABLE
&& $calendar_user->permission != $new_perm) {
$calendar_user->permission = $new_perm;
if ($calendar_user->store()) {
if ($new_perm == Calendar::PERMISSION_READABLE) {
$read++;
} else {
$write++;
}
}
}
}
$sum = $deleted + $read + $write;
if ($sum) {
if ($deleted) {
$details[] = sprintf(ngettext('Einer Person wurde die Berechtigungen entzogen.',
'%s Personen wurden die Berechtigungen entzogen.', $deleted), $deleted);
}
if ($read) {
$details[] = sprintf(ngettext('Eine Person wurde auf leseberechtigt gesetzt.',
'%s Personen wurden auf leseberechtigt gesetzt.', $read), $read);
}
if ($write) {
$details[] = sprintf(ngettext('Eine Person wurde auf schreibberechtigt gesetzt.',
'%s Personen wurden auf schreibberechtigt gesetzt.', $write), $write);
}
PageLayout::postSuccess(sprintf(
ngettext('Die Berechtigungen von einer Person wurde geändert.',
'Die Berechtigungen von %s Personen wurden geändert.',
$sum), $sum),
$details
);
// no message if the group was changed
} else if (!Request::submitted('calendar_group_submit')) {
PageLayout::postSuccess(_('Es wurden keine Berechtigungen geändert.'));
}
$this->redirect($this->url_for(
'calendar/single/manage_access/' . $this->calendar->getRangeId(),
['group_filter' => Request::option('group_filter', 'list')])
);
}
public function seminar_events_action($order_by = null, $order = 'asc')
{
$config_sem = $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE;
if (!Config::get()->MY_COURSES_ENABLE_ALL_SEMESTERS && $config_sem == 'all') {
$config_sem = 'future';
}
$this->sem_data = Semester::findAllVisible();
$sem = $config_sem ?: Config::get()->MY_COURSES_DEFAULT_CYCLE;
if (Request::option('sem_select')) {
$sem = Request::get('sem_select', $sem);
}
if (!in_array($sem, words('future all last current')) && isset($sem)) {
Request::set('sem_select', $sem);
}
$this->group_field = 'sem_number';
$this->order_by = $order_by;
$this->config_sem_number = Config::get()->IMPORTANT_SEMNUMBER;
// Needed parameters for selecting courses
$params = [
'group_field' => $this->group_field,
'order_by' => $order_by,
'order' => $order,
'studygroups_enabled' => false,
'deputies_enabled' => false
];
$this->sem_courses = MyRealmModel::getPreparedCourses($sem, $params);
$semesters = new SimpleCollection(Semester::getAll());
$this->sem = $sem;
$this->semesters = $semesters->orderBy('beginn desc');
$this->bind_calendar = SimpleCollection::createFromArray(
CourseMember::findBySQL('user_id = ? AND bind_calendar = 1', [$GLOBALS['user']->id])
)->pluck('seminar_id');
}
public function store_selected_sem_action()
{
CSRFProtection::verifySecurityToken();
if (Request::submitted('store')) {
$selected_sems = Request::intArray('selected_sem');
$courses = SimpleORMapCollection::createFromArray(
CourseMember::findBySQL('user_id = ? AND Seminar_id IN (?)',
[$GLOBALS['user']->id, array_keys($selected_sems)]));
$courses->each(function ($a) use ($selected_sems) {
$a->bind_calendar = $selected_sems[$a->seminar_id];
$a->store();
});
PageLayout::postSuccess(_('Die Auswahl der Veranstaltungen wurde gespeichert.'));
}
$this->redirect($this->url_for('calendar/single/' . $this->last_view));
}
/**
* Retrieve the title of the calendar depending on calendar owner (range).
*
* @param SingleCalendar $calendar The calendar
* @param string $title_end Additional text
* @return string The complete title for the headline
*/
protected function getTitle(SingleCalendar $calendar, $title_end)
{
$status = '';
if ($calendar->getRangeId() == $GLOBALS['user']->id) {
$title = _('Mein persönlicher Terminkalender');
} else {
if ($calendar->getRange() == Calendar::RANGE_USER) {
$title = sprintf(_('Terminkalender von %s'),
$calendar->range_object->getFullname());
} else {
$title = Context::getHeaderLine();
}
if ($calendar->havePermission(Calendar::PERMISSION_WRITABLE)) {
$status = ' (' . _('schreibberechtigt') . ')';
} else {
$status = ' (' . _('leseberechtigt') . ')';
}
}
return $title . ' - ' . $title_end . $status ;
}
}
<?php
final class CaptchaController extends StudipController
{
public function challenge_action(): void
{
$this->response->add_header(
'Expires',
gmdate('D, d M Y H:i:s', time() + CaptchaChallenge::CHALLENGE_EXPIRATION) . ' GMT'
);
$this->render_json(CaptchaChallenge::createNewChallenge());
}
}
......@@ -27,7 +27,7 @@ class Consultation_AdminController extends ConsultationController
$this->activateNavigation('admin');
if (Context::isCourse() || Context::isInstitute()) {
PageLayout::setTitle(Context::get()->getFullname() . ' - ' . _('Verwaltung der Termine'));
PageLayout::setTitle(Context::get()->getFullName() . ' - ' . _('Verwaltung der Termine'));
} else {
PageLayout::setTitle(_('Verwaltung der Termine'));
}
......@@ -69,10 +69,10 @@ class Consultation_AdminController extends ConsultationController
public function index_action($page = 0)
{
$this->count = ConsultationSlot::countByRange($this->range);
$this->limit = Config::get()->ENTRIES_PER_PAGE;
$count = ConsultationSlot::countByRange($this->range);
$limit = Config::get()->ENTRIES_PER_PAGE;
if ($page >= ceil($this->count / $this->limit)) {
if ($page >= ceil($count / $limit)) {
$page = 0;
}
......@@ -81,7 +81,7 @@ class Consultation_AdminController extends ConsultationController
if ($GLOBALS['user']->cfg->CONSULTATION_SHOW_GROUPED) {
$this->blocks = $this->groupSlots(ConsultationSlot::findByRange(
$this->range,
"ORDER BY start ASC LIMIT " . ($this->page * $this->limit) . ", {$this->limit}"
"ORDER BY start ASC LIMIT " . ($this->page * $limit) . ", {$limit}"
));
} else {
$this->blocks = ConsultationBlock::findByRange(
......@@ -90,20 +90,22 @@ class Consultation_AdminController extends ConsultationController
);
$this->slots = ConsultationSlot::findByRange(
$this->range,
"ORDER BY start ASC LIMIT " . ($this->page * $this->limit) . ", {$this->limit}"
"ORDER BY start ASC LIMIT " . ($this->page * $limit) . ", {$limit}"
);
}
$this->pagination = Pagination::create($count, $this->page, $limit);
$action = $GLOBALS['user']->cfg->CONSULTATION_SHOW_GROUPED ? 'index' : 'ungrouped';
$this->render_action($action);
}
public function expired_action($page = 0)
{
$this->count = ConsultationSlot::countByRange($this->range, true);
$this->limit = Config::get()->ENTRIES_PER_PAGE;
$count = ConsultationSlot::countByRange($this->range, true);
$limit = Config::get()->ENTRIES_PER_PAGE;
if ($page >= ceil($this->count / $this->limit)) {
if ($page >= ceil($count / $limit)) {
$page = 0;
}
......@@ -112,7 +114,7 @@ class Consultation_AdminController extends ConsultationController
if ($GLOBALS['user']->cfg->CONSULTATION_SHOW_GROUPED) {
$this->blocks = $this->groupSlots(ConsultationSlot::findByRange(
$this->range,
"ORDER BY start DESC LIMIT " . ($this->page * $this->limit) . ", {$this->limit}",
"ORDER BY start DESC LIMIT " . ($this->page * $limit) . ", {$limit}",
true
));
} else {
......@@ -123,11 +125,13 @@ class Consultation_AdminController extends ConsultationController
);
$this->slots = ConsultationSlot::findByRange(
$this->range,
"ORDER BY start DESC LIMIT " . ($this->page * $this->limit) . ", {$this->limit}",
"ORDER BY start DESC LIMIT " . ($this->page * $limit) . ", {$limit}",
true
);
}
$this->pagination = Pagination::create($count, $this->page, $limit);
$action = $GLOBALS['user']->cfg->CONSULTATION_SHOW_GROUPED ? 'index' : 'ungrouped';
$this->render_action($action);
}
......@@ -136,25 +140,56 @@ class Consultation_AdminController extends ConsultationController
{
PageLayout::setTitle(_('Neue Terminblöcke anlegen'));
$this->room = '';
$this->responsible = false;
$room = '';
$responsible = false;
// TODO: inst_default?
if ($this->range instanceof User) {
$rooms = $this->range->institute_memberships->pluck('Raum');
$rooms = array_filter($rooms);
$this->room = $rooms ? reset($rooms) : '';
$room = $rooms ? reset($rooms) : '';
} elseif ($this->range instanceof Course) {
$this->room = $this->range->ort;
$room = $this->range->ort;
$block = new ConsultationBlock();
$block->range = $this->range;
$this->responsible = $block->getPossibleResponsibilites();
$responsible = $block->getPossibleResponsibilites();
} elseif ($this->range instanceof Institute) {
$block = new ConsultationBlock();
$block->range = $this->range;
$this->responsible = $block->getPossibleResponsibilites();
$responsible = $block->getPossibleResponsibilites();
}
$convertResponsibilities = function ($input) {
if ($input === false) {
return false;
}
foreach ($input as $key => $values) {
$input[$key] = array_map(
fn($item) => [
'id' => $item->id,
'label' => $item instanceof Statusgruppen ? $item->getName() : $item->getFullName(),
],
$values
);
}
return $input;
};
$this->render_vue_app(
Studip\VueApp::create('ConsultationCreator')
->withProps([
'as-dialog' => Request::isXhr(),
'cancel-url' => $this->indexURL(),
'default-room' => $room,
'range-type' => get_class($this->range),
'slot-count-threshold' => self::SLOT_COUNT_THRESHOLD,
'store-url' => $this->storeURL(),
'with-responsible' => $convertResponsibilities($responsible),
])
);
}
public function store_action()
......@@ -162,18 +197,29 @@ class Consultation_AdminController extends ConsultationController
CSRFProtection::verifyUnsafeRequest();
try {
$interval = Request::int('interval');
$start = $this->getDateAndTime('start');
$end = $this->getDateAndTime('end');
$end = $this->getDateAndTime($interval <= 0 ? 'start' : 'end', 'end');
if (date('Hi', $end) <= date('Hi', $start)) {
throw new InvalidArgumentException(_('Die Endzeit liegt vor der Startzeit!'));
}
$dow = Request::int('day-of-week');
if ($interval <= 0) {
$dow = date('w', $start);
}
// Determine duration of a slot and pause times
$duration = Request::int('duration');
if ($duration === null && $interval === -1) {
$duration = (int) (($end - $start) / 60);
}
$pause_time = Request::bool('pause') ? Request::int('pause_time') : null;
$pause_duration = Request::bool('pause') ? Request::int('pause_duration') : null;
if ($pause_time && $pause_time < $duration) {
if ($interval >= 0 && $pause_time && $pause_time < $duration) {
throw new InvalidArgumentException(_('Die definierte Zeit bis zur Pause ist kleiner als die Dauer eines Termins.'));
}
......@@ -184,12 +230,13 @@ class Consultation_AdminController extends ConsultationController
$slot_count = ConsultationBlock::countSlots(
$start,
$end,
Request::int('day-of-week'),
Request::int('interval'),
Request::int('duration'),
$dow,
$interval,
$duration,
$pause_time,
$pause_duration
);
if ($slot_count >= self::SLOT_COUNT_THRESHOLD && !Request::int('confirmed')) {
$this->flash['confirm-many'] = $slot_count;
throw new Exception('', -1);
......@@ -199,8 +246,8 @@ class Consultation_AdminController extends ConsultationController
$this->range,
$start,
$end,
Request::int('day-of-week'),
Request::int('interval')
$dow,
$interval
);
$stored = 0;
......@@ -214,8 +261,9 @@ class Consultation_AdminController extends ConsultationController
$block->note = Request::get('note');
$block->size = Request::int('size', 1);
$block->lock_time = Request::int('lock_time');
$block->consecutive = Request::bool('consecutive', false);
$slots = $block->createSlots(Request::int('duration'), $pause_time, $pause_duration);
$slots = $block->createSlots($duration, $pause_time, $pause_duration);
if (count($slots) === 0) {
continue;
}
......@@ -356,15 +404,19 @@ class Consultation_AdminController extends ConsultationController
'sem_perm' => $permissions,
]);
}
}
if (Request::isPost()) {
public function store_booking_action($block_id, $slot_id, $page = 0): void
{
CSRFProtection::verifyUnsafeRequest();
if ($this->slot->isOccupied()) {
$slot = $this->loadSlot($block_id, $slot_id);
if ($slot->isOccupied()) {
PageLayout::postError(_('Dieser Termin ist bereits belegt.'));
} else {
$booking = new ConsultationBooking();
$booking->slot_id = $this->slot->id;
$booking->slot_id = $slot->id;
$booking->user_id = Request::option('user_id');
$booking->reason = trim(Request::get('reason'));
$booking->store();
......@@ -372,8 +424,7 @@ class Consultation_AdminController extends ConsultationController
PageLayout::postSuccess(_('Der Termin wurde reserviert.'));
}
$this->redirect("consultation/admin/index/{$page}#slot-{$this->slot->id}");
}
$this->redirect("consultation/admin/index/{$page}#slot-{$slot->id}");
}
public function edit_action($block_id, $page = 0)
......@@ -403,6 +454,7 @@ class Consultation_AdminController extends ConsultationController
$this->block->mail_to_tutors = Request::bool('mail-to-tutors', false);
$this->block->confirmation_text = trim(Request::get('confirmation-text'));
$this->block->lock_time = Request::int('lock_time');
$this->block->consecutive = Request::bool('consecutive', false);
foreach ($this->block->slots as $slot) {
$slot->note = '';
......@@ -738,7 +790,7 @@ class Consultation_AdminController extends ConsultationController
// Redirect to message write
$_SESSION['sms_data'] = compact('p_rec');
page_close();
sess()->save();
$this->redirect(URLHelper::getURL(
'dispatch.php/messages/write',
compact('default_subject')
......@@ -808,7 +860,7 @@ class Consultation_AdminController extends ConsultationController
_('Terminblöcke anlegen'),
$this->createURL(),
Icon::create('add')
)->asDialog('size=auto');
)->asDialog('size=big');
$actions->addLink(
_('Namen des Reiters ändern'),
$this->tabURL($action === 'expired'),
......@@ -867,16 +919,34 @@ class Consultation_AdminController extends ConsultationController
}
}
private function getDateAndTime($index)
private function getDateAndTime(string $index, string $index_time = null)
{
if (!Request::submitted("{$index}-date") || !Request::submitted("{$index}-time")) {
if ($index_time === null) {
$index_time = $index;
}
if (!Request::submitted("{$index}-date") || !Request::submitted("{$index_time}-time")) {
throw new Exception("Date with index '{$index}' was not submitted properly");
}
return strtotime(implode(' ', [
Request::get("{$index}-date"),
Request::get("{$index}-time")
]));
$date = Datetime::createFromFormat(
DateTimeInterface::RFC3339_EXTENDED,
Request::get("{$index}-date")
);
if (!$date) {
throw new Exception("Date with index '{$index}' could not be parsed");
}
$date->setTimezone(new DateTimeZone(date_default_timezone_get()));
$date->setTime(
...array_map(
intval(...),
explode(':', Request::get("{$index_time}-time"))
)
);
return $date->getTimestamp();
}
private function getUserConfig(): RangeConfig
......
......@@ -19,7 +19,7 @@ abstract class ConsultationController extends AuthenticatedController
$this->range = Context::get();
$type = 'object';
} else {
$this->range = $GLOBALS['user']->getAuthenticatedUser();
$this->range = User::findCurrent();
}
if (!$this->range) {
......@@ -60,7 +60,7 @@ abstract class ConsultationController extends AuthenticatedController
$this->render_template('consultation/not_found', $this->layout);
}
protected function activateNavigation($path)
protected function activateNavigation($path): void
{
$path = ltrim($path, '/');
......@@ -73,7 +73,7 @@ abstract class ConsultationController extends AuthenticatedController
}
}
protected function getConsultationTitle()
protected function getConsultationTitle(): string
{
return $this->range->getConfiguration()->CONSULTATION_TAB_TITLE;
}
......@@ -103,7 +103,8 @@ abstract class ConsultationController extends AuthenticatedController
return $block;
}
protected function loadSlot($block_id, $slot_id)
protected function loadSlot($block_id, $slot_id): ConsultationSlot
{
$block = $this->loadBlock($block_id);
$slot = $block->slots->find($slot_id);
......@@ -115,7 +116,7 @@ abstract class ConsultationController extends AuthenticatedController
return $slot;
}
protected function loadBooking($block_id, $slot_id, $booking_id)
protected function loadBooking($block_id, $slot_id, $booking_id): ConsultationBooking
{
$slot = $this->loadSlot($block_id, $slot_id);
$booking = $slot->bookings->find($booking_id);
......
......@@ -71,6 +71,8 @@ class Consultation_OverviewController extends ConsultationController
if ($this->slot->isOccupied()) {
PageLayout::postError(_('Dieser Termin ist bereits belegt.'));
} elseif (!$this->slot->isBookable()) {
PageLayout::postError(_('Dieser Termin ist für Buchungen gesperrt.'));
} else {
$booking = new ConsultationBooking();
$booking->slot_id = $this->slot->id;
......
......@@ -20,15 +20,15 @@ class ContactController extends AuthenticatedController
parent::before_filter($action, $args);
// Load statusgroups
$this->groups = SimpleCollection::createFromArray(Statusgruppen::findByRange_id(User::findCurrent()->id));
$this->groups = SimpleCollection::createFromArray(ContactGroup::findByOwner_id(User::findCurrent()->id));
// Load requested group
if (!empty($args[0])) {
$this->group = $this->groups->findOneBy('statusgruppe_id', $args[0]);
$this->group = $this->groups->findOneBy('id', $args[0]);
//Check for cheaters
if ($this->group->range_id != User::findCurrent()->id) {
throw new AccessDeniedException;
if ($this->group->owner_id !== User::findCurrent()->id) {
throw new AccessDeniedException();
}
}
......@@ -43,16 +43,17 @@ class ContactController extends AuthenticatedController
// Check if we need to add contacts
$mps = MultiPersonSearch::load('contacts');
$imported = 0;
foreach ($mps->getAddedUsers() as $userId) {
$user_to_add = User::find($userId);
foreach ($mps->getAddedUsers() as $user_id) {
$user_to_add = User::find($user_id);
if ($user_to_add) {
$new_contact = [
'owner_id' => User::findCurrent()->id,
'user_id' => $user_to_add->id];
'user_id' => $user_to_add->id,
];
if ($filter && $this->group) {
$new_contact['group_assignments'][] = [
'statusgruppe_id' => $this->group->id,
'user_id' => $user_to_add->id
$new_contact['groups'][] = [
'group_id' => $this->group->id,
'user_id' => $user_to_add->id,
];
}
$imported += (bool)Contact::import($new_contact)->store();
......@@ -74,7 +75,7 @@ class ContactController extends AuthenticatedController
if ($filter) {
$selected = $this->group;
$contacts = SimpleCollection::createFromArray(User::findMany($selected->members->pluck('user_id')));
$contacts = SimpleCollection::createFromArray(User::findMany($selected->items->pluck('user_id')));
} else {
$selected = false;
$contacts = User::findCurrent()->contacts;
......@@ -125,7 +126,7 @@ class ContactController extends AuthenticatedController
$contact = Contact::find([User::findCurrent()->id, User::findByUsername($contact_username)->id]);
if ($contact) {
if ($group) {
$contact->group_assignments->unsetBy('statusgruppe_id', $group);
$contact->groups->unsetBy('group_id', $group);
if ($contact->store()) {
$removed_group_number++;
}
......@@ -144,7 +145,7 @@ class ContactController extends AuthenticatedController
$contact = Contact::find([User::findCurrent()->id, User::findByUsername(Request::username('user'))->id]);
if ($contact) {
if ($group) {
$contact->group_assignments->unsetBy('statusgruppe_id', $group);
$contact->groups->unsetBy('group_id', $group);
if ($contact->store()) {
PageLayout::postSuccess(_('Der Kontakt wurde aus der Gruppe entfernt.'));
}
......@@ -161,11 +162,11 @@ class ContactController extends AuthenticatedController
public function editGroup_action()
{
if (!$this->group) {
$this->group = new Statusgruppen();
$this->group->range_id = User::findCurrent()->id;
$this->group = new ContactGroup();
$this->group->owner_id = User::findCurrent()->id;
}
if (Request::submitted('store')) {
CSRFProtection::verifyRequest();
CSRFProtection::verifyUnsafeRequest();
$this->group->name = Request::get('name');
$this->group->store();
$this->redirect('contact/index/' . $this->group->id);
......@@ -174,7 +175,7 @@ class ContactController extends AuthenticatedController
public function deleteGroup_action()
{
CSRFProtection::verifyRequest();
CSRFProtection::verifyUnsafeRequest();
$this->group->delete();
$this->redirect('contact/index');
}
......@@ -184,21 +185,34 @@ class ContactController extends AuthenticatedController
$charset = 'utf-8';
$filename = _('Kontakte');
$this->set_layout(null);
$users = [];
if (Request::submitted('user')) {
$user = User::findManyByUsername(Request::getArray('user'));
$users = User::findManyByUsername(Request::getArray('user'));
}
if ($group) {
$user = User::findMany(Statusgruppen::find($group)->members->pluck('user_id'));
$group_object = Statusgruppen::find($group);
if (!$group_object) {
$this->set_status(404);
$this->render_nothing();
return;
}
$users = User::findMany($group_object->members->pluck('user_id'));
}
if (empty($users)) {
$user_object = User::findCurrent();
if (!$user_object) {
$this->set_status(404);
$this->render_nothing();
return;
}
if (!$user) {
$user = User::findCurrent()->contacts;
$users = User::findCurrent()->contacts;
}
header("Content-type: text/x-vCard;charset=" . $charset);
header("Content-disposition: attachment; " . encode_header_parameter('filename', $filename . '.vcf'));
header("Pragma: private");
$this->vCard = vCard::export($user);
$this->vCard = vCard::export($users);
}
private function initSidebar($active_id = null)
......
......@@ -120,6 +120,14 @@ class Contents_CoursewareController extends CoursewareController
$this->user_id = $GLOBALS['user']->id;
}
public function pdf_export_action(Courseware\StructuralElement $element, bool $with_children = false): void
{
$this->render_pdf(
$element->pdfExport(User::findCurrent(), $with_children),
trim($element->title) . '.pdf'
);
}
private function setBookmarkSidebar(): void
{
$sidebar = Sidebar::Get();
......@@ -199,49 +207,35 @@ class Contents_CoursewareController extends CoursewareController
$courses = new SimpleCollection($courses);
if (!Config::get()->MY_COURSES_ENABLE_STUDYGROUPS) {
$courses = $courses->filter(function ($a) {
return !$a->isStudygroup();
$courses = $courses->filter(function (Course $course) {
return !$course->isStudygroup();
});
}
if ($sem_key != 'all') {
$semester = Semester::find($sem_key);
$courses = $courses->filter(function ($a) use ($semester) {
if ($a->isInSemester($semester)) {
return true;
}
return false;
// Filter courses with enabled and visible courseware
$courses = $courses->filter(function (Course $course) {
return $this->isCoursewareEnabledAndVisible($course);
});
$coursewares = [];
foreach ($courses as $course) {
$element = StructuralElement::getCoursewareCourse($course->id);
if (!empty($element) && $this->isCoursewareEnabled($course->id)) {
$element['payload'] = json_decode($element['payload'], true);
$coursewares[] = $element;
}
}
if ($sem_key !== 'all') {
$semester = Semester::find($sem_key);
if (empty($coursewares)) {
return [];
$courses = $courses->filter(function (Course $course) use ($semester) {
return $course->isInSemester($semester);
});
}
return [$semester->id => [
'semester_name' => $semester->name,
'coursewares' => $coursewares
]];
} else {
$all_semesters = Semester::getAll();
$sem_courses = [];
foreach ($courses as $course) {
$element = StructuralElement::getCoursewareCourse($course->id);
if (!empty($element) && $this->isCoursewareEnabled($course->id)) {
$units = Unit::findCoursesUnits($course);
foreach ($units as $unit) {
$element = $unit->structural_element;
if (!$element || !$element->canRead(User::findCurrent())) {
continue;
}
$element['payload'] = json_decode($element['payload'], true);
if ($course->duration_time == -1) {
if ($course->isOpenEnded()) {
$sem_courses[$this->current_semester->id]['coursewares'][] = $element;
} else {
$end_semester = $course->getEndSemester();
......@@ -252,74 +246,38 @@ class Contents_CoursewareController extends CoursewareController
return $sem_courses;
}
}
/**
* Returns true if the courseware module is enabled for the passed course
* Returns true if the courseware module is enabled and visible for the
* passed course and current user
*
* @param string $course_id the course to check
* @return boolean true if courseware is enabled, false otherwise
* @param Course $course the course to check
* @return boolean true if courseware is enabled and visible,
* false otherwise
*/
private function isCoursewareEnabled($course_id): bool
private function isCoursewareEnabledAndVisible(Course $course): bool
{
$studip_module = PluginManager::getInstance()->getPlugin('CoursewareModule');
if (!$studip_module || !$studip_module->isActivated($course_id)) {
// Check if courseware is globally enabled
$studip_module = PluginManager::getInstance()->getPlugin(CoursewareModule::class);
if (!$studip_module) {
return false;
}
return true;
}
private function getProjects($purpose): array
{
$elements = StructuralElement::findProjects($this->user->id, $purpose);
foreach($elements as &$element) {
$element['payload'] = json_decode($element['payload'], true);
}
return $elements;
}
public function pdf_export_action($element_id, $with_children): void
{
$element = \Courseware\StructuralElement::findOneById($element_id);
$this->render_pdf($element->pdfExport($this->user, $with_children), trim($element->title).'.pdf');
// Check if courseware is enabled in course
$active_tool = ToolActivation::find([
$course->id,
$studip_module->getPluginId(),
]);
if (!$active_tool) {
return false;
}
/**
* To display the shared courseware
*
* @param string $entry_element_id the shared struct element id
*/
public function shared_content_courseware_action($entry_element_id): void
{
global $user;
$navigation = new Navigation(_('Geteiltes Lernmaterial'), 'dispatch.php/contents/courseware/shared_content_courseware/' . $entry_element_id);
Navigation::addItem('/contents/courseware/shared_content_courseware', $navigation);
Navigation::activateItem('/contents/courseware/shared_content_courseware');
$this->entry_element_id = $entry_element_id;
$struct = \Courseware\StructuralElement::findOneBySQL(
"id = ? AND range_type = 'user'",
[$this->entry_element_id]
// Check visibility
return $GLOBALS['perm']->have_studip_perm(
$active_tool->getVisibilityPermission(),
$course->id,
User::findCurrent()->id
);
if (!$struct) {
throw new Trails_Exception(404, _('Der geteilte Inhalt kann nicht gefunden werden.'));
}
if (!$struct->canRead($user) && !$struct->canEdit($user)) {
throw new AccessDeniedException();
}
$this->user_id = $struct->owner_id;
$this->setCoursewareSidebar();
}
}
......@@ -31,13 +31,13 @@ class Course_AdmissionController extends AuthenticatedController
if (!get_object_type($this->course_id, ['sem']) ||
SeminarCategories::GetBySeminarId($this->course_id)->studygroup_mode ||
!$GLOBALS['perm']->have_studip_perm('tutor', $this->course_id)) {
throw new Trails_Exception(403);
throw new Trails\Exception(403);
}
$this->course = Course::find($this->course_id);
$this->user_id = $GLOBALS['user']->id;
PageLayout::setHelpKeyword("Basis.VeranstaltungenVerwaltenZugangsberechtigungen");
PageLayout::setTitle($this->course->getFullname()." - " ._("Verwaltung von Zugangsberechtigungen"));
PageLayout::setTitle($this->course->getFullName()." - " ._("Verwaltung von Zugangsberechtigungen"));
$lockrules = words('admission_turnout admission_type admission_endtime admission_binding passwort read_level write_level admission_prelim admission_prelim_txt admission_starttime admission_endtime_sem admission_disable_waitlist user_domain admission_binding admission_studiengang');
$this->is_locked = [];
......@@ -48,7 +48,6 @@ class Course_AdmissionController extends AuthenticatedController
$this->is_locked['write_level'] = 'disabled readonly';
}
AdmissionApplication::addMembers($this->course->id);
PageLayout::addScript('studip-admission.js');
URLHelper::addLinkParam('return_to_dialog', Request::get('return_to_dialog'));
}
......@@ -138,14 +137,18 @@ class Course_AdmissionController extends AuthenticatedController
if (Request::submittedSome('change_admission_prelim_no', 'change_admission_prelim_yes') || !$question) {
if ($this->course->admission_prelim == 1 && $this->course->getNumParticipants() && Request::submitted('change_admission_prelim_yes')) {
$num_moved = 0;
$seminar = new Seminar($this->course_id);
foreach ($this->course->members->findBy('status', ['user','autor'])->pluck('user_id') as $user_id) {
$seminar->addPreliminaryMember($user_id);
$num_moved += ($seminar->deleteMember($user_id) !== false);
setTempLanguage($user_id);
foreach ($this->course->members->findBy('status', ['user','autor'])->pluck('user') as $user) {
$this->course->addPreliminaryMember($user);
try {
$this->course->deleteMember($user);
} catch (\Studip\Exception $e) {
continue;
}
$num_moved++;
setTempLanguage($user->id);
$message_body = sprintf(_('Sie wurden in der Veranstaltung **%s** in den Status **vorläufig akzeptiert** befördert, da das Anmeldeverfahren geändert wurde.'), $this->course->name);
$message_title = sprintf(_("Statusänderung %s"), $this->course->name);
messaging::sendSystemMessage($user_id, $message_title, $message_body);
messaging::sendSystemMessage($user->id, $message_title, $message_body);
restoreLanguage();
}
if ($num_moved) {
......@@ -158,13 +161,17 @@ class Course_AdmissionController extends AuthenticatedController
if ($this->course->admission_prelim == 0 && $this->course->getNumPrelimParticipants()) {
if (Request::submitted('change_admission_prelim_yes')) {
$num_moved = 0;
$seminar = new Seminar($this->course_id);
foreach ($this->course->admission_applicants->findBy('status', 'accepted')->pluck('user_id') as $user_id) {
$num_moved += ($seminar->addMember($user_id, 'autor') !== false);
setTempLanguage($user_id);
foreach ($this->course->admission_applicants->findBy('status', 'accepted')->pluck('user') as $user) {
try {
$this->course->addMember($user, 'autor');
} catch (\Studip\Exception $e) {
continue;
}
$num_moved++;
setTempLanguage($user->id);
$message_body = sprintf(_('Sie wurden in der Veranstaltung **%s** in den Status **Autor** versetzt, da das Anmeldeverfahren geändert wurde.'), $this->course->name);
$message_title = sprintf(_("Statusänderung %s"), $this->course->name);
messaging::sendSystemMessage($user_id, $message_title, $message_body);
messaging::sendSystemMessage($user, $message_title, $message_body);
restoreLanguage();
}
if ($num_moved) {
......@@ -200,7 +207,7 @@ class Course_AdmissionController extends AuthenticatedController
}
}
if (empty($question)) {
$this->redirect($this->action_url('index'));
$this->relocate($this->action_url('index'));
} else {
$this->button_yes = 'change_admission_prelim_yes';
$this->button_no = 'change_admission_prelim_no';
......@@ -241,7 +248,7 @@ class Course_AdmissionController extends AuthenticatedController
PageLayout::postSuccess(_("Zugriff für externe Nutzer wurde geändert."));
}
}
$this->redirect($this->action_url('index'));
$this->relocate($this->action_url('index'));
}
function change_admission_turnout_action()
......@@ -305,10 +312,11 @@ class Course_AdmissionController extends AuthenticatedController
}
}
if (empty($question)) {
$this->redirect($this->action_url('index'));
$this->relocate($this->action_url('index'));
} else {
$this->request = $request;
$this->button_yes = 'change_admission_turnout_yes';
$this->button_no = 'change_admission_turnout_no';
PageLayout::postInfo($question);
$this->render_template('course/admission/_change_admission.php');
}
......@@ -329,7 +337,7 @@ class Course_AdmissionController extends AuthenticatedController
PageLayout::postSuccess(_('Die zugelassenen Nutzerdomänen wurden geändert.'));
}
}
$this->redirect($this->action_url('index'));
$this->relocate($this->action_url('index'));
}
function change_course_set_action()
......@@ -393,10 +401,11 @@ class Course_AdmissionController extends AuthenticatedController
}
}
if (empty($question)) {
$this->redirect($this->action_url('index'));
$this->relocate($this->action_url('index'));
} else {
$this->request = ['change_course_set_unassign' => 1];
$this->button_yes = 'change_course_set_unassign_yes';
$this->button_no = 'change_course_set_unassign_no';
PageLayout::postInfo($question);
$this->render_template('course/admission/_change_admission.php');
}
......@@ -427,68 +436,42 @@ class Course_AdmissionController extends AuthenticatedController
$rule_types = AdmissionRule::getAvailableAdmissionRules(true);
if (isset($rule_types[$type])) {
$rule = new $type($rule_id);
if ($type === 'LockedAdmission') {
$courseset = CourseSet::getGlobalLockedAdmissionSetId();
CourseSet::addCourseToSet($courseset, $this->course_id);
PageLayout::postSuccess(_('Die Veranstaltung wurde gesperrt.'));
$this->relocate('course/admission');
} else {
$another_rule = null;
if (isset($rule_types[$another_type])) {
$another_rule = new $another_type($another_rule_id);
}
$course_set = CourseSet::getSetForRule($rule_id) ?: new CourseSet();
if ((Request::isPost() && Request::submitted('save')) || $rule instanceof LockedAdmission) {
if ($rule instanceof LockedAdmission) {
$course_set_id = CourseSet::getGlobalLockedAdmissionSetId();
CourseSet::addCourseToSet($course_set_id, $this->course_id);
PageLayout::postSuccess(_('Die Veranstaltung wurde gesperrt.'));
$this->redirect($this->action_url('index'));
return;
} else {
CSRFProtection::verifyUnsafeRequest();
$errors = $rule->validate(Request::getInstance());
if (empty($errors)) {
$rule->setAllData(Request::getInstance());
}
$types = [$type];
if ($another_rule) {
$another_errors = $another_rule->validate(Request::getInstance());
if (empty($another_errors)) {
$another_rule->setAllData(Request::getInstance());
}
$errors = array_merge($errors, $another_errors);
}
if (!mb_strlen(trim(Request::get('instant_course_set_name')))) {
$errors[] = _("Bitte geben Sie einen Namen für die Anmelderegel ein!");
} else {
$course_set->setName(trim(Request::get('instant_course_set_name')));
$types[] = $another_type;
}
if (count($errors)) {
PageLayout::postError(_('Speichern fehlgeschlagen'), array_map('htmlready', $errors));
} else {
$rule->store();
$course_set->setPrivate(true);
$course_set->addAdmissionRule($rule);
$course_set->setAlgorithm(new RandomAlgorithm());//TODO
$course_set->setCourses([$this->course_id]);
if ($another_rule) {
$course_set->addAdmissionRule($another_rule);
}
$course_set->store();
PageLayout::postSuccess(_("Die Anmelderegel wurde erzeugt und der Veranstaltung zugewiesen."));
$this->redirect($this->action_url('index'));
return;
}
}
}
if (!$course_set->getId()) {
$course_set->setName($rule->getName() . ': ' . $this->course->name);
}
$this->rule_template = $rule->getTemplate();
$this->type = $type;
$this->rule_id = $rule_id;
if ($another_rule) {
$this->type = $this->type . '_' . $another_type;
$this->rule_id = $this->rule_id . '_' . $another_rule->getId();
$this->rule_template = $this->rule_template . $another_rule->getTemplate();
$course_set_name = $rule->getName() . ': ' . $this->course->name;
$props = [
'rule-types' => $types,
'course-set-name' => $course_set_name,
'course-id' => $this->course_id
];
$this->render_vue_app(
Studip\VueApp::create('admission/InstantCourseSet')
->withProps($props)
);
}
$this->course_set_name = $course_set->getName();
} else {
throw new Trails_Exception(400);
throw new Trails\Exception(400);
}
}
......@@ -500,10 +483,10 @@ class Course_AdmissionController extends AuthenticatedController
$response = $this->relay('admission/courseset/configure/' . $cs->getId());
$this->body = $response->body;
if (!empty($response->headers['Location'])) {
$this->redirect($response->headers['Location']);
$this->relocate($response->headers['Location']);
}
} else {
throw new Trails_Exception(403);
throw new Trails\Exception(403);
}
}
......@@ -515,10 +498,10 @@ class Course_AdmissionController extends AuthenticatedController
$response = $this->relay('admission/courseset/save/' . $cs->getId());
$this->body = $response->body;
if ($response->headers['Location']) {
$this->redirect($response->headers['Location']);
$this->relocate($response->headers['Location']);
}
} else {
throw new Trails_Exception(403);
throw new Trails\Exception(403);
}
}
......
......@@ -165,9 +165,8 @@ class Course_ArchiveController extends AuthenticatedController
$course = Course::find($courseId);
if ($course) {
$seminar = new Seminar($course);
$coursename = $course->getFullname();
if ($seminar->delete()) {
if ($course->delete()) {
$this->deletedCourses[] = $courseId;
PageLayout::postSuccess(sprintf(
_('Die Veranstaltung %s wurde erfolgreich gelöscht.'),
......
<?php
class Course_AvatarController extends AuthenticatedController
{
public function index_action()
{
$this->course_id = Context::getId();
if (!$GLOBALS['perm']->have_studip_perm('tutor', $this->course_id)) {
throw new AccessDeniedException(_("Sie haben keine Berechtigung diese " .
"Veranstaltung zu verändern."));
}
PageLayout::setTitle(Context::getHeaderLine() . ' - ' . _('Veranstaltungsbild ändern'));
Navigation::activateItem('/course/admin/avatar');
$avatar = CourseAvatar::getAvatar($this->course_id);
$this->avatar_url = $avatar->getURL(Avatar::NORMAL);
}
}
\ No newline at end of file
......@@ -21,17 +21,14 @@ class Course_BasicdataController extends AuthenticatedController
/**
* Set up the list of input fields. Some fields may be locked for
* some reasons (lock rules, insufficient permissions etc.). This
* some reason (lock rules, insufficient permissions etc.). This
* method does not return anything, it just sets up $this->attributes
* and $this->descriptions.
*
* @param Seminar $sem
* @param Course $course
*/
private function setupInputFields($sem)
protected function setupInputFields(Course $course)
{
$course_id = $sem->getId();
$data = $sem->getData();
$this->attributes = [];
$this->attributes[] = [
'title' => _("Name der Veranstaltung"),
......@@ -39,16 +36,16 @@ class Course_BasicdataController extends AuthenticatedController
'must' => true,
'type' => 'text',
'i18n' => true,
'value' => $data['name'],
'locked' => LockRules::Check($course_id, 'Name')
'value' => $course->name,
'locked' => LockRules::Check($course->id, 'Name')
];
$this->attributes[] = [
'title' => _("Untertitel der Veranstaltung"),
'name' => "course_subtitle",
'title' => _('Untertitel der Veranstaltung'),
'name' => 'course_untertitel',
'type' => 'text',
'i18n' => true,
'value' => $data['subtitle'],
'locked' => LockRules::Check($course_id, 'Untertitel')
'value' => $course->untertitel,
'locked' => LockRules::Check($course->id, 'Untertitel')
];
$changable = true;
$this->attributes[] = [
......@@ -56,60 +53,60 @@ class Course_BasicdataController extends AuthenticatedController
'name' => 'course_status',
'must' => true,
'type' => 'select',
'value' => $data['status'],
'locked' => LockRules::Check($course_id, 'status'),
'choices' => $this->_getTypes($sem, $data, $changable),
'value' => $course->status,
'locked' => LockRules::Check($course->id, 'status'),
'choices' => $this->_getTypes($course, $changable),
'changable' => $changable,
];
$this->attributes[] = [
'title' => _("Art der Veranstaltung"),
'name' => "course_form",
'name' => 'course_art',
'type' => 'text',
'i18n' => true,
'value' => $data['form'],
'locked' => LockRules::Check($course_id, 'art')
'value' => $course->art,
'locked' => LockRules::Check($course->id, 'art')
];
$course_number_format_config = Config::get()->getMetadata('COURSE_NUMBER_FORMAT');
$this->attributes[] = [
'title' => _("Veranstaltungsnummer"),
'name' => "course_seminar_number",
'title' => _('Veranstaltungsnummer'),
'name' => 'course_veranstaltungsnummer',
'type' => 'text',
'value' => $data['seminar_number'],
'locked' => LockRules::Check($course_id, 'VeranstaltungsNummer'),
'value' => $course->veranstaltungsnummer,
'locked' => LockRules::Check($course->id, 'VeranstaltungsNummer'),
'description' => $course_number_format_config['comment'],
'pattern' => Config::get()->COURSE_NUMBER_FORMAT
];
$this->attributes[] = [
'title' => _("ECTS-Punkte"),
'name' => "course_ects",
'title' => _('ECTS-Punkte'),
'name' => 'course_ects',
'type' => 'text',
'value' => $data['ects'],
'locked' => LockRules::Check($course_id, 'ects')
'value' => $course->ects,
'locked' => LockRules::Check($course->id, 'ects')
];
$this->attributes[] = [
'title' => _("max. Teilnehmendenzahl"),
'name' => "course_admission_turnout",
'title' => _('max. Teilnehmendenzahl'),
'name' => 'course_admission_turnout',
'must' => false,
'type' => 'number',
'value' => $data['admission_turnout'],
'locked' => LockRules::Check($course_id, 'admission_turnout'),
'value' => $course->admission_turnout,
'locked' => LockRules::Check($course->id, 'admission_turnout'),
'min' => '0'
];
$this->attributes[] = [
'title' => _("Beschreibung"),
'name' => "course_description",
'title' => _('Beschreibung'),
'name' => 'course_beschreibung',
'type' => 'textarea',
'i18n' => true,
'value' => $data['description'],
'locked' => LockRules::Check($course_id, 'Beschreibung')
'value' => $course->beschreibung,
'locked' => LockRules::Check($course->id, 'Beschreibung')
];
$this->institutional = [];
$my_institutes = Institute::getMyInstitutes();
$institutes = Institute::getInstitutes();
foreach ($institutes as $institute) {
if ($institute['Institut_id'] === $data['institut_id']) {
if ($institute['Institut_id'] === $course->institut_id) {
$found = false;
foreach ($my_institutes as $inst) {
if ($inst['Institut_id'] === $institute['Institut_id']) {
......@@ -128,54 +125,53 @@ class Course_BasicdataController extends AuthenticatedController
'name' => 'course_institut_id',
'must' => true,
'type' => 'nested-select',
'value' => $data['institut_id'],
'value' => $course->institut_id,
'choices' => $this->instituteChoices($my_institutes),
'locked' => LockRules::Check($course_id, 'Institut_id')
'locked' => LockRules::Check($course->id, 'Institut_id')
];
$sem_institutes = $sem->getInstitutes();
$this->institutional[] = [
'title' => _('beteiligte Einrichtungen'),
'name' => 'related_institutes[]',
'type' => 'nested-select',
'value' => array_diff($sem_institutes, [$sem->institut_id]),
'value' => array_diff($course->institutes->pluck('id'), [$course->institut_id]),
'choices' => $this->instituteChoices($institutes),
'locked' => LockRules::Check($course_id, 'seminar_inst'),
'locked' => LockRules::Check($course->id, 'seminar_inst'),
'multiple' => true,
];
$this->descriptions = [];
$this->descriptions[] = [
'title' => _("Teilnehmende"),
'name' => "course_participants",
'title' => _('Teilnehmende'),
'name' => 'course_teilnehmer',
'type' => 'textarea',
'i18n' => true,
'value' => $data['participants'],
'locked' => LockRules::Check($course_id, 'teilnehmer')
'value' => $course->teilnehmer,
'locked' => LockRules::Check($course->id, 'teilnehmer')
];
$this->descriptions[] = [
'title' => _("Voraussetzungen"),
'name' => "course_requirements",
'title' => _('Voraussetzungen'),
'name' => 'course_vorrausetzungen',
'type' => 'textarea',
'i18n' => true,
'value' => $data['vorrausetzungen'],
'locked' => LockRules::Check($course_id, 'voraussetzungen')
'value' => $course->vorrausetzungen,
'locked' => LockRules::Check($course->id, 'voraussetzungen')
];
$this->descriptions[] = [
'title' => _("Lernorganisation"),
'name' => "course_orga",
'title' => _('Lernorganisation'),
'name' => 'course_lernorga',
'type' => 'textarea',
'i18n' => true,
'value' => $data['orga'],
'locked' => LockRules::Check($course_id, 'lernorga')
'value' => $course->lernorga,
'locked' => LockRules::Check($course->id, 'lernorga')
];
$this->descriptions[] = [
'title' => _("Leistungsnachweis"),
'name' => "course_leistungsnachweis",
'title' => _('Leistungsnachweis'),
'name' => 'course_leistungsnachweis',
'type' => 'textarea',
'i18n' => true,
'value' => $data['leistungsnachweis'],
'locked' => LockRules::Check($course_id, 'leistungsnachweis')
'value' => $course->leistungsnachweis,
'locked' => LockRules::Check($course->id, 'leistungsnachweis')
];
$this->descriptions[] = [
'title' => _("Ort") .
......@@ -186,18 +182,18 @@ class Course_BasicdataController extends AuthenticatedController
"Angaben aus Zeiten oder Sitzungsterminen gemacht werden können.") .
"</span>",
'i18n' => true,
'name' => "course_location",
'name' => 'course_ort',
'type' => 'textarea',
'value' => $data['ort'],
'locked' => LockRules::Check($course_id, 'Ort')
'value' => $course->ort,
'locked' => LockRules::Check($course->id, 'Ort')
];
$datenfelder = DataFieldEntry::getDataFieldEntries($course_id, 'sem', $data["status"]);
$datenfelder = DataFieldEntry::getDataFieldEntries($course->id, 'sem', $course->status);
if ($datenfelder) {
foreach($datenfelder as $datenfeld) {
if ($datenfeld->isVisible()) {
$locked = !$datenfeld->isEditable()
|| LockRules::Check($course_id, $datenfeld->getID());
|| LockRules::Check($course->id, $datenfeld->getID());
$desc = $locked ? _('Diese Felder werden zentral durch die zuständigen Administratoren erfasst.') : $datenfeld->getDescription();
$this->descriptions[] = [
'title' => $datenfeld->getName(),
......@@ -215,11 +211,11 @@ class Course_BasicdataController extends AuthenticatedController
}
}
$this->descriptions[] = [
'title' => _("Sonstiges"),
'name' => "course_misc",
'title' => _('Sonstiges'),
'name' => 'course_sonstiges',
'type' => 'textarea',
'value' => $data['misc'],
'locked' => LockRules::Check($course_id, 'Sonstiges')
'value' => $course->sonstiges,
'locked' => LockRules::Check($course->id, 'Sonstiges')
];
}
......@@ -286,27 +282,26 @@ class Course_BasicdataController extends AuthenticatedController
PageLayout::setHelpKeyword("Basis.VeranstaltungenVerwaltenGrunddaten");
PageLayout::setTitle(_("Verwaltung der Grunddaten"));
if ($this->course_id) {
PageLayout::setTitle(Course::find($this->course_id)->getFullname()." - ".PageLayout::getTitle());
PageLayout::setTitle(Course::find($this->course_id)->getFullName()." - ".PageLayout::getTitle());
}
//Daten sammeln:
$course = Course::find($this->course_id);
$sem = new Seminar($course);
$data = $sem->getData();
$data = $course->toRawArray();
//Erster, zweiter und vierter Reiter des Akkordions: Grundeinstellungen
$this->setupInputFields($sem);
$this->setupInputFields($course);
$sem_institutes = $sem->getInstitutes();
$sem_institutes = $course->institutes->pluck('id');
$this->dozent_is_locked = LockRules::Check($this->course_id, 'dozent');
$this->tutor_is_locked = LockRules::Check($this->course_id, 'tutor');
//Dritter Reiter: Personal
$this->dozenten = $sem->getMembers('dozent');
$instUsers = new SimpleCollection(InstituteMember::findByInstituteAndStatus($sem->getInstitutId(), 'dozent'));
$this->dozenten = $course->getMembersWithStatus('dozent');
$instUsers = new SimpleCollection(InstituteMember::findByInstituteAndStatus($course->institut_id, 'dozent'));
$this->lecturersOfInstitute = $instUsers->pluck('user_id');
if (SeminarCategories::getByTypeId($sem->status)->only_inst_user) {
if (SeminarCategories::getByTypeId($course->status)->only_inst_user) {
$search_template = "user_inst_not_already_in_sem";
} else {
$search_template = "user_not_already_in_sem";
......@@ -314,7 +309,7 @@ class Course_BasicdataController extends AuthenticatedController
$this->dozentUserSearch = new PermissionSearch(
$search_template,
sprintf(_("%s suchen"), get_title_for_status('dozent', 1, $sem->status)),
sprintf(_('%s suchen'), get_title_for_status('dozent', 1, $course->status)),
"user_id",
[
'permission' => 'dozent',
......@@ -323,25 +318,25 @@ class Course_BasicdataController extends AuthenticatedController
'institute' => $sem_institutes
]
);
$this->dozenten_title = get_title_for_status('dozent', 1, $sem->status);
$this->dozenten_title = get_title_for_status('dozent', 1, $course->status);
$this->deputies_enabled = $deputies_enabled;
if ($this->deputies_enabled) {
$this->deputies = Deputy::findDeputies($this->course_id);
$this->deputySearch = new PermissionSearch(
"user_not_already_in_sem_or_deputy",
sprintf(_("%s suchen"), get_title_for_status('deputy', 1, $sem->status)),
sprintf(_("%s suchen"), get_title_for_status('deputy', 1, $course->status)),
"user_id",
['permission' => Deputy::getValidPerms(), 'seminar_id' => $this->course_id]
);
$this->deputy_title = get_title_for_status('deputy', 1, $sem->status);
$this->deputy_title = get_title_for_status('deputy', 1, $course->status);
}
$this->tutoren = $sem->getMembers('tutor');
$this->tutoren = $course->getMembersWithStatus('tutor');
$this->tutorUserSearch = new PermissionSearch(
$search_template,
sprintf(_("%s suchen"), get_title_for_status('tutor', 1, $sem->status)),
sprintf(_('%s suchen'), get_title_for_status('tutor', 1, $course->status)),
"user_id",
['permission' => ['dozent','tutor'],
'seminar_id' => $this->course_id,
......@@ -349,8 +344,8 @@ class Course_BasicdataController extends AuthenticatedController
'institute' => $sem_institutes
]
);
$this->tutor_title = get_title_for_status('tutor', 1, $sem->status);
$instUsers = new SimpleCollection(InstituteMember::findByInstituteAndStatus($sem->getInstitutId(), 'tutor'));
$this->tutor_title = get_title_for_status('tutor', 1, $course->status);
$instUsers = new SimpleCollection(InstituteMember::findByInstituteAndStatus($course->institut_id, 'tutor'));
$this->tutorsOfInstitute = $instUsers->pluck('user_id');
unset($instUsers);
......@@ -373,23 +368,20 @@ class Course_BasicdataController extends AuthenticatedController
_('Veranstaltung kopieren'),
$this->url_for(
'course/wizard/copy/' . $this->course_id,
['studip_ticket' => Seminar_Session::get_ticket()]
['studip_ticket' => get_ticket()]
),
Icon::create('seminar')
);
}
}
$widget->addLink(_('Bild ändern'),
$this->url_for('avatar/update/course', $this->course_id),
Icon::create('edit')
);
if ($GLOBALS['perm']->have_perm('admin')) {
$is_locked = $course->lock_rule;
$widget->addLink(
_('Sperrebene ändern') . ' (' . ($is_locked ? _('gesperrt') : _('nicht gesperrt')) . ')',
$this->url_for(
'course/management/lock',
['studip_ticket' => Seminar_Session::get_ticket()]
['studip_ticket' => get_ticket()]
),
Icon::create('lock-' . ($is_locked ? 'locked' : 'unlocked'))
)->asDialog('size=auto');
......@@ -405,7 +397,7 @@ class Course_BasicdataController extends AuthenticatedController
$is_visible ? _('Veranstaltung verstecken') : _('Veranstaltung sichtbar schalten'),
$this->url_for(
'course/management/change_visibility',
['studip_ticket' => Seminar_Session::get_ticket()]
['studip_ticket' => get_ticket()]
),
Icon::create('visibility-' . ($is_visible ? 'visible' : 'invisible'))
);
......@@ -424,10 +416,11 @@ class Course_BasicdataController extends AuthenticatedController
$text = '';
}
if ($newstatus !== '' && $text !== '') {
$widget->addLink($text,
$widget->addLink(
$text,
$this->url_for('course/basicdata/switchdeputy', $this->course_id, $newstatus),
Icon::create('persons')
);
)->asButton();
}
}
if (Config::get()->ALLOW_DOZENT_DELETE || $GLOBALS['perm']->have_perm('admin')) {
......@@ -435,7 +428,7 @@ class Course_BasicdataController extends AuthenticatedController
_('Veranstaltung löschen'),
$this->url_for(
'course/archive/confirm',
['studip_ticket' => Seminar_Session::get_ticket()]
['studip_ticket' => get_ticket()]
),
Icon::create('trash')
)->asDialog('size=auto');
......@@ -445,6 +438,14 @@ class Course_BasicdataController extends AuthenticatedController
$widget = new CourseManagementSelectWidget();
$sidebar->addWidget($widget);
}
foreach ($this->flash['msg'] ?? [] as $msg) {
match ($msg[0]) {
'msg' => PageLayout::postSuccess($msg[1]),
'error' => PageLayout::postError($msg[1]),
'info' => PageLayout::postInfo($msg[1]),
};
}
}
/**
......@@ -455,18 +456,22 @@ class Course_BasicdataController extends AuthenticatedController
{
global $perm;
CSRFProtection::verifySecurityToken();
CSRFProtection::verifyUnsafeRequest();
$course_number_format = Config::get()->COURSE_NUMBER_FORMAT;
$sem = Seminar::getInstance($course_id);
$this->msg = [];
$old_settings = $sem->getSettings();
$course = Course::find($course_id);
$this->msg = [
'success' => '',
'errors' => []
];
$old_settings = $course->toRawArray();
unset($old_settings['config']);
//Seminar-Daten:
if ($perm->have_studip_perm("tutor", $sem->getId())) {
$this->setupInputFields($sem);
if ($perm->have_studip_perm('tutor', $course_id)) {
$this->setupInputFields($course);
$changemade = false;
$invalid_datafields = [];
$all_fields_types = DataFieldEntry::getDataFieldEntries($sem->id, 'sem', $sem->status);
$all_fields_types = DataFieldEntry::getDataFieldEntries($course->id, 'sem', $course->status);
$datafield_values = Request::getArray('datafields');
foreach (array_merge($this->attributes, $this->institutional, $this->descriptions) as $field) {
......@@ -482,9 +487,17 @@ class Course_BasicdataController extends AuthenticatedController
} else {
$invalid_datafields[] = $datafield->getName();
}
} else if ($field['name'] == 'related_institutes[]') {
} else if ($field['name'] === 'related_institutes[]') {
// only related_institutes supported for now
if ($sem->setInstitutes(Request::optionArray('related_institutes'))) {
$related_institute_ids = Request::optionArray('related_institutes');
$current_institute_ids = $course->institutes->pluck('id');
$current_institute_ids = array_diff($current_institute_ids, [$course->institut_id]);
$institutes_changed = count($related_institute_ids) !== count($current_institute_ids)
|| count(array_diff($current_institute_ids, $related_institute_ids)) > 0;
if ($institutes_changed) {
$course->institutes = Institute::findMany($related_institute_ids);
$changemade = true;
}
} else {
......@@ -497,14 +510,14 @@ class Course_BasicdataController extends AuthenticatedController
}
if ($varname === "name" && !$req_value) {
$this->msg[] = ["error", _("Name der Veranstaltung darf nicht leer sein.")];
$this->msg['error'][] = _('Name der Veranstaltung darf nicht leer sein.');
} elseif ($varname === "seminar_number" && $req_value && $course_number_format &&
!preg_match('/^' . $course_number_format . '$/', $req_value)) {
$this->msg[] = ['error', _('Die Veranstaltungsnummer hat ein ungültiges Format.')];
$this->msg['error'][] = _('Die Veranstaltungsnummer hat ein ungültiges Format.');
} else if ($field['type'] == 'select' && !in_array($req_value, array_flatten(array_values(array_map('array_keys', $field['choices']))))) {
// illegal value - just ignore this
} else if ($sem->{$varname} != $req_value) {
$sem->{$varname} = $req_value;
} else if ($course->getValue($varname) != $req_value) {
$course->setValue($varname, $req_value);
$changemade = true;
}
}
......@@ -518,23 +531,25 @@ class Course_BasicdataController extends AuthenticatedController
count($invalid_datafields)
);
$message = sprintf($message, join(', ', array_map('htmlReady', $invalid_datafields)));
$this->msg[] = ['error', $message];
$this->msg['error'][] = $message;
}
$sem->store();
$course->store();
// Logging
$before = array_diff_assoc($old_settings, $sem->getSettings());
$after = array_diff_assoc($sem->getSettings(), $old_settings);
$current_settings = $course->toRawArray();
unset($current_settings['config']);
$before = array_diff_assoc($old_settings, $current_settings);
$after = array_diff_assoc($current_settings, $old_settings);
//update admission, if turnout was raised
if (
!empty($after['admission_turnout'])
&& !empty($before['admission_turnout'])
&& $after['admission_turnout'] > $before['admission_turnout']
&& $sem->isAdmissionEnabled()
&& $course->isAdmissionEnabled()
) {
AdmissionApplication::addMembers($sem->getId());
AdmissionApplication::addMembers($course_id);
}
if (sizeof($before) && sizeof($after)) {
......@@ -542,30 +557,30 @@ class Course_BasicdataController extends AuthenticatedController
foreach ($before as $k => $v) {
$log_message .= "$k: $v => " . $after[$k] . " \n";
}
StudipLog::log('CHANGE_BASIC_DATA', $sem->getId(), " ", $log_message);
NotificationCenter::postNotification('SeminarBasicDataDidUpdate', $sem->id , $GLOBALS['user']->id);
StudipLog::log('CHANGE_BASIC_DATA', $course_id, ' ', $log_message);
NotificationCenter::postNotification('SeminarBasicDataDidUpdate', $course->id , $GLOBALS['user']->id);
}
// end of logging
if ($changemade) {
$this->msg[] = ["msg", _("Die Grunddaten der Veranstaltung wurden verändert.")];
$this->msg['success'] = _('Die Grunddaten der Veranstaltung wurden verändert.');
}
} else {
$this->msg[] = ["error", _("Sie haben keine Berechtigung diese Veranstaltung zu verändern.")];
$this->msg['error'][] = _('Sie haben keine Berechtigung diese Veranstaltung zu verändern.');
}
//Labels/Funktionen für Dozenten und Tutoren
if ($perm->have_studip_perm('dozent', $sem->getId())) {
if ($perm->have_studip_perm('dozent', $course_id)) {
foreach (Request::getArray('label') as $user_id => $label) {
if ($GLOBALS['perm']->have_studip_perm('tutor', $sem->getId(), $user_id)) {
$mb = CourseMember::findOneBySQL('user_id = ? AND Seminar_id = ?', [$user_id, $sem->getId()]);
if ($GLOBALS['perm']->have_studip_perm('tutor', $course_id, $user_id)) {
$mb = CourseMember::findOneBySQL('user_id = ? AND Seminar_id = ?', [$user_id, $course_id]);
if ($mb) {
$mb->label = $label;
if ($mb->store()) {
NotificationCenter::postNotification(
'CourseDidChangeMemberLabel',
$sem,
$course,
$mb
);
}
......@@ -574,55 +589,68 @@ class Course_BasicdataController extends AuthenticatedController
}
}
foreach($sem->getStackedMessages() as $key => $messages) {
foreach($messages['details'] as $message) {
$this->msg[] = [($key !== "success" ? $key : "msg"), $message];
}
if (!empty($this->msg['error'])) {
PageLayout::postError(
_('Die folgenden Fehler traten auf:'),
$this->msg['error']
);
} elseif ($this->msg['success']) {
PageLayout::postSuccess($this->msg['success']);
}
$this->flash['msg'] = $this->msg;
$this->flash['open'] = Request::get("open");
if (Request::isDialog()) {
$this->response->add_header('X-Dialog-Close', 1);
$this->response->add_header('X-Dialog-Execute', 'STUDIP.AdminCourses.App.loadCourse');
$this->render_text($course_id);
} else {
$this->redirect($this->url_for('course/basicdata/view/' . $sem->getId()));
$this->redirect($this->url_for('course/basicdata/view/' . $course_id));
}
}
public function add_member_action($course_id, $status = 'dozent')
{
CSRFProtection::verifySecurityToken();
if (!$GLOBALS['perm']->have_studip_perm('dozent', $course_id)) {
throw new AccessDeniedException();
}
// We don't need to check the csrf protection at this point since it
// is already checked by the multiperson search endpoint
// load MultiPersonSearch object
$mp = MultiPersonSearch::load("add_member_{$status}{$course_id}");
switch($status) {
case 'tutor' :
$func = 'addTutor';
break;
case 'deputy':
$func = 'addDeputy';
break;
default:
$func = 'addTeacher';
break;
}
$course = Course::find($course_id);
$succeeded = [];
$failed = [];
foreach ($mp->getAddedUsers() as $a) {
$result = $this->$func($a, $course_id);
if ($result !== false) {
$succeeded[] = User::find($a)->getFullname('no_title_rev');
$user = User::find($a);
$result = false;
if ($status === 'deputy') {
$result = Deputy::addDeputy($user->id, $course->id) > 0;
} else {
$failed[] = User::find($a)->getFullname('no_title_rev');
try {
$course->addMember(
$user,
$status === 'tutor' ? 'tutor' : 'dozent'
);
$result = true;
} catch (\Studip\EnrolmentException $e) {
$result = $e->getMessage();
}
}
if ($result === true) {
$succeeded[] = $user->getFullName('no_title_rev');
} elseif (is_string($result)) {
$failed[] = [$user->getFullName('no_title_rev'), $result];
} else {
$failed[] = [$user->getFullName('no_title_rev')];
}
}
// Only show the success messagebox once
if ($succeeded) {
$sem = Seminar::GetInstance($course_id);
$status_title = get_title_for_status($status, count($succeeded), $sem->status);
$status_title = get_title_for_status($status, count($succeeded), $course->status);
if (count($succeeded) > 1) {
$messagetext = sprintf(
_("%u %s wurden hinzugefügt."),
......@@ -644,9 +672,17 @@ class Course_BasicdataController extends AuthenticatedController
// only show an error messagebox once with list of errors!
if ($failed) {
$messages = [];
foreach ($failed as $fail) {
if (is_array($fail)) {
$messages[] = sprintf('%s: %s', $fail[0], $fail[1]);
} else {
$messages[] = $fail;
}
}
PageLayout::postError(
_('Bei den folgenden Nutzer/-innen ist ein Fehler aufgetreten') ,
array_map('htmlReady', $failed)
array_map('htmlReady', $messages)
);
}
$this->flash['open'] = 'bd_personal';
......@@ -655,72 +691,28 @@ class Course_BasicdataController extends AuthenticatedController
$this->redirect($this->url_for($redirect));
}
private function addTutor($tutor, $course_id)
{
//Tutoren hinzufügen:
if ($GLOBALS['perm']->have_studip_perm('tutor', $course_id)) {
$sem = Seminar::GetInstance($course_id);
if ($sem->addMember($tutor, 'tutor')) {
// Check if we need to add user to course parent as well.
if ($sem->parent_course) {
$this->addTutor($tutor, $sem->parent_course);
}
return true;
}
}
return false;
}
private function addDeputy($user_id, $course_id)
{
//Vertretung hinzufügen:
if ($GLOBALS['perm']->have_studip_perm('dozent', $course_id)) {
$sem = Seminar::GetInstance($course_id);
if (Deputy::addDeputy($user_id, $sem->getId())) {
return true;
}
}
return false;
}
private function addTeacher($dozent, $course_id)
/**
* A helper method since the steps for removing someone are all the same in this controller.
* Only the actions differ.
*
* @param Course $course The course from which to remove a user.
* @param User $user The user to be removed.
* @return void
*/
protected function deleteUserFromCourse(Course $course, User $user)
{
$deputies_enabled = Config::get()->DEPUTIES_ENABLE;
$sem = Seminar::GetInstance($course_id);
if ($GLOBALS['perm']->have_studip_perm('dozent', $course_id)) {
if ($sem->addMember($dozent, 'dozent')) {
// Check if we need to add user to course parent as well.
if ($sem->parent_course) {
$this->addTeacher($dozent, $sem->parent_course);
try {
$course->deleteMember($user);
} catch (\Studip\Exception $e) {
PageLayout::postError(_('Ein Fehler ist aufgetreten.'), $e->getMessage());
return;
}
// Only applicable when globally enabled and user deputies enabled too
if ($deputies_enabled) {
// Check whether chosen person is set as deputy
// -> delete deputy entry.
$deputy = Deputy::find([$course_id, $dozent]);
if ($deputy) {
$deputy->delete();
}
// Add default deputies of the chosen lecturer...
if (Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) {
$deputies = Deputy::findDeputies($dozent)->pluck('user_id');
$lecturers = $sem->getMembers();
foreach ($deputies as $deputy) {
// ..but only if not already set as lecturer or deputy.
if (!isset($lecturers[$deputy])) {
Deputy::addDeputy($deputy, $course_id);
}
}
}
}
return true;
}
}
return false;
PageLayout::postSuccess(
studip_interpolate(
_('%{name} wurde aus der Veranstaltung ausgetragen.'),
['name' => $user->getFullName()]
)
);
}
/**
......@@ -739,24 +731,10 @@ class Course_BasicdataController extends AuthenticatedController
} elseif ($teacher_id === $GLOBALS['user']->id) {
PageLayout::postError(_('Sie dürfen sich nicht selbst aus der Veranstaltung austragen.'));
} else {
$sem = Seminar::getInstance($course_id);
$sem->deleteMember($teacher_id);
// Remove user from subcourses as well.
foreach ($sem->children as $child) {
$child->deleteMember($teacher_id);
}
$this->msg = [];
foreach ($sem->getStackedMessages() as $key => $messages) {
foreach ($messages['details'] as $message) {
$this->msg[] = [
$key !== 'success' ? $key : 'msg',
$message
];
}
}
$this->flash['msg'] = $this->msg;
$this->deleteUserFromCourse(
Course::find($course_id),
User::find($teacher_id)
);
}
$this->flash['open'] = 'bd_personal';
......@@ -779,23 +757,23 @@ class Course_BasicdataController extends AuthenticatedController
} elseif ($deputy_id === $GLOBALS['user']->id) {
PageLayout::postError(_('Sie dürfen sich nicht selbst aus der Veranstaltung austragen.'));
} else {
$sem = Seminar::getInstance($course_id);
$course = Course::find($course_id);
$deputy = Deputy::find([$course_id, $deputy_id]);
if ($deputy && $deputy->delete()) {
// Remove user from subcourses as well.
if (count($sem->children)) {
$children_ids = $sem->children->pluck('seminar_id');
if (count($course->children) > 0) {
$children_ids = $course->children->pluck('seminar_id');
Deputy::deleteBySQL('user_id = ? AND range_id IN (?)', [$deputy_id, $children_ids]);
}
PageLayout::postSuccess(sprintf(
_('%s wurde entfernt.'),
htmlReady(get_title_for_status('deputy', 1, $sem->status))
htmlReady(get_title_for_status('deputy', 1, $course->status))
));
} else {
PageLayout::postError(sprintf(
_('%s konnte nicht entfernt werden.'),
htmlReady(get_title_for_status('deputy', 1, $sem->status))
htmlReady(get_title_for_status('deputy', 1, $course->status))
));
}
}
......@@ -818,24 +796,10 @@ class Course_BasicdataController extends AuthenticatedController
if (!$GLOBALS['perm']->have_studip_perm('dozent', $course_id)) {
PageLayout::postError( _('Sie haben keine Berechtigung diese Veranstaltung zu verändern.'));
} else {
$sem = Seminar::getInstance($course_id);
$sem->deleteMember($tutor_id);
// Remove user from subcourses as well.
foreach ($sem->children as $child) {
$child->deleteMember($tutor_id);
}
$this->msg = [];
foreach ($sem->getStackedMessages() as $key => $messages) {
foreach ($messages['details'] as $message) {
$this->msg[] = [
$key !== 'success' ? $key : 'msg',
$message
];
}
}
$this->flash['msg'] = $this->msg;
$this->deleteUserFromCourse(
Course::find($course_id),
User::find($tutor_id)
);
}
$this->flash['open'] = 'bd_personal';
......@@ -843,82 +807,68 @@ class Course_BasicdataController extends AuthenticatedController
}
/**
* Falls eine Person in der >>Reihenfolge<< hochgestuft werden soll.
* Leitet danach weiter auf View und öffnet den Reiter Personal.
* Moves a course member up one position in the position list for the
* corresponding permission level in the course.
*
* @param md5 $user_id
* @param string $status
* @param string $course_id The course where to increase the priority.
*
* @param string $user_id The user for whom to increase the priority.
*
* @param string $status The permission level. This is an unused parameter that is only kept
* for compatibility reasons.
*/
public function priorityupfor_action($course_id, $user_id, $status = "dozent")
public function priorityupfor_action(string $course_id, string $user_id, string $status = 'dozent')
{
global $user, $perm;
CSRFProtection::verifySecurityToken();
CSRFProtection::verifyUnsafeRequest();
$sem = Seminar::getInstance($course_id);
$course = Course::find($course_id);
$user = User::find($user_id);
$this->msg = [];
if ($perm->have_studip_perm("dozent", $sem->getId())) {
$teilnehmer = $sem->getMembers($status);
$members = [];
foreach($teilnehmer as $key => $member) {
$members[] = $member["user_id"];
}
foreach($members as $key => $member) {
if ($key > 0 && $member == $user_id) {
$temp_member = $members[$key-1];
$members[$key-1] = $member;
$members[$key] = $temp_member;
if ($GLOBALS['perm']->have_studip_perm('dozent', $course->id)) {
if ($course->moveMemberUp($user) < 0) {
$this->msg[] = ['error', _('Die Person konnte nicht nach oben verschoben werden.')];
}
}
$sem->setMemberPriority($members);
} else {
$this->msg[] = ["error", _("Sie haben keine Berechtigung diese Veranstaltung zu verändern.")];
}
$this->flash['msg'] = $this->msg;
$this->flash['open'] = "bd_personal";
$this->redirect($this->url_for('course/basicdata/view/' . $sem->getId()));
$this->redirect($this->url_for('course/basicdata/view/' . $course->id));
}
/**
* Falls eine Person in der >>Reihenfolge<< runtergestuft werden soll.
* Leitet danach weiter auf View und öffnet den Reiter Personal.
* Moves a course member down one position in the position list for the
* corresponding permission level in the course.
*
* @param md5 $user_id
* @param string $status
* @param string $course_id The course where to decrease the priority.
*
* @param string $user_id The user for whom to decrease the priority.
*
* @param string $status The permission level. This is an unused parameter that is only kept
* for compatibility reasons.
*/
public function prioritydownfor_action($course_id, $user_id, $status = "dozent")
public function prioritydownfor_action($course_id, $user_id, $status = 'dozent')
{
global $user, $perm;
CSRFProtection::verifySecurityToken();
CSRFProtection::verifyUnsafeRequest();
$sem = Seminar::getInstance($course_id);
$course = Course::find($course_id);
$user = User::find($user_id);
$this->msg = [];
if ($perm->have_studip_perm("dozent", $sem->getId())) {
$teilnehmer = $sem->getMembers($status);
$members = [];
foreach($teilnehmer as $key => $member) {
$members[] = $member["user_id"];
}
foreach($members as $key => $member) {
if ($key < count($members)-1 && $member == $user_id) {
$temp_member = $members[$key+1];
$members[$key+1] = $member;
$members[$key] = $temp_member;
if ($GLOBALS['perm']->have_studip_perm('dozent', $course->id)) {
if ($course->moveMemberDown($user) < 0) {
$this->msg[] = ['error', _('Die Person konnte nicht nach unten verschoben werden.')];
}
}
$sem->setMemberPriority($members);
} else {
$this->msg[] = ["error", _("Sie haben keine Berechtigung diese Veranstaltung zu verändern.")];
$this->msg[] = ['error', _('Sie haben keine Berechtigung diese Veranstaltung zu verändern.')];
}
$this->flash['msg'] = $this->msg;
$this->flash['open'] = "bd_personal";
$this->redirect($this->url_for('course/basicdata/view/' . $sem->getId()));
$this->redirect($this->url_for('course/basicdata/view/' . $course->id));
}
public function switchdeputy_action($course_id, $newstatus)
{
CSRFProtection::verifySecurityToken();
CSRFProtection::verifyUnsafeRequest();
switch($newstatus) {
case 'dozent':
......@@ -953,29 +903,38 @@ class Course_BasicdataController extends AuthenticatedController
$this->redirect($this->url_for('course/basicdata/view/'.$course_id));
}
private function _getTypes($sem, $data, &$changable = true)
private function _getTypes(Course $course, &$changable = true)
{
$sem_types = [];
$sem_classes = [];
if ($GLOBALS['perm']->have_perm("admin")) {
foreach (SemClass::getClasses() as $sc) {
if (!$sc['course_creation_forbidden']) {
$sem_types[$sc['name']] = array_map(function ($st) {
return $st['name'];
}, $sc->getSemTypes());
$sem_classes[] = $sc;
}
}
} else {
$sc = $sem->getSemClass();
$sem_classes[] = $course->getSemClass();
}
if (!$course->isStudyGroup()) {
$sem_classes = array_filter($sem_classes, function (SemClass $sc) {
return !$sc['studygroup_mode'];
});
}
foreach ($sem_classes as $sc) {
$sem_types[$sc['name']] = array_map(function ($st) {
return $st['name'];
}, $sc->getSemTypes());
}
if (!in_array($data['status'], array_flatten(array_values(array_map('array_keys', $sem_types))))) {
$class_name = $sem->getSemClass()->offsetGet('name');
if (!in_array($course->status, array_flatten(array_values(array_map('array_keys', $sem_types))))) {
$class_name = $course->getSemClass()->offsetGet('name');
if (!isset($sem_types[$class_name])) {
$sem_types[$class_name] = [];
}
$sem_types[$class_name][] = $sem->getSemType()->offsetGet('name');
$sem_types[$class_name][] = $course->getSemType()->offsetGet('name');
$changable = false;
}
......
......@@ -33,10 +33,10 @@ class Course_BlockAppointmentsController extends AuthenticatedController
SeminarCategories::GetBySeminarId($this->course_id)->studygroup_mode ||
!$GLOBALS['perm']->have_studip_perm("tutor", $this->course_id)
) {
throw new Trails_Exception(400);
throw new Trails\Exception(400);
}
PageLayout::setHelpKeyword('Basis.VeranstaltungenVerwaltenAendernVonZeitenUndTerminen');
PageLayout::setTitle(Course::findCurrent()->getFullname() . " - " . _('Blockveranstaltungstermine anlegen'));
PageLayout::setTitle(Course::findCurrent()->getFullName() . " - " . _('Blockveranstaltungstermine anlegen'));
}
......@@ -242,10 +242,10 @@ class Course_BlockAppointmentsController extends AuthenticatedController
$result = $d->store();
} else {
$result = $d->store();
$singledate = new SingleDate($d);
$singledate->bookRoom(Request::option('room_id'));
$room = Resource::find(Request::option('room_id'))?->getDerivedClassInstance();
$d->bookRoom($room);
}
return $result ? $d->getFullname() : null;
return $result ? $d->getFullName() : null;
}, $dates));
if ($date_count > 1) {
$dates_created = array_count_values($dates_created);
......
......@@ -25,24 +25,24 @@ class Course_CancelDatesController extends AuthenticatedController
parent::before_filter($action, $args);
if (Request::get('termin_id')) {
$this->dates[0] = new SingleDate(Request::option('termin_id'));
$this->dates[0] = CourseDate::find(Request::option('termin_id'));
$this->course_id = $this->dates[0]->range_id;
}
if (Request::get('issue_id')) {
$this->issue_id = Request::option('issue_id');
$this->dates = array_values(array_map(function ($data) {
$d = new SingleDate();
$d->fillValuesFromArray($data);
return $d;
}, IssueDB::getDatesforIssue(Request::option('issue_id'))));
$this->dates = CourseDate::findBySQL(
"JOIN `themen_termine` USING (`termin_id`)
WHERE `issue_id` = :issue_id ORDER BY `date`",
['issue_id' => Request::option('issue_id')]
);
$this->course_id = $this->dates[0]->range_id;
}
if (!get_object_type($this->course_id, ['sem']) || !$perm->have_studip_perm("tutor", $this->course_id)) {
throw new Trails_Exception(400);
throw new Trails\Exception(400);
}
PageLayout::setHelpKeyword('Basis.VeranstaltungenVerwaltenAendernVonZeitenUndTerminen');
PageLayout::setTitle(Course::findCurrent()->getFullname() . " - " . _('Veranstaltungstermine absagen'));
PageLayout::setTitle(Course::findCurrent()->getFullName() . " - " . _('Veranstaltungstermine absagen'));
}
public function index_action()
......@@ -52,13 +52,13 @@ class Course_CancelDatesController extends AuthenticatedController
public function store_action()
{
CSRFProtection::verifyUnsafeRequest();
$sem = Seminar::getInstance($this->course_id);
$msg = '';
foreach ($this->dates as $date) {
$sem->cancelSingleDate($date->getTerminId(), $date->getMetadateId());
$date->setComment(Request::get('cancel_dates_comment'));
$date->setExTermin(true);
$date->store();
$ex_date = $date->cancelDate();
if ($ex_date) {
$ex_date->content = Request::get('cancel_dates_comment');
$ex_date->store();
}
}
if (Request::int('cancel_dates_snd_message') && count($this->dates) > 0) {
$snd_messages = raumzeit_send_cancel_message(Request::get('cancel_dates_comment'), $this->dates);
......@@ -67,7 +67,7 @@ class Course_CancelDatesController extends AuthenticatedController
}
}
PageLayout::postSuccess(_('Folgende Termine wurden abgesagt') . ($msg ? ' (' . $msg . '):' : ':'), array_map(function ($d) {
return $d->toString();
return $d->getFullName();
}, $this->dates));
$this->redirect($this->url_for('course/dates'));
......
......@@ -16,7 +16,6 @@
*/
class Course_ChangeViewController extends AuthenticatedController
{
// see Trails_Controller#before_filter
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
......@@ -28,7 +27,7 @@ class Course_ChangeViewController extends AuthenticatedController
* Sets the current course into participant view.
* Only available for tutor upwards.
*
* @throws Trails_Exception Someone with unfitting rights tried to call here.
* @throws Trails\Exception Someone with unfitting rights tried to call here.
*/
public function set_changed_view_action()
{
......@@ -43,7 +42,7 @@ class Course_ChangeViewController extends AuthenticatedController
* Resets a course currently in participant view to normal view
* with real rights.
*
* @throws Trails_Exception Someone with unfitting rights tried to call here.
* @throws Trails\Exception Someone with unfitting rights tried to call here.
*/
public function reset_changed_view_action()
{
......
<?php
class Course_ConnectedcoursesController extends AuthenticatedController
{
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
if (!$GLOBALS['perm']->have_studip_perm('tutor', Context::getId())) {
throw new AccessDeniedException();
}
}
public function index_action()
{
Navigation::activateItem('/course/admin/connectedcourses');
$this->connected = StudygroupCourse::findBySQL(
'INNER JOIN seminare ON (seminare.Seminar_id = studygroup_courses.course_id) WHERE studygroup_courses.studygroup_id = ? ORDER BY seminare.name ASC',
[
Context::getId()
]
);
$this->proposals = StudygroupCourseProposal::findBySQL(
'INNER JOIN seminare ON (seminare.Seminar_id = studygroup_courses_proposals.studygroup_id) WHERE studygroup_courses_proposals.studygroup_id = ? ORDER BY seminare.name ASC',
[
Context::getId()
]
);
$this->buildSidebar();
}
public function connect_action($course_id = null)
{
Navigation::activateItem('/course/admin/connectedcourses');
PageLayout::setTitle(_('Veranstaltung suchen und zur Verknüpfung vorschlagen'));
if (Request::isPost() && (Request::option('course_id') || $course_id)) {
CSRFProtection::verifySecurityToken();
$course_id = $course_id ?? Request::option('course_id');
$status = StudygroupModel::proposeAsStudygroupTo(Context::get(), $course_id);
if ($status === 'connected') {
PageLayout::postSuccess(_('Veranstaltung wurde verknüpft.'));
}
if ($status === 'proposed') {
PageLayout::postSuccess(_('Vorschlag wurde eingereicht.'));
}
$this->redirect('course/connectedcourses/index');
return;
}
$connected = StudygroupCourse::findBySQL(
'INNER JOIN seminare ON (seminare.Seminar_id = studygroup_courses.course_id) WHERE studygroup_courses.studygroup_id = ? ORDER BY seminare.name ASC',
[
Context::getId()
]
);
$proposals = StudygroupCourseProposal::findBySQL(
'INNER JOIN seminare ON (seminare.Seminar_id = studygroup_courses_proposals.studygroup_id) WHERE studygroup_courses_proposals.studygroup_id = ? ORDER BY seminare.name ASC',
[
Context::getId()
]
);
$already_covered = array_map(function($c) { return $c->course_id; }, $connected);
$already_covered = $already_covered + array_map(function($c) { return $c->course_id; }, $proposals);
$studygroup_ids = [];
foreach (SemClass::getClasses() as $sem_class) {
if ($sem_class['studygroup_mode'] > 0) {
foreach ($sem_class->getSemTypes() as $sem_type) {
$studygroup_ids[] = $sem_type['id'];
}
}
}
$this->my_courses = [];
if (!$GLOBALS['perm']->have_perm('admin')) {
$this->my_courses = Course::findBySQL('INNER JOIN `seminar_user` USING (`Seminar_id`)
LEFT JOIN `semester_courses` ON (`seminare`.`Seminar_id` = `semester_courses`.`course_id`)
WHERE `seminar_user`.`user_id` = :user_id
AND `seminare`.`status` NOT IN (:studygroup_sem_types)
AND (`semester_courses`.`semester_id` IS NULL OR `semester_courses`.`semester_id` = :semester_id)
AND `seminare`.`Seminar_id` NOT IN (:ignore)
ORDER BY `seminare`.`name` ASC ',
[
'user_id' => User::findCurrent()->id,
'studygroup_sem_types' => $studygroup_ids,
'semester_id' => Request::get('semester_id') ?? Semester::findCurrent()->id,
'ignore' => count($already_covered) ? $already_covered : ''
]);
foreach ($this->my_courses as $my_course) {
$already_covered[] = $my_course->id;
}
}
if (Request::get('search') && Request::get('search') != 1) {
//do the search:
$query = SQLQuery::table('seminare')
->where('search',
'`name` LIKE :search OR `VeranstaltungsNummer` LIKE :search',
['search' => '%' . Request::get('search') . '%']
)
->where(
'studygroups',
'`seminare`.`status` NOT IN (:sem_type_ids)',
['sem_type_ids' => $studygroup_ids]
)
->groupBy('`seminare`.`Seminar_id`');
if (count($already_covered) > 0) {
$query->where(
'ignore',
'`seminare`.`Seminar_id` NOT IN (:ignore)',
['ignore' => $already_covered]
);
}
if (!empty(Request::get('semester_id'))) {
$query->join(
'semester_courses',
'semester_courses',
'`semester_courses`.`course_id` = `seminare`.`Seminar_id`',
'LEFT JOIN'
);
$query->where(
'semester_id',
'semester_courses.semester_id = :semester_id OR semester_courses.semester_id IS NULL',
['semester_id' => Request::get('semester_id')]
);
}
$this->searchresults = $query->fetchAll(Course::class);
} else {
//get up to 10 courses with a lot of members of the current studygroup:
$statement = DBManager::get()->prepare("
SELECT `seminare`.*
FROM `seminar_user`
INNER JOIN `seminare` ON (`seminare`.`Seminar_id` = `seminar_user`.`Seminar_id`)
INNER JOIN `seminar_user` AS `su2` ON (`su2`.`user_id` = `seminar_user`.`user_id` AND `su2`.`Seminar_id` = :course_id)
LEFT JOIN `studygroup_courses` ON (`studygroup_courses`.`course_id` = `seminare`.`Seminar_id` AND `studygroup_courses`.`studygroup_id` = `su2`.`Seminar_id`)
WHERE `seminare`.`status` NOT IN (:studygroup_sem_types)
AND `studygroup_courses`.`id` IS NULL
AND `seminare`.`Seminar_id` NOT IN (:ignore)
GROUP BY `seminare`.`Seminar_id`
HAVING COUNT(`seminar_user`.`user_id`) > 1
ORDER BY COUNT(`seminar_user`.`user_id`) DESC
LIMIT 20
");
$statement->execute([
'course_id' => Context::getId(),
'studygroup_sem_types' => $studygroup_ids,
'ignore' => count($already_covered) ? $already_covered : ''
]);
$suggestions = $statement->fetchAll(PDO::FETCH_ASSOC);
$this->suggestions = array_map(function ($d) {
return Course::buildExisting($d);
}, $suggestions);
}
}
public function remove_action($course_id)
{
if (Request::isPost() && $course_id) {
CSRFProtection::verifySecurityToken();
StudygroupCourse::deleteBySQL('course_id = ? AND studygroup_id = ?', [
$course_id,
Context::getId()
]);
PageLayout::postSuccess(_('Verknüpfung zu der Veranstaltung wurde aufgehoben.'));
}
$this->redirect('course/connectedcourses/index');
}
public function decline_action(StudygroupCourseProposal $proposal)
{
if (Request::isPost()) {
CSRFProtection::verifySecurityToken();
if ($GLOBALS['perm']->have_studip_perm('tutor', $proposal['course_id']) || $GLOBALS['perm']->have_studip_perm('tutor', $proposal['studygroup_id'])) {
if ($proposal['proposed_from'] === 'course') {
PageLayout::postSuccess(_('Vorschlag wurde abgewiesen.'));
$statement = DBManager::get()->prepare("
SELECT `username`, `user_id`
FROM `auth_user_md5`
INNER JOIN `seminar_user` USING (`user_id`)
WHERE `seminar_user`.`Seminar_id` = ? AND `seminar_user`.`status` IN ('tutor', 'dozent')
");
$statement->execute([$proposal['course_id']]);
$messaging = new messaging();
foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $user_data) {
setTempLanguage($user_data['user_id']);
$messaging->insert_message(
sprintf(
_('Ihr Vorschlag, die Studiengruppe „%1$s“ mit der Veranstaltung „%2$s“ zu verknüpfen, wurde leider abgewiesen.'),
Context::get()->getFullname(),
$proposal->studygroup->getFullname()
),
$user_data['username'],
'____%system%____',
'',
'',
'',
'',
_('Verknüpfungsvorschlag abgewiesen'),
'',
'normal',
['Studiengruppe']
);
restoreLanguage();
}
} else {
PageLayout::postSuccess(_('Vorschlag wurde zurückgezogen.'));
}
$proposal->delete();
}
}
$this->redirect('course/connectedcourses/index');
}
protected function buildSidebar()
{
$actions = new ActionsWidget();
$actions->addLink(
_('Verknüpfung vorschlagen'),
$this->url_for('course/connectedcourses/connect'),
Icon::create('add'),
['data-dialog' => 1]
);
Sidebar::Get()->addWidget($actions);
}
}
<?php
class Course_ConnectedstudygroupsController extends AuthenticatedController
{
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
if (!$GLOBALS['perm']->have_studip_perm('tutor', Context::getId())) {
throw new AccessDeniedException();
}
}
public function index_action()
{
Navigation::activateItem('/course/admin/connectedstudygroups');
$this->connected = StudygroupCourse::findBySQL(
'INNER JOIN seminare ON (seminare.Seminar_id = studygroup_courses.studygroup_id) WHERE studygroup_courses.course_id = ? ORDER BY seminare.name ASC',
[
Context::getId()
]
);
$this->proposals = StudygroupCourseProposal::findBySQL(
'INNER JOIN seminare ON (seminare.Seminar_id = studygroup_courses_proposals.studygroup_id) WHERE studygroup_courses_proposals.course_id = ? ORDER BY seminare.name ASC',
[
Context::getId()
]
);
$this->buildSidebar();
}
public function connect_action($course_id = null)
{
Navigation::activateItem('/course/admin/connectedstudygroups');
PageLayout::setTitle(_('Studiengruppe suchen und verknüpfen'));
if (Request::isPost() && (Request::option('course_id') || $course_id)) {
CSRFProtection::verifySecurityToken();
$course_id = $course_id ?? Request::option('course_id');
$proposal = StudygroupCourseProposal::findOneBySQL('course_id = ? AND studygroup_id = ?', [
Context::getId(),
$course_id
]);
if ($GLOBALS['perm']->have_studip_perm('tutor', $course_id) || $proposal['proposed_from'] === 'studygroup') {
$connection = StudygroupCourse::findOneBySQL('course_id = ? AND studygroup_id = ?', [
Context::getId(),
$course_id
]);
if (!$connection) {
$connection = new StudygroupCourse();
$connection['course_id'] = Context::getId();
$connection['studygroup_id'] = $course_id;
$connection->store();
}
if ($proposal) {
if ($proposal['proposed_from'] === 'studygroup') {
$statement = DBManager::get()->prepare("
SELECT `username`, `user_id`
FROM `auth_user_md5`
INNER JOIN `seminar_user` USING (`user_id`)
WHERE `seminar_user`.`Seminar_id` = ? AND `seminar_user`.`status` IN ('tutor', 'dozent')
");
$statement->execute([$course_id]);
$messaging = new messaging();
foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $user_data) {
setTempLanguage($user_data['user_id']);
$messaging->insert_message(
sprintf(
_('Ihr Vorschlag, die Studiengruppe „%1$s“ mit der Veranstaltung „%2$s“ zu verknüpfen, wurde angenommen.'),
Context::get()->getFullname(),
Course::find($course_id)->getFullname()
),
$user_data['username'],
'____%system%____',
'',
'',
'',
'',
_('Verknüpfungsvorschlag angenommen'),
'',
'normal',
['Studiengruppe']
);
restoreLanguage();
}
}
$proposal->delete();
}
PageLayout::postSuccess(_('Veranstaltung wurde verknüpft.'));
} else {
//send message:
if (!$proposal) {
$proposal = new StudygroupCourseProposal();
$proposal['course_id'] = Context::getId();
$proposal['studygroup_id'] = $course_id;
$proposal['proposed_from'] = 'course';
$proposal['user_id'] = User::findCurrent()->id;
$proposal->store();
$statement = DBManager::get()->prepare("
SELECT `username`, `user_id`
FROM `auth_user_md5`
INNER JOIN `seminar_user` USING (`user_id`)
WHERE `seminar_user`.`Seminar_id` = ? AND `seminar_user`.`status` IN ('tutor', 'dozent')
");
$statement->execute([$course_id]);
$messaging = new messaging();
$oldbase = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']);
foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $user_data) {
setTempLanguage($user_data['user_id']);
$messaging->insert_message(
sprintf(
_('Es wurde vorgeschlagen, die Veranstaltung „%1$s“ mit Ihrer Studiengruppe „%2$s“ zu verknüpfen. Sie können den Vorschlag unter folgendem Link annehmen oder ablehnen:'),
Context::get()->getFullname(),
Course::find($course_id)->getFullname()
)."\n\n".URLHelper::getURL('dispatch.php/course/connectedcourses/index', ['cid' => $course_id]),
$user_data['username'],
'____%system%____',
'',
'',
'',
'',
_('Verknüpfung Ihrer Studiengruppe zu einer Veranstaltung'),
'',
'normal',
['Studiengruppe']
);
restoreLanguage();
}
URLHelper::setBaseURL($oldbase);
}
PageLayout::postSuccess(_('Antrag wurde gestellt.'));
}
$this->redirect('course/connectedstudygroups/index');
return;
}
$connected = StudygroupCourse::findBySQL(
'INNER JOIN seminare ON (seminare.Seminar_id = studygroup_courses.studygroup_id) WHERE studygroup_courses.course_id = ? ORDER BY seminare.name ASC',
[
Context::getId()
]
);
$proposals = StudygroupCourseProposal::findBySQL(
'INNER JOIN seminare ON (seminare.Seminar_id = studygroup_courses_proposals.course_id) WHERE studygroup_courses_proposals.course_id = ? ORDER BY seminare.name ASC',
[
Context::getId()
]
);
$already_covered = array_map(function ($c) {
return $c->course_id;
}, $connected);
$already_covered = $already_covered + array_map(function ($c) {
return $c->course_id;
}, $proposals);
$studygroup_ids = [];
foreach (SemClass::getClasses() as $sem_class) {
if ($sem_class['studygroup_mode'] > 0) {
foreach ($sem_class->getSemTypes() as $sem_type) {
$studygroup_ids[] = $sem_type['id'];
}
}
}
if (Request::get('search') && Request::get('search') != 1) {
$query = SQLQuery::table('seminare')
->where('search', '`name` LIKE :search', ['search' => '%' . Request::get('search') . '%'])
->where(
'studygroups',
'`seminare`.`status` IN (:sem_type_ids)',
['sem_type_ids' => $studygroup_ids]
)
->groupBy('`seminare`.`Seminar_id`');
if (count($already_covered) > 0) {
$query->where(
'ignore',
'`seminare`.`Seminar_id` NOT IN (:ignore)',
['ignore' => $already_covered]
);
}
if (!empty(Request::get('semester_id'))) {
$query->join(
'semester_courses',
'semester_courses',
'`semester_courses`.`course_id` = `seminare`.`Seminar_id`',
'LEFT JOIN'
);
$query->where(
'semester_id',
'semester_courses.semester_id = :semester_id OR semester_courses.semester_id IS NULL',
['semester_id' => Request::get('semester_id')]
);
}
$this->searchresults = $query->fetchAll(Course::class);
} else {
$this->my_studygroups = [];
if (!$GLOBALS['perm']->have_perm('admin')) {
$this->my_studygroups = Course::findBySQL('INNER JOIN `seminar_user` USING (`Seminar_id`)
WHERE `seminar_user`.`user_id` = :user_id
AND `seminare`.`status` IN (:studygroup_sem_types)
AND `seminare`.`Seminar_id` NOT IN (:ignore)
ORDER BY `seminare`.`name` ASC ',
[
'user_id' => User::findCurrent()->id,
'studygroup_sem_types' => $studygroup_ids,
'ignore' => count($already_covered) ? $already_covered : ''
]);
foreach ($this->my_studygroups as $my_studygroup) {
$already_covered[] = $my_studygroup->id;
}
}
//get all studygroups with a lot of members in the current course:
$statement = DBManager::get()->prepare("
SELECT `seminare`.*
FROM `seminar_user`
INNER JOIN `seminare` ON (`seminare`.`Seminar_id` = `seminar_user`.`Seminar_id`)
LEFT JOIN `seminar_user` AS `su2` ON (`su2`.`user_id` = `seminar_user`.`user_id` AND `su2`.`Seminar_id` = :course_id)
LEFT JOIN `studygroup_courses` ON (`studygroup_courses`.`studygroup_id` = `seminare`.`Seminar_id` AND `studygroup_courses`.`course_id` = `su2`.`Seminar_id`)
WHERE `seminare`.`status` IN (:studygroup_sem_types)
AND `studygroup_courses`.`id` IS NULL
GROUP BY `seminare`.`Seminar_id`
HAVING COUNT(`seminar_user`.`user_id`) > 1
ORDER BY COUNT(`seminar_user`.`user_id`) DESC
LIMIT 20
");
$statement->execute([
'course_id' => Context::getId(),
'studygroup_sem_types' => $studygroup_ids
]);
$this->suggestions = $statement->fetchAll(PDO::FETCH_ASSOC);
$this->suggestions = array_map(function ($d) {
return Course::buildExisting($d);
}, $this->suggestions);
}
}
public function remove_action($course_id)
{
if (Request::isPost() && $course_id) {
CSRFProtection::verifySecurityToken();
$connection = StudygroupCourse::deleteBySQL('course_id = ? AND studygroup_id = ?', [
Context::getId(),
$course_id
]);
PageLayout::postSuccess(_('Verknüpfung zu der Studiengruppe wurde aufgehoben.'));
}
$this->redirect('course/connectedstudygroups/index');
}
public function decline_action(StudygroupCourseProposal $proposal)
{
if (Request::isPost()) {
CSRFProtection::verifySecurityToken();
if ($GLOBALS['perm']->have_studip_perm('tutor', $proposal['course_id']) || $GLOBALS['perm']->have_studip_perm('tutor', $proposal['studygroup_id'])) {
if ($proposal['proposed_from'] === 'studygroup') {
PageLayout::postSuccess(_('Vorschlag wurde abgewiesen.'));
$statement = DBManager::get()->prepare("
SELECT `username`, `user_id`
FROM `auth_user_md5`
INNER JOIN `seminar_user` USING (`user_id`)
WHERE `seminar_user`.`Seminar_id` = ? AND `seminar_user`.`status` IN ('tutor', 'dozent')
");
$statement->execute([$proposal['studygroup_id']]);
$messaging = new messaging();
foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $user_data) {
setTempLanguage($user_data['user_id']);
$messaging->insert_message(
sprintf(
_('Ihr Vorschlag, die Studiengruppe „%1$s“ mit der Veranstaltung „%2$s“ zu verknüpfen, wurde leider abgewiesen.'),
$proposal->studygroup->getFullname(),
Context::get()->getFullname()
),
$user_data['username'],
'____%system%____',
'',
'',
'',
'',
_('Verknüpfungsvorschlag abgewiesen'),
'',
'normal',
['Studiengruppe']
);
restoreLanguage();
}
} else {
PageLayout::postSuccess(_('Vorschlag wurde zurückgezogen.'));
}
$proposal->delete();
}
}
$this->redirect('course/connectedstudygroups/index');
}
protected function buildSidebar()
{
$actions = new ActionsWidget();
$actions->addLink(
_('Studiengruppe verknüpfen'),
$this->url_for('course/connectedstudygroups/connect'),
Icon::create('add'),
['data-dialog' => 1]
);
Sidebar::Get()->addWidget($actions);
}
}
......@@ -51,10 +51,10 @@ class Course_ContentmodulesController extends AuthenticatedController
$this->categories[] = $module['category'];
}
if (!$module['category']) {
if (!in_array(_('Sonstige'), $this->categories)) {
$this->categories[] = _('Sonstige');
if (!in_array(_('Sonstiges'), $this->categories)) {
$this->categories[] = _('Sonstiges');
}
$this->modules[$i]['category'] = _('Sonstige');
$this->modules[$i]['category'] = _('Sonstiges');
}
}
sort($this->categories);
......@@ -83,37 +83,37 @@ class Course_ContentmodulesController extends AuthenticatedController
Sidebar::Get()->addWidget($widget);
}
PageLayout::addHeadElement('script', [
'type' => 'text/javascript',
], sprintf(
'window.ContentModulesStoreData = %s;',
json_encode([
$this->render_vue_app(
Studip\VueApp::create('ContentModules')
->withProps([
'range-type' => get_class($this->sem),
])
->withVuexStore('ContentModulesStore', 'contentmodules', [
'setCategories' => $this->categories,
'setHighlighted' => $this->highlighted_modules,
'setModules' => array_values($this->modules),
'setUserId' => User::findCurrent()->id,
'setView' => $GLOBALS['user']->cfg->CONTENTMODULES_TILED_DISPLAY ? 'tiles' : 'table',
])
));
);
}
public function trigger_action()
{
$context = Context::get();
$required_perm = $context->getRangeType() === 'course' ? 'tutor' : 'admin';
if (!$GLOBALS['perm']->have_studip_perm($required_perm, $context->id)) {
if (!$context->isEditableByUser()) {
throw new AccessDeniedException();
}
if (Request::isPost()) {
if ($context->getRangeType() === 'course') {
if ($context instanceof Course) {
$sem_class = $context->getSemClass();
} else {
$sem_class = SemClass::getDefaultInstituteClass($context->type);
}
$moduleclass = Request::get('moduleclass');
$active = Request::bool('active', false);
$module = new $moduleclass;
$module = PluginEngine::getPlugin($moduleclass);
if ($module->isActivatableForContext($context)) {
PluginManager::getInstance()->setPluginActivated($module->getPluginId(), $context->getId(), $active);
}
......@@ -125,25 +125,27 @@ class Course_ContentmodulesController extends AuthenticatedController
$active_tool->store();
}
}
//$this->redirect("course/contentmodules/trigger", ['cid' => $context->getId()]);
$this->redirect("course/contentmodules/trigger", ['cid' => $context->getId(), 'plugin_id' => $module->getPluginId()]);
return;
}
$active_tool = ToolActivation::find([$context->id, Request::int('plugin_id')]);
$template = $GLOBALS['template_factory']->open('tabs.php');
$template->navigation = Navigation::getItem('/course');
Navigation::getItem('/course/admin')->setActive(true);
$this->render_json([
'tabs' => $template->render(),
'position' => $active_tool->position
'position' => isset($active_tool) ? $active_tool->position : null,
]);
}
public function reorder_action()
{
$context = Context::get();
$required_perm = $context->getRangeType() === 'course' ? 'tutor' : 'admin';
if (!$GLOBALS['perm']->have_studip_perm($required_perm, $context->id)) {
if (!$context->isEditableByUser()) {
throw new AccessDeniedException();
}
if (Request::isPost()) {
$position = 0;
foreach (Request::getArray('order') as $plugin_id) {
......@@ -154,6 +156,7 @@ class Course_ContentmodulesController extends AuthenticatedController
$this->redirect($this->reorderURL());
return;
}
Navigation::getItem('/course/admin')->setActive(true);
$template = $GLOBALS['template_factory']->open('tabs.php');
$template->navigation = Navigation::getItem('/course');
......@@ -167,14 +170,14 @@ class Course_ContentmodulesController extends AuthenticatedController
if (!Request::isPost()) {
throw new AccessDeniedException();
}
$context = Context::get();
$required_perm = $context->getRangeType() === 'course' ? 'tutor' : 'admin';
if (!$GLOBALS['perm']->have_studip_perm($required_perm, $context->id)) {
$context = Context::get();
if (!$context->isEditableByUser()) {
throw new AccessDeniedException();
}
$moduleclass = Request::get('moduleclass');
$module = new $moduleclass;
$module = PluginEngine::getPlugin($moduleclass);
$active_tool = ToolActivation::find([$context->id, $module->getPluginId()]);
$metadata = $active_tool->metadata->getArrayCopy();
......@@ -205,15 +208,18 @@ class Course_ContentmodulesController extends AuthenticatedController
public function rename_action($module_id)
{
$context = Context::get();
$required_perm = $context->getRangeType() === 'course' ? 'tutor' : 'admin';
if (!$GLOBALS['perm']->have_studip_perm($required_perm, $context->id)) {
if (!$context->isEditableByUser()) {
throw new AccessDeniedException();
}
$this->module = PluginManager::getInstance()->getPluginById($module_id);
$this->metadata = $this->module->getMetadata();
$this->original_name = $this->metadata['displayname'] ?? $this->module->getPluginName();
PageLayout::setTitle(_('Werkzeug umbenennen'));
$this->tool = ToolActivation::find([$context->id, $module_id]);
if (Request::isPost()) {
$metadata = $this->tool->metadata->getArrayCopy();
if (!trim(Request::get('displayname')) || Request::submitted('delete')) {
......@@ -253,20 +259,25 @@ class Course_ContentmodulesController extends AuthenticatedController
}
if (isset($this->metadata['screenshots'])) {
foreach ($this->metadata['screenshots']['pictures'] as $picture) {
$title = $picture['title'];
$title = $picture['title'] ?? '';
$source = "{$this->plugin->getPluginURL()}/{$this->metadata['screenshots']['path']}/{$picture['source']}";
$this->screenshots[] = compact('title', 'source');
}
}
PageLayout::setTitle(sprintf(_('Informationen über %s'), $this->metadata['displayname']));
$this->metadata['icon'] = $this->getIconFromMetadata($this->metadata, $this->plugin);
PageLayout::setTitle(sprintf(
_('Informationen über %s'),
$this->metadata['displayname'] ?? $this->plugin->getPluginName()
));
}
private function getModules(Range $context)
{
$list = [];
foreach (PluginEngine::getPlugins('StudipModule') as $plugin) {
foreach (PluginEngine::getPlugins(StudipModule::class) as $plugin) {
if (!$plugin->isActivatableForContext($context)) {
continue;
}
......@@ -289,6 +300,7 @@ class Course_ContentmodulesController extends AuthenticatedController
$visibility = $tool ? $tool->getVisibilityPermission() : 'nobody';
$metadata = $plugin->getMetadata();
$icon = $this->getIconFromMetadata($metadata, $plugin);
$list[$plugin_id] = [
'id' => $plugin_id,
'moduleclass' => get_class($plugin),
......@@ -297,25 +309,58 @@ class Course_ContentmodulesController extends AuthenticatedController
'displayname' => $displayname,
'visibility' => $visibility,
'active' => (bool) $tool,
'icon' => $icon ? $icon->asImagePath() : null,
'summary' => $metadata['summary'] ?? null,
'mandatory' => $this->sem_class->isModuleMandatory(get_class($plugin)),
'highlighted' => (bool) $plugin->isHighlighted(),
'highlight_text' => $plugin->getHighlightText(),
'category' => $metadata['category'] ?? null,
];
if (!empty($metadata['icon_clickable'])) {
$list[$plugin_id]['icon'] = $metadata['icon_clickable'] instanceof Icon
? $metadata['icon_clickable']->asImagePath()
: Icon::create($plugin->getPluginURL().'/'.$metadata['icon_clickable'])->asImagePath();
} elseif (!empty($metadata['icon'])) {
$list[$plugin_id]['icon'] = $metadata['icon'] instanceof Icon
? $metadata['icon']->asImagePath()
: Icon::create($plugin->getPluginURL().'/'.$metadata['icon'])->asImagePath();
} else {
$list[$plugin_id]['icon'] = null;
}
$list[$plugin_id]['summary'] = $metadata['summary'] ?? null;
$list[$plugin_id]['mandatory'] = $this->sem_class->isModuleMandatory(get_class($plugin));
$list[$plugin_id]['highlighted'] = (bool) $plugin->isHighlighted();
$list[$plugin_id]['highlight_text'] = $plugin->getHighlightText();
$list[$plugin_id]['category'] = $metadata['category'] ?? null;
}
return $list;
}
/**
* @param array $metadata
* @param CorePlugin|StudIPPlugin $plugin
*/
private function getIconFromMetadata(array $metadata, $plugin): ?Icon
{
$icon = $metadata['icon_clickable'] ?? $metadata['icon'] ?? null;
if (!$icon) {
return null;
}
if ($plugin instanceof StudIPPlugin && str_starts_with($icon, '..')) {
$path = $GLOBALS['ABSOLUTE_PATH_STUDIP'] . '/' . $plugin->getPluginPath() . '/' . $icon;
$icon = $this->getCoreIcon($path) ?? $icon;
}
if (!$icon instanceof Icon) {
$icon = Icon::create($plugin->getPluginURL() . '/' . $icon);
}
return $icon->copyWithRole(Icon::ROLE_CLICKABLE);
}
private function getCoreIcon(string $path): ?Icon
{
$path = realpath($path);
if (!file_exists($path)) {
return null;
}
try {
$icon = basename($path, '.svg');
$color = basename(dirname($path));
$roles = Icon::colorToRoles($color);
return Icon::create($icon, $roles[0]);
} catch (Exception $e) {
return null;
}
}
}