diff --git a/app/controllers/admin/semester.php b/app/controllers/admin/semester.php index 1fbfc7b80d20e0d655a81f6807b4de5edf48ff01..2130390a132c8d0985d81d6d4ce7bec501c197e0 100644 --- a/app/controllers/admin/semester.php +++ b/app/controllers/admin/semester.php @@ -411,18 +411,25 @@ class Admin_SemesterController extends AuthenticatedController } // Hide courses and set lock rule - $query = "UPDATE `seminare` - SET `visible` = 0, `lock_rule` = ? - WHERE `Seminar_id` IN (?)"; - DBManager::get()->execute($query, [$lock_rule, $course_ids]); + Course::findEachMany( + function (Course $course) use ($lock_rule) { + $course->visible = 0; + $course->lock_rule = $lock_rule; + $course->store(); + }, + [$course_ids] + ); // Degrade users if ($degrade_users) { - $query = "UPDATE `seminar_user` - SET `status` = 'user' - WHERE `Seminar_id` IN (?) - AND `status` = 'autor'"; - DBManager::get()->execute($query, [$course_ids]); + CourseMember::findEachBySQL( + function (CourseMember $cm) { + $cm->status = 'user'; + $cm->store(); + }, + "`Seminar_id` IN (?) and `status` = 'autor'", + [$course_ids] + ); } // Lock enrolment diff --git a/app/controllers/admission/courseset.php b/app/controllers/admission/courseset.php index 11b0672826c46a85c91f4c4edbdca73ed026c0d5..7d73de4552267bb5a579b1315f0e7990b93a54a7 100644 --- a/app/controllers/admission/courseset.php +++ b/app/controllers/admission/courseset.php @@ -14,8 +14,6 @@ * @since 3.0 */ -require_once 'lib/admission.inc.php'; - class Admission_CoursesetController extends AuthenticatedController { /** @@ -539,7 +537,7 @@ class Admission_CoursesetController extends AuthenticatedController $ok += $course->store(); if ($do_update_admission) { - update_admission($course->id); + AdmissionApplication::addMembers($course->id); } } } diff --git a/app/controllers/course/admission.php b/app/controllers/course/admission.php index a4d838306554a985121ed98a0c2fb9dabbe1382a..a0d38170e41554f965261a13951b7e8869e6aa85 100644 --- a/app/controllers/course/admission.php +++ b/app/controllers/course/admission.php @@ -46,7 +46,7 @@ class Course_AdmissionController extends AuthenticatedController if (!SeminarCategories::GetByTypeId($this->course->status)->write_access_nobody) { $this->is_locked['write_level'] = 'disabled readonly'; } - update_admission($this->course->id); + AdmissionApplication::addMembers($this->course->id); PageLayout::addScript('studip-admission.js'); URLHelper::addLinkParam('return_to_dialog', Request::get('return_to_dialog')); } diff --git a/app/controllers/course/basicdata.php b/app/controllers/course/basicdata.php index 708e8db49f6216a39b37e33c96b5b0e6f5233a39..a2524b4a375d81a3a65033c837ca1e322d21fd71 100644 --- a/app/controllers/course/basicdata.php +++ b/app/controllers/course/basicdata.php @@ -477,7 +477,7 @@ class Course_BasicdataController extends AuthenticatedController //update admission, if turnout was raised if($after['admission_turnout'] > $before['admission_turnout'] && $sem->isAdmissionEnabled()) { - update_admission($sem->getId()); + AdmissionApplication::addMembers($sem->getId()); } if (sizeof($before) && sizeof($after)) { @@ -496,9 +496,21 @@ class Course_BasicdataController extends AuthenticatedController } //Labels/Funktionen für Dozenten und Tutoren - if ($perm->have_studip_perm("dozent", $sem->getId())) { - foreach (Request::getArray("label") as $user_id => $label) { - $sem->setLabel($user_id, $label); + if ($perm->have_studip_perm('dozent', $sem->getId())) { + 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 ($mb) { + $mb->label = $label; + if ($mb->store()) { + NotificationCenter::postNotification( + 'CourseDidChangeMemberLabel', + $sem, + $mb + ); + } + } + } } } @@ -791,7 +803,7 @@ class Course_BasicdataController extends AuthenticatedController $members[$key] = $temp_member; } } - $sem->setMemberPriority($members, $status); + $sem->setMemberPriority($members); } else { $this->msg[] = ["error", _("Sie haben keine Berechtigung diese Veranstaltung zu verändern.")]; } @@ -828,7 +840,7 @@ class Course_BasicdataController extends AuthenticatedController $members[$key] = $temp_member; } } - $sem->setMemberPriority($members, $status); + $sem->setMemberPriority($members); } else { $this->msg[] = ["error", _("Sie haben keine Berechtigung diese Veranstaltung zu verändern.")]; } diff --git a/app/controllers/course/members.php b/app/controllers/course/members.php index ad7d6dacf93257376ba95df191d15be48945cdb0..40ebef1c4ccbeb8b972d627d00db778ea82ad19b 100644 --- a/app/controllers/course/members.php +++ b/app/controllers/course/members.php @@ -16,8 +16,6 @@ */ require_once 'lib/messaging.inc.php'; //Funktionen des Nachrichtensystems - -require_once 'lib/admission.inc.php'; //Funktionen der Teilnehmerbegrenzung require_once 'lib/export/export_studipdata_func.inc.php'; // Funktionne für den Export require_once 'lib/export/export_linking_func.inc.php'; @@ -88,11 +86,8 @@ class Course_MembersController extends AuthenticatedController ]; //check for admission / waiting list - update_admission($this->course_id); - - // Create new MembersModel, to get additionanl informations to a given Seminar - $this->members = new MembersModel($this->course_id, $this->course_title); - $this->members->checkUserVisibility(); + AdmissionApplication::addMembers($this->course->id); + $this->checkUserVisibility(); } public function index_action() @@ -104,17 +99,28 @@ class Course_MembersController extends AuthenticatedController $sem = Seminar::getInstance($this->course_id); $this->sort_by = Request::option('sortby', 'nachname'); $this->order = Request::option('order', 'desc'); - $this->sort_status = Request::get('sort_status'); + $this->sort_status = Request::get('sort_status', ''); Navigation::activateItem('/course/members/view'); if (Request::int('toggle')) { $this->order = $this->order == 'desc' ? 'asc' : 'desc'; } - $filtered_members = $this->members->getMembers($this->sort_status, $this->sort_by . ' ' . $this->order, !$this->is_tutor ? $this->user_id : null); + $filtered_members = CourseMember::getMembers( + $this->course_id, + $this->sort_status, + $this->sort_by . ' ' . $this->order + ); if ($this->is_tutor) { - $filtered_members = array_merge($filtered_members, $this->members->getAdmissionMembers($this->sort_status, $this->sort_by . ' ' . $this->order )); + $filtered_members = array_merge( + $filtered_members, + AdmissionApplication::getAdmissionMembers( + $this->course_id, + $this->sort_status, + $this->sort_by . ' ' . $this->order + ) + ); $this->awaiting = $filtered_members['awaiting']->toArray('user_id username vorname nachname visible mkdate'); $this->accepted = $filtered_members['accepted']->toArray('user_id username vorname nachname visible mkdate'); $this->claiming = $filtered_members['claiming']->toArray('user_id username vorname nachname visible mkdate'); @@ -269,7 +275,7 @@ class Course_MembersController extends AuthenticatedController } else { PageLayout::postError(_('Bemerkung konnte nicht erfolgreich gespeichert werden.')); } - $this->redirect('course/members/index'); + $this->redirect($this->indexURL()); } /** @@ -287,19 +293,20 @@ class Course_MembersController extends AuthenticatedController $mp = MultiPersonSearch::load("add_autor" . $this->course_id); $countAdded = 0; + $msg = []; foreach ($mp->getAddedUsers() as $a) { - if($this->members->addMember($a, 'autor', Request::get('consider_contingent'))) { + if ($this->addMember($a, true, Request::bool('consider_contingent'), $msg)) { $countAdded++; } } if ($countAdded == 1) { - $text = _("Es wurde eine neue Person hinzugefügt."); + $text = _('Es wurde eine neue Person hinzugefügt.'); } else { - $text = sprintf(_("Es wurden %s neue Personen hinzugefügt."), $countAdded); + $text = sprintf(_('Es wurden %s neue Personen hinzugefügt.'), $countAdded); } - PageLayout::postSuccess($text); - $this->redirect('course/members/index'); + PageLayout::postSuccess($text, $msg); + $this->redirect($this->indexURL()); } /** @@ -350,7 +357,7 @@ class Course_MembersController extends AuthenticatedController $countAdded = 0; $countFailed = 0; foreach ($mp->getAddedUsers() as $a) { - if ($this->members->addToWaitlist($a)) { + if ($this->addToWaitlist($a)) { $countAdded++; } else { $countFailed++; @@ -503,22 +510,24 @@ class Course_MembersController extends AuthenticatedController public function send_to_course_action() { if ($target = $this->flash['target_course']) { - $msg = $this->members->sendToCourse( - $this->flash['users_to_send'], + $msg = $this->sendToCourse( + (array)$this->flash['users_to_send'], $target, $this->flash['move'] ); if ($msg['success']) { - if (sizeof($msg['success']) == 1) { + if (count($msg['success']) === 1) { $text = _('Eine Person wurde in die Zielveranstaltung eingetragen.'); } else { - $text = sprintf(_('%s Person(en) wurde(n) in die Zielveranstaltung eingetragen.'), - sizeof($msg['success'])); + $text = sprintf( + _('%s Person(en) wurde(n) in die Zielveranstaltung eingetragen.'), + count($msg['success']) + ); } PageLayout::postSuccess($text); } if ($msg['existing']) { - if (sizeof($msg['existing']) == 1) { + if (count($msg['existing']) === 1) { $text = _('Eine Person ist bereits in die Zielveranstaltung eingetragen ' . 'und kann daher nicht verschoben/kopiert werden.'); } else { @@ -529,7 +538,7 @@ class Course_MembersController extends AuthenticatedController PageLayout::postInfo($text); } if ($msg['failed']) { - if (sizeof($msg['failed']) == 1) { + if (count($msg['failed']) === 1) { $text = _('Eine Person kann nicht in die Zielveranstaltung eingetragen werden.'); } else { $text = sprintf(_('%s Person(en) konnten nicht in die Zielveranstaltung eingetragen werden.'), @@ -540,7 +549,7 @@ class Course_MembersController extends AuthenticatedController } else { PageLayout::postError(_('Bitte wählen Sie eine Zielveranstaltung.')); } - $this->redirect('course/members/index'); + $this->redirect($this->indexURL()); } /** @@ -578,6 +587,7 @@ class Course_MembersController extends AuthenticatedController Navigation::activateItem('/course/members/view'); } $datafields = DataField::getDataFields('user', 1 | 2 | 4 | 8, true); + $accessible_df = []; foreach ($datafields as $df) { if ($df->accessAllowed() && in_array($df->getId(), $GLOBALS['TEILNEHMER_IMPORT_DATAFIELDS'])) { $accessible_df[] = $df; @@ -617,12 +627,13 @@ class Course_MembersController extends AuthenticatedController } } } - + $csv_count_contingent_full = 0; + $csv_count_present = 0; + $csv_not_found = []; + $consider_contingent = false; if (Request::get('csv_import')) { // remove duplicate users from csv-import $csv_lines = array_unique($csv_request); - $csv_count_contingent_full = 0; - foreach ($csv_lines as $csv_line) { $csv_name = preg_split('/[,\t]/', mb_substr($csv_line, 0, 100), -1, PREG_SPLIT_NO_EMPTY); $csv_nachname = trim($csv_name[0]); @@ -632,25 +643,22 @@ class Course_MembersController extends AuthenticatedController continue; } - if (Request::option('csv_import_format') == 'realname') { - $csv_users = $this->members->getMemberByIdentification($csv_nachname, $csv_vorname); - } elseif (Request::option('csv_import_format') == 'username') { - $csv_users = $this->members->getMemberByUsername($csv_nachname); - } elseif (Request::option('csv_import_format') == 'email') { - $csv_users = $this->members->getMemberByEmail($csv_nachname); + if (Request::option('csv_import_format') === 'realname') { + $csv_users = CourseMember::getMemberByIdentification($this->course_id, $csv_nachname, $csv_vorname); + } elseif (Request::option('csv_import_format') === 'username') { + $csv_users = CourseMember::getMemberByUsername($this->course_id, $csv_nachname); + } elseif (Request::option('csv_import_format') === 'email') { + $csv_users = CourseMember::getMemberByEmail($this->course_id, $csv_nachname); } else { - $csv_users = $this->members->getMemberByDatafield($csv_nachname, $datafield_id); + $csv_users = CourseMember::getMemberByDatafield($this->course_id, $csv_nachname, $datafield_id); } // if found more then one result to given name if (count($csv_users) > 1) { - // if user have two accounts foreach ($csv_users as $row) { - if ($row['is_present']) { $csv_count_double++; - } else { $csv_mult_founds[$csv_line][] = $row; } @@ -664,7 +672,7 @@ class Course_MembersController extends AuthenticatedController if (!$row['is_present']) { $consider_contingent = Request::option('consider_contingent_csv'); - if (insert_seminar_user($this->course_id, $row['user_id'], 'autor', isset($consider_contingent), $consider_contingent)) { + if (CourseMember::insertCourseMember($this->course_id, $row['user_id'], 'autor', isset($consider_contingent), $consider_contingent)) { $csv_count_insert++; setTempLanguage($this->user_id); @@ -689,10 +697,13 @@ class Course_MembersController extends AuthenticatedController if (!empty($selected_users) && count($selected_users) > 0) { foreach ($selected_users as $selected_user) { if ($selected_user) { - if (insert_seminar_user($this->course_id, get_userid($selected_user), 'autor', isset($consider_contingent), $consider_contingent)) { + if (CourseMember::insertCourseMember($this->course_id, get_userid($selected_user), 'autor', isset($consider_contingent), $consider_contingent)) { $csv_count_insert++; setTempLanguage($this->user_id); - $message = sprintf(_('Sie wurden manuell in die Veranstaltung **%s** eingetragen.'), $this->course_title); + $message = sprintf(_('Sie wurden manu + + ell in die Veranstaltung **%s** eingetragen.'), $this->course_title); + restoreLanguage(); $messaging->insert_message($message, $selected_user, '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), _('Eintragung in Veranstaltung')), TRUE); } elseif (isset($consider_contingent)) { @@ -742,9 +753,8 @@ class Course_MembersController extends AuthenticatedController */ public function csv_manual_assignment_action() { - global $perm; // Security. If user not autor, then redirect to index - if (!$perm->have_studip_perm('tutor', $this->course_id)) { + if (!$GLOBALS['perm']->have_studip_perm('tutor', $this->course_id)) { throw new AccessDeniedException(); } @@ -755,27 +765,35 @@ class Course_MembersController extends AuthenticatedController /** * Change the visibilty of an autor + * @return void */ public function change_visibility_action($cmd, $mode) { - global $perm; // Security. If user not autor, then redirect to index - if ($perm->have_studip_perm('tutor', $this->course_id)) { + if ($GLOBALS['perm']->have_studip_perm('tutor', $this->course_id)) { throw new AccessDeniedException(); } // Check for visibile mode - if ($cmd == 'make_visible') { + if ($cmd === 'make_visible') { $command = 'yes'; } else { $command = 'no'; } - if ($mode == 'awaiting') { - $result = $this->members->setAdmissionVisibility($this->user_id, $command); + if ($mode === 'awaiting') { + $model = AdmissionApplication::findOneBySQL( + 'user_id = ? AND seminar_id = ?', + [$this->user_id, $this->course_id] + ); } else { - $result = $this->members->setVisibilty($this->user_id, $command); + $model = CourseMember::findOneBySQL( + 'user_id = ? AND Seminar_id = ?', + [$this->user_id, $this->course_id] + ); } + $model->visible = $command; + $result = $model->store(); if ($result > 0) { PageLayout::postSuccess(_('Ihre Sichtbarkeit wurde erfolgreich geändert.')); @@ -800,9 +818,6 @@ class Course_MembersController extends AuthenticatedController // select the additional method switch (Request::get('action_tutor')) { - case '': - $target = 'course/members/index'; - break; case 'downgrade': $target = 'course/members/downgrade_user/tutor/autor'; break; @@ -834,9 +849,6 @@ class Course_MembersController extends AuthenticatedController $this->flash['users'] = Request::getArray('autor'); switch (Request::get('action_autor')) { - case '': - $target = 'course/members/index'; - break; case 'upgrade': $target = 'course/members/upgrade_user/autor/tutor'; break; @@ -883,9 +895,6 @@ class Course_MembersController extends AuthenticatedController // select the additional method switch (Request::get('action_user')) { - case '': - $target = 'course/members/index'; - break; case 'upgrade': $target = 'course/members/upgrade_user/user/autor'; break; @@ -929,9 +938,6 @@ class Course_MembersController extends AuthenticatedController $waiting_type = Request::option('waiting_type'); // select the additional method switch (Request::get('action_awaiting')) { - case '': - $target = 'course/members/index'; - break; case 'upgrade_autor': $target = 'course/members/insert_admission/awaiting/collection'; break; @@ -969,9 +975,6 @@ class Course_MembersController extends AuthenticatedController // select the additional method switch (Request::get('action_accepted')) { - case '': - $target = 'course/members/index'; - break; case 'upgrade': $target = 'course/members/insert_admission/accepted/collection'; break; @@ -1015,12 +1018,17 @@ class Course_MembersController extends AuthenticatedController } if ($users) { - $msgs = $this->members->insertAdmissionMember($users, $target_status, Request::get('consider_contingent'), $status == 'accepted'); + $msgs = $this->insertAdmissionMember( + $users, + $target_status, + Request::bool('consider_contingent'), + $status === 'accepted' + ); if ($msgs) { - if ($cmd == 'add_user') { + if ($cmd === 'add_user') { $message = sprintf(_('%s wurde in die Veranstaltung mit dem Status <b>%s</b> eingetragen.'), htmlReady(join(',', $msgs)), $this->decoratedStatusGroups['autor']); } else { - if ($status == 'awaiting') { + if ($status === 'awaiting') { $message = sprintf(_('%s wurde aus der Anmelde bzw. Warteliste mit dem Status <b>%s</b> in die Veranstaltung eingetragen.'), htmlReady(join(', ', $msgs)), $this->decoratedStatusGroups[$target_status]); } else { @@ -1053,17 +1061,16 @@ class Course_MembersController extends AuthenticatedController if (!$this->is_tutor) { throw new AccessDeniedException(); } - + $course = Seminar::GetInstance($this->course_id); if (!Request::submitted('no')) { - if (Request::submitted('yes')) { CSRFProtection::verifyUnsafeRequest(); $users = Request::getArray('users'); if (!empty($users)) { if (in_array($status, words('accepted awaiting claiming'))) { - $msgs = $this->members->cancelAdmissionSubscription($users, $status); + $msgs = $course->cancelAdmissionSubscription($users, $status); } else { - $msgs = $this->members->cancelSubscription($users); + $msgs = $course->cancelSubscription($users); } // deleted authors @@ -1106,36 +1113,26 @@ class Course_MembersController extends AuthenticatedController htmlReady($this->status_groups[$status]) ) )->setAcceptURL( - $this->url_for("course/members/cancel_subscription/collection/{$status}"), + $this->cancel_subscriptionURL('collection', $status), compact('users') ); $this->flash['checked'] = $users; } } - - - - $this->redirect('course/members/index'); + $this->redirect($this->indexURL()); } /** * Upgrade a user to a selected status - * @param type $status - * @param type $next_status - * @param type $username - * @param type $cmd + * @param string $status + * @param string $next_status * @throws AccessDeniedException */ public function upgrade_user_action($status, $next_status) { - global $perm; - - // Security Check - if (!$this->is_tutor) { - throw new AccessDeniedException(); - } - - if ($this->is_tutor && $perm->have_studip_perm('tutor', $this->course_id) && $next_status != 'autor' && !$perm->have_studip_perm('dozent', $this->course_id)) { + if ($GLOBALS['perm']->have_studip_perm('tutor', $this->course_id) + && $next_status !== 'autor' + && !$GLOBALS['perm']->have_studip_perm('dozent', $this->course_id)) { throw new AccessDeniedException(); } @@ -1150,7 +1147,7 @@ class Course_MembersController extends AuthenticatedController if (!empty($users)) { // insert admission user to autorlist - $msgs = $this->members->setMemberStatus($users, $status, $next_status, 'upgrade'); + $msgs = $this->setMemberStatus($users, $status, $next_status, 'upgrade'); if ($msgs['success']) { PageLayout::postSuccess(sprintf( @@ -1168,7 +1165,10 @@ class Course_MembersController extends AuthenticatedController )); } } else { - PageLayout::postError(sprintf(_('Sie haben keine %s zum Hochstufen ausgewählt'), htmlReady($this->status_groups[$status]))); + PageLayout::postError(sprintf( + _('Sie haben keine %s zum Hochstufen ausgewählt'), + htmlReady($this->status_groups[$status]) + )); } $this->redirect('course/members/index'); @@ -1176,10 +1176,8 @@ class Course_MembersController extends AuthenticatedController /** * Downgrade a user to a selected status - * @param type $status - * @param type $next_status - * @param type $username - * @param type $cmd + * @param string $status + * @param string $next_status * @throws AccessDeniedException */ public function downgrade_user_action($status, $next_status) @@ -1188,8 +1186,8 @@ class Course_MembersController extends AuthenticatedController if (!$this->is_tutor) { throw new AccessDeniedException(); } - // TODO: Check this - if ($this->is_tutor && $next_status !== 'user' && !$this->is_dozent) { + + if ($next_status !== 'user' && !$this->is_dozent) { throw new AccessDeniedException(); } @@ -1202,7 +1200,7 @@ class Course_MembersController extends AuthenticatedController } if (!empty($users)) { - $msgs = $this->members->setMemberStatus($users, $status, $next_status, 'downgrade'); + $msgs = $this->setMemberStatus($users, $status, $next_status, 'downgrade'); if ($msgs['success']) { PageLayout::postSuccess(sprintf( @@ -1238,7 +1236,7 @@ class Course_MembersController extends AuthenticatedController } if (!empty($users)) { - $msg = $this->members->moveToWaitlist($users, $which_end); + $msg = $this->moveToWaitlist($users, $which_end); if (count($msg['success'])) { PageLayout::postSuccess(sprintf(_('%s Person(en) wurden auf die Warteliste verschoben.'), count($msg['success'])), @@ -1276,7 +1274,7 @@ class Course_MembersController extends AuthenticatedController $export_widget = new ExportWidget(); $export_widget->addLink( _('Zusatzangaben exportieren'), - $this->url_for('course/members/export_additional'), + $this->export_additionalURL(), Icon::create('file-excel') ); @@ -1296,7 +1294,7 @@ class Course_MembersController extends AuthenticatedController $course->aux->updateMember($member, Request::getArray($member->user_id)); } - $this->redirect('course/members/additional'); + $this->redirect($this->additionalURL()); } /** @@ -1368,8 +1366,6 @@ class Course_MembersController extends AuthenticatedController /** * Get the visibility of a user in a seminar - * @param String $user_id - * @param String $seminar_id * @return Array */ private function getUserVisibility() @@ -1381,9 +1377,9 @@ class Course_MembersController extends AuthenticatedController $result['visible_mode'] = false; if ($visibility) { - $result['iam_visible'] = ($visibility == 'yes' || $visibility == 'unknown'); + $result['iam_visible'] = ($visibility === 'yes' || $visibility === 'unknown'); - if ($status == 'user' || $status == 'autor') { + if ($status === 'user' || $status === 'autor') { $result['visible_mode'] = 'participant'; } else { $result['iam_visible'] = true; @@ -1402,10 +1398,8 @@ class Course_MembersController extends AuthenticatedController { $result = Seminar::GetInstance($this->course_id)->getNumber(); - $subject = ($result == '') ? sprintf('[%s]', $this->course_title) : + return ($result == '') ? sprintf('[%s]', $this->course_title) : sprintf(_('[%s: %s]'), $result, $this->course_title); - - return $subject; } private function createSidebar($filtered_members) @@ -1646,10 +1640,11 @@ class Course_MembersController extends AuthenticatedController } $widget->addLink( _('Teilnehmendenliste importieren'), - $this->url_for('course/members/import_autorlist'), + $this->import_autorlistURL(), Icon::create('persons'), ['data-dialog' => 1] ); + } if (Config::get()->EXPORT_ENABLE) { @@ -1756,7 +1751,7 @@ class Course_MembersController extends AuthenticatedController $actions = $sidebar->addWidget(new ActionsWidget()); $actions->addLink( $link_text, - $this->url_for('course/members/change_visibility', $modus, $this->my_visibility['visible_mode']), + $this->change_visibilityURL($modus, $this->my_visibility['visible_mode']), $icon, ['title' => $text] ); @@ -1768,13 +1763,18 @@ class Course_MembersController extends AuthenticatedController if (!$this->is_tutor) { throw new AccessDeniedException(); } - $filtered_members = $this->members->getMembers($this->sort_status, $this->sort_by . ' ' . $this->order); - $filtered_members = array_merge($filtered_members, $this->members->getAdmissionMembers($this->sort_status, $this->sort_by . ' ' . $this->order )); + $filtered_members = CourseMember::getMembers($this->sort_status, $this->sort_by . ' ' . $this->order); + $filtered_members = array_merge( + $filtered_members, + AdmissionApplication::getAdmissionMembers( + $this->course_id, + $this->sort_status, + $this->sort_by . ' ' . $this->order ) + ); $dozenten = $filtered_members['dozent']->toArray('user_id username vorname nachname visible mkdate'); $tutoren = $filtered_members['tutor']->toArray('user_id username vorname nachname visible mkdate'); $autoren = $filtered_members['autor']->toArray('user_id username vorname nachname visible mkdate'); - $header = [_('Titel'), _('Vorname'), _('Nachname'), _('Titel2'), _('Nutzernamen'), _('Privatadr'), _('Privatnr'), _('E-Mail'), _('Anmeldedatum'), _('Studiengänge')]; $data = [$header]; foreach ([$dozenten, $tutoren, $autoren] as $usergroup) { @@ -1800,7 +1800,7 @@ class Course_MembersController extends AuthenticatedController $this->config->store('COURSE_STUDENT_MAILING', $state); - $this->redirect('course/members'); + $this->redirect($this->indexURL()); } public function course_members_hide_action($state) @@ -1811,7 +1811,7 @@ class Course_MembersController extends AuthenticatedController $this->config->store('COURSE_MEMBERS_HIDE', $state); - $this->redirect('course/members'); + $this->redirect($this->indexURL()); } @@ -1933,7 +1933,6 @@ class Course_MembersController extends AuthenticatedController if ($who_param) { $url_params['who'] = implode(',', $who_param); } - //print_r($url_params);die(); $this->redirect(URLHelper::getURL( 'dispatch.php/messages/write', @@ -1942,4 +1941,318 @@ class Course_MembersController extends AuthenticatedController } } } + public function checkUserVisibility() + { + $membership = CourseMember::findOneBySQL("visible = 'unknown' AND Seminar_id = ?", [$this->course_id]); + if ($membership) { + CourseMember::findEachBySQL( + function(CourseMember $membership) { + $membership->visible = 'yes'; + $membership->store(); + }, + "status IN ('tutor', 'dozent') AND Seminar_id = ?", + [$this->course_id] + ); + + CourseMember::findEachBySQL( + function(CourseMember $membership) { + $user = $membership->user; + if (in_array($user->visible, ['no','never']) + || ($user->visible === 'unknown') && (int)!Config::get()->USER_VISIBILITY_UNKNOWN + ) { + $mode = 'no'; + } else { + $mode = 'yes'; + } + $membership->visible = $mode; + $membership->store(); + }, + "Seminar_id = ? AND visible='unknown'", + [$this->course_id] + ); + } + } + + private function setMemberStatus($members, $status, $next_status, $direction) + { + $msgs = []; + foreach ($members as $user_id) { + $temp_user = User::find($user_id); + if ($next_status == 'tutor' && !$GLOBALS['perm']->have_perm('tutor', $user_id)) { + $msgs['no_tutor'][$user_id] = $temp_user->getFullName(); + } else { + if ($temp_user) { + $next_pos = 0; + // get the next position of the user + switch ($next_status) { + case 'autor': + case 'user': + // get the current position of the user + $next_pos = $this->getPosition($user_id); + break; + // set the status to tutor + case 'tutor': + // get the next position of the user + $next_pos = CourseMember::getNextPosition($next_status, $this->course_id); + // resort the tutors + CourseMember::resortMembership($this->course_id, $this->getPosition($user_id)); + break; + } + + $membership = CourseMember::findOneBySQL( + 'Seminar_id = ? AND user_id = ? AND status = ?', + [$this->course_id, $user_id, $status] + ); + $membership->status = $next_status; + $membership->position = $next_pos; + + if ($membership->store()) { + StudipLog::log('SEM_CHANGED_RIGHTS', $this->course_id, $user_id, $next_status, + $this->getLogLevel($direction, $next_status)); + NotificationCenter::postNotification('CourseMemberStatusDidUpdate', $this->course_id, $user_id); + if ($next_status === 'autor') { + CourseMember::resortMembership($this->course_id, $next_pos); + } + $msgs['success'][$user_id] = $temp_user->getFullName(); + } + } + } + } + + if (!empty($msgs)) { + return $msgs; + } else { + return false; + } + } + + public function addMember(string $user_id, bool $accepted = false, bool $consider_contingent = null, &$msg = []): bool + { + $user = User::find($user_id); + $messaging = new messaging; + + $status = 'autor'; + $msg = []; + // insert + $copy_course = $accepted || $consider_contingent; + $admission_user = CourseMember::insertCourseMember($this->course_id, $user_id, $status, $copy_course, $consider_contingent, true); + + if ($admission_user) { + setTempLanguage($user_id); + $message = sprintf( + _('Sie wurden in die Veranstaltung **%s** eingetragen.'), + $this->course_title + ); + restoreLanguage(); + $messaging->insert_message( + $message, + $user->username, + '____%system%____', + false, + false, + '1', + false, + sprintf('%s %s', _('Systemnachricht:'), _('Eintragung in Veranstaltung')), + true + ); + $msg['success'] = sprintf( + _('%1$s wurde in die Veranstaltung mit dem Status<b>%2$s</b> eingetragen.'), + $user->getFullName(), + $status + ); + } else if ($consider_contingent) { + PageLayout::postError(_('Es stehen keine weiteren Plätze mehr im Teilnehmendenkontingent zur Verfügung.')); + return false; + } else { + PageLayout::postError( + _('Beim Eintragen ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut oder wenden Sie sich an die Administrierenden') + ); + return false; + } + //Warteliste neu sortieren + AdmissionApplication::renumberAdmission($this->course_id); + return true; + } + + /** + * Adds the given user to the waitlist of the current course and sends a + * corresponding message. + * + * @param String $user_id The user to add + * @return bool Successful operation? + */ + private function addToWaitlist(string $user_id): bool + { + $course = Seminar::getInstance($this->course_id); + // Insert user in waitlist at current position. + if ($course->addToWaitlist($user_id, 'last')) { + setTempLanguage($user_id); + $message = sprintf(_('Sie wurden von einem/einer Veranstaltungsleiter/-in (%1$s) ' . + 'oder einem/einer Administrator/-in auf die Warteliste der Veranstaltung **%2$s** gesetzt.'), + get_title_for_status('dozent', 1), $this->course_title); + restoreLanguage(); + messaging::sendSystemMessage($user_id, sprintf('%s %s', _('Systemnachricht:'), + _('Auf Warteliste gesetzt')), $message); + + return true; + } + return false; + } + + /** + * Adds the given users to the target course. + * @param array $users users to add + * @param string $target_course_id which course to add users to + * @param bool $move move users (=delete in source course) or just add to target course? + * @return array success and failure statuses + */ + private function sendToCourse(array $users, string $target_course_id, bool $move = false): array + { + $msg = []; + foreach ($users as $user) { + if (!CourseMember::exists([$target_course_id, $user])) { + $target_course = Seminar::GetInstance($target_course_id); + if ($target_course->addMember($user)) { + if ($move) { + $remove_from = Seminar::getInstance($this->course_id); + $remove_from->deleteMember($user); + } + $msg['success'][] = $user; + } else { + $msg['failed'][] = $user; + } + } else { + $msg['existing'][] = $user; + } + } + return $msg; + } + + private function insertAdmissionMember(array $users, string $next_status, bool $consider_contingent, bool $accepted = false, string $cmd = 'add_user'): array + { + $messaging = new messaging; + $status_title = get_title_for_status('dozent', 1); + foreach ($users as $user_id => $value) { + if ($value) { + $user = User::find($user_id); + if ($user) { + $admission_user = CourseMember::insertCourseMember( + $this->course_id, + $user_id, + $next_status, + $accepted || $consider_contingent, + $consider_contingent + ); + + // only if user was on the waiting list + if ($admission_user) { + setTempLanguage($user_id); + restoreLanguage(); + + if ($cmd === 'add_user') { + $message = sprintf( + _('Sie wurden in die Veranstaltung **%s** eingetragen.'), + $this->course_title + ); + } else { + if (!$accepted) { + $message = sprintf(_('Sie wurden aus der Warteliste in die Veranstaltung **%s** aufgenommen und sind damit zugelassen.'), +$this->course_title); + } else { + $message = sprintf(_('Sie wurden vom Status **vorläufig akzeptiert** auf **teilnehmend** in der Veranstaltung **%s** hochgestuft und sind damit zugelassen.'), $this->course_title); + } + } + + $messaging->insert_message( + $message, + $user->username, + '____%system%____', + false, + false, + '1', + false, + sprintf('%s %s', _('Systemnachricht:'), _('Eintragung in Veranstaltung')), + true + ); + $msgs[] = $user->getFullName(); + } + } + } + } + + // resort admissionlist + AdmissionApplication::renumberAdmission($this->course_id); + + return $msgs; + } + + /** + * Adds given users to the course waitlist, either at list beginning or end. + * System messages are sent to affected users. + * + * @param array $users array of user ids to add + * @param String $which_end 'last' or 'first': which list end to append to + * @return array Array of messages (stating success and/or errors) + */ + public function moveToWaitlist($users, $which_end) + { + $course = Seminar::getInstance($this->course_id); + $msgs = []; + foreach ($users as $user_id) { + // Delete member from seminar + $temp_user = User::find($user_id); + if ($course->deleteMember($user_id)) { + setTempLanguage($user_id); + $message = sprintf(_('Sie wurden aus der Veranstaltung **%s** abgemeldet. '. + 'Sie wurden auf die Warteliste dieser Veranstaltung gesetzt.'), + $this->course_title); + restoreLanguage(); + messaging::sendSystemMessage($user_id, sprintf('%s %s', _('Systemnachricht:'), + _('Anmeldung aufgehoben, auf Warteliste gesetzt')), $message); + if ($course->addToWaitlist($user_id, $which_end)) { + $msgs['success'][] = $temp_user->getFullname('no_title'); + } else { + $msgs['error'][] = $temp_user->getFullname('no_title'); + } + // Something went wrong on inserting the user in waitlist. + } else { + $msgs['error'][] = $temp_user->getFullname('no_title'); + } + } + return $msgs; + } + + /** + * Get the position out of the database + * @param String $user_id + * @return int + */ + private function getPosition($user_id): ?int + { + $membership = CourseMember::findByUser($user_id); + if ($membership) { + return (int)$membership->position; + } + return 0; + } + + private function getLogLevel($direction, $status) + { + if ($direction === 'upgrade') { + $directionString = 'hochgestuft'; + } else { + $directionString = 'runtergestuft'; + } + + switch ($status) { + case 'tutor': $log_level = 'zum Tutor'; + break; + case 'autor': $log_level = 'zum Autor'; + break; + case 'dozent': $log_level = 'zum Dozenten'; + break; + } + + return sprintf('%s %s', $directionString, $log_level); + } } diff --git a/app/controllers/course/statusgroups.php b/app/controllers/course/statusgroups.php index e0ed12e65a41b13749b38f6e321773f93586a48c..9d40b8f086b68f40b9d6c79c974d4368d253a291 100644 --- a/app/controllers/course/statusgroups.php +++ b/app/controllers/course/statusgroups.php @@ -1348,9 +1348,8 @@ class Course_StatusgroupsController extends AuthenticatedController CSRFProtection::verifyUnsafeRequest(); $members = Request::getArray('members'); - $model = new MembersModel($this->course_id, $this->course_title); - - $removed_names = $model->cancelSubscription($members); + $course = Seminar::GetInstance($this->course_id); + $removed_names = $course->cancelSubscription($members); PageLayout::postSuccess( _('Die folgenden Personen wurden aus der Veranstaltung ausgetragen'), diff --git a/app/controllers/course/studygroup.php b/app/controllers/course/studygroup.php index fc55bf560a1d484920d972afff169ad25deb54af..c1e7b4831c82b012349712ee8375804063bed931 100644 --- a/app/controllers/course/studygroup.php +++ b/app/controllers/course/studygroup.php @@ -320,10 +320,12 @@ class Course_StudygroupController extends AuthenticatedController if ($admin) { // insert founder(s) foreach ($founders as $user_id) { - $stmt = DBManager::get()->prepare("INSERT INTO seminar_user - (seminar_id, user_id, status, gruppe) - VALUES (?, ?, 'dozent', 8)"); - $stmt->execute([$sem->id, $user_id]); + CourseMember::create([ + 'seminar_id' => $sem->id, + 'user_id' => $user_id, + 'status' => 'dozent', + 'gruppe' => 8 + ]); } $this->founders = null; @@ -331,10 +333,12 @@ class Course_StudygroupController extends AuthenticatedController } else { $user_id = $GLOBALS['auth']->auth['uid']; // insert dozent - $query = "INSERT INTO seminar_user (seminar_id, user_id, status, gruppe) - VALUES (?, ?, 'dozent', 8)"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$sem->id, $user_id]); + CourseMember::create([ + 'seminar_id' => $sem->id, + 'user_id' => $user_id, + 'status' => 'dozent', + 'gruppe' => 8 + ]); } $sem->store(); diff --git a/app/controllers/my_courses.php b/app/controllers/my_courses.php index ee356164973d20dc8814c0952a34dfde7149ec70..8e17ead8c50020bfbb68edd3bca42071031307b2 100644 --- a/app/controllers/my_courses.php +++ b/app/controllers/my_courses.php @@ -301,25 +301,31 @@ class MyCoursesController extends AuthenticatedController ); $gruppe = Request::getArray('gruppe'); if (!empty($gruppe)) { - $query = "UPDATE seminar_user SET gruppe = ? WHERE Seminar_id = ? AND user_id = ?"; - $user_statement = DBManager::get()->prepare($query); - - $query = "UPDATE deputies SET gruppe = ? WHERE range_id = ? AND user_id = ?"; - $deputy_statement = DBManager::get()->prepare($query); - foreach ($gruppe as $key => $value) { - $user_statement->execute([$value, - $key, - $GLOBALS['user']->id - ]); - $updated = $user_statement->rowCount(); - - if ($deputies_enabled && !$updated) { - $deputy_statement->execute([ - $value, + $updated = CourseMember::findEachBySQL( + function (CourseMember $cm) use ($value) { + $cm->gruppe = $value; + $cm->store(); + }, + 'Seminar_id = ? AND user_id = ?', + [ $key, $GLOBALS['user']->id - ]); + ] + ); + + if ($deputies_enabled && !$updated) { + Deputy::findEachBySQL( + function (Deputy $deputy) use ($value) { + $deputy->gruppe = $value; + $deputy->store(); + }, + 'range_id = ? AND user_id = ?', + [ + $key, + $GLOBALS['user']->id + ] + ); } } } @@ -442,7 +448,7 @@ class MyCoursesController extends AuthenticatedController } $cmd = 'kill'; } else { - if (admission_seminar_user_get_position($GLOBALS['user']->id, $course_id) === false) { + if (AdmissionApplication::checkMemberPosition($GLOBALS['user']->id, $course_id) === false) { $message = sprintf( _('Wollen Sie sich von der Anmeldeliste der Veranstaltung "%s" wirklich abmelden?'), htmlReady($current_seminar->name) @@ -465,10 +471,7 @@ class MyCoursesController extends AuthenticatedController return; } else { if (!LockRules::Check($course_id, 'participants') && $ticket_check && Request::option('cmd') != 'back' && Request::get('cmd') != 'kill_admission') { - $query = "DELETE FROM seminar_user WHERE user_id = ? AND Seminar_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$GLOBALS['user']->id, $course_id]); - if ($statement->rowCount() == 0) { + if (CourseMember::deleteBySQL('user_id = ? AND Seminar_id = ?', [$GLOBALS['user']->id, $course_id]) === 0) { PageLayout::postError( _('In der ausgewählten Veranstaltung wurde die gesuchten Personen nicht gefunden und konnte daher nicht ausgetragen werden.') ); @@ -487,7 +490,7 @@ class MyCoursesController extends AuthenticatedController } // Are successor available - update_admission($course_id); + AdmissionApplication::addMembers($course_id); // If this course is a child of another course... if ($current_seminar->parent_course) { @@ -522,16 +525,16 @@ class MyCoursesController extends AuthenticatedController if ($current_seminar->isAdmissionEnabled()) { $prio_delete = AdmissionPriority::unsetPriority($current_seminar->getCourseSet()->getId(), $GLOBALS['user']->id, $course_id); } - $query = "DELETE FROM admission_seminar_user WHERE user_id = ? AND seminar_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$GLOBALS['user']->id, - $course_id]); NotificationCenter::postNotification('UserDidLeaveWaitingList', $course_id, $GLOBALS['user']->id); - if ($statement->rowCount() || $prio_delete) { + $deleted = AdmissionApplication::deleteBySQL( + 'user_id = ? AND seminar_id = ?', + [$GLOBALS['user']->id, $course_id] + ); + if ($deleted || $prio_delete) { //Warteliste neu sortieren - renumber_admission($course_id); + AdmissionApplication::renumberAdmission($course_id); //Pruefen, ob es Nachruecker gibt - update_admission($course_id); + AdmissionApplication::addMembers($course_id); PageLayout::postSuccess(sprintf( _("Der Eintrag in der Anmelde- bzw. Warteliste der Veranstaltung <b>%s</b> wurde aufgehoben. Wenn Sie an der Veranstaltung teilnehmen wollen, müssen Sie sich erneut bewerben."), htmlReady($current_seminar->name) diff --git a/lib/admission.inc.php b/lib/admission.inc.php index 817dbb50c21dbc4ff7c730c59159dbd9511b93ce..d0728f9d924f86fc9267386c8263d803a3366774 100644 --- a/lib/admission.inc.php +++ b/lib/admission.inc.php @@ -14,6 +14,8 @@ * @modulegroup admission * @module admission.inc.php * @package studip_core + * @deprecated since Stud.IP 5.3 + * */ // +---------------------------------------------------------------------------+ diff --git a/lib/classes/AutoInsert.class.php b/lib/classes/AutoInsert.class.php index d9b1558a87d6e7c80777536ff762422a33bc04ba..8aac873a7564cf0a6d7995980464e87e933e8c28 100644 --- a/lib/classes/AutoInsert.class.php +++ b/lib/classes/AutoInsert.class.php @@ -150,43 +150,29 @@ class AutoInsert private function addUser($user_id, $seminar) { - $query = "INSERT IGNORE INTO seminar_user (Seminar_id, user_id, status, gruppe, mkdate) - VALUES (?, ?, 'autor', ?, UNIX_TIMESTAMP())"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$seminar['Seminar_id'], $user_id, select_group($seminar['start_time'])]); - $rows = $statement->rowCount(); - if ($rows > 0) return true; - - return false; + $course_member = new CourseMember([$seminar['Seminar_id'], $user_id]); + $course_member->setData([ + 'Seminar_id' => $seminar['Seminar_id'], + 'user_id' => $user_id, + 'status' => 'autor', + 'gruppe' => select_group($seminar['start_time']), + ]); + + return $course_member->store() > 0; } private function removeUser($user_id, $seminar) { - $query = "DELETE FROM seminar_user " . "WHERE user_id = ? " . "AND Seminar_id = ? "; - - $statement = DBManager::get()->prepare($query); - $statement->execute([$user_id, $seminar['Seminar_id']]); - $rows = $statement->rowCount(); - - $query = "DELETE FROM statusgruppe_user " . "WHERE user_id = ? " . "AND statusgruppe_id IN (SELECT statusgruppe_id FROM statusgruppen WHERE range_id = ?)"; - $statusgruppe_stmt = DBManager::get()->prepare($query); - $statusgruppe_stmt->execute([$user_id, $seminar['Seminar_id']]); - $statusgruppe_rows = $statusgruppe_stmt->rowCount(); + $rows = CourseMember::deleteBySQL(' user_id = ? AND Seminar_id = ?', [$user_id, $seminar['Seminar_id']]); + $statusgruppe_rows = StatusgruppeUser::deleteBySQL( + 'user_id = ? AND statusgruppe_id IN (SELECT statusgruppe_id FROM statusgruppen WHERE range_id = ?)', + [$user_id, $seminar['Seminar_id']] + ); if ($rows > 0 || $statusgruppe_rows > 0) return true; return false; } - /** - * - * @param type $user_id - */ - public function deleteUserSeminare($user_id) - { - $db = DBManager::get(); - $db->exec("DELETE FROM seminar_user " . "WHERE user_id = " . $db->quote($user_id)); - } - /** * Tests if a seminar already has an autoinsert record * @param string $seminar_id Id of the seminar diff --git a/lib/classes/MembersModel.php b/lib/classes/MembersModel.php index 3c787084c19663db21a3f44dd1620a7887ec9918..bf776d99f0f31ea47bdc7f227c74f3e5644e3d25 100644 --- a/lib/classes/MembersModel.php +++ b/lib/classes/MembersModel.php @@ -1,4 +1,7 @@ <?php +/** + * @deprecated since Stud.IP 5.3 + */ class MembersModel { @@ -100,8 +103,8 @@ class MembersModel $message = sprintf(_('Ihre Anmeldung zur Veranstaltung **%1$s** wurde von Lehrenden (%2$s) oder Admin aufgehoben.'), $this->course_title, get_title_for_status('dozent', 1)); restoreLanguage(); $messaging->insert_message($message, $user->username, - '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), - _("Anmeldung aufgehoben")), TRUE); + '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), + _("Anmeldung aufgehoben")), TRUE); $msgs[] = $user->getFullName(); } } @@ -130,8 +133,8 @@ class MembersModel } restoreLanguage(); $messaging->insert_message($message, $user->username, - '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), - _("nicht zugelassen in Veranstaltung")), TRUE); + '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), + _("nicht zugelassen in Veranstaltung")), TRUE); StudipLog::log('SEM_USER_DEL', $this->course_id, $user_id, 'Wurde aus der Veranstaltung entfernt'); NotificationCenter::postNotification('UserDidLeaveCourse', $this->course_id, $user_id); @@ -149,7 +152,7 @@ class MembersModel $user = User::find($user_id); if ($user) { $admission_user = insert_seminar_user($this->course_id, $user_id, $next_status, - ($accepted || $consider_contingent ? TRUE : FALSE), $consider_contingent); + ($accepted || $consider_contingent ? TRUE : FALSE), $consider_contingent); // only if user was on the waiting list if ($admission_user) { @@ -163,7 +166,7 @@ class MembersModel if (!$accepted) { $message = sprintf(_('Sie wurden von %1$s oder Admin aus der Warteliste in die Veranstaltung **%2$s** aufgenommen und sind damit zugelassen.'), - get_title_for_status('dozent', 1), $this->course_title); + get_title_for_status('dozent', 1), $this->course_title); } else { $message = sprintf(_('Sie wurden von einem/einer %1$s oder Admin vom Status **vorläufig akzeptiert** auf **teilnehmend** in der Veranstaltung **%2$s** @@ -172,8 +175,8 @@ class MembersModel } $messaging->insert_message($message, $user->username, - '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), - _('Eintragung in Veranstaltung')), TRUE); + '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), + _('Eintragung in Veranstaltung')), TRUE); $msgs[] = $user->getFullName(); } } @@ -208,7 +211,7 @@ class MembersModel if (!$accepted) { $message = sprintf(_('Sie wurden vom einem/einer %1$s oder Admin aus der Warteliste in die Veranstaltung **%2$s** aufgenommen und sind damit zugelassen.'), - get_title_for_status('dozent', 1), $this->course_title); + get_title_for_status('dozent', 1), $this->course_title); } else { $message = sprintf(_('Sie wurden von einem/einer %1$s oder Admin vom Status **vorläufig akzeptiert** auf "**teilnehmend** in der Veranstaltung **%2$s** @@ -217,8 +220,8 @@ class MembersModel } restoreLanguage(); $messaging->insert_message($message, $user->username, - '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), - _('Eintragung in Veranstaltung')), TRUE); + '____%system%____', FALSE, FALSE, '1', FALSE, sprintf('%s %s', _('Systemnachricht:'), + _('Eintragung in Veranstaltung')), TRUE); } //Warteliste neu sortieren @@ -265,7 +268,7 @@ class MembersModel get_title_for_status('dozent', 1), $this->course_title); restoreLanguage(); messaging::sendSystemMessage($user_id, sprintf('%s %s', _('Systemnachricht:'), - _('Auf Warteliste gesetzt')), $message); + _('Auf Warteliste gesetzt')), $message); return true; } @@ -483,36 +486,36 @@ class MembersModel * @param String $which_end 'last' or 'first': which list end to append to * @return mixed Array of messages (stating success and/or errors) */ - public function moveToWaitlist($users, $which_end) - { - $course = Seminar::getInstance($this->course_id); - foreach ($users as $user_id) { - // Delete member from seminar - if ($course->deleteMember($user_id)) { - setTempLanguage($user_id); - $message = sprintf(_('Sie wurden von der Veranstaltung **%s** von '. - '%s oder der Administration abgemeldet, '. - 'Sie wurden auf die Warteliste dieser Veranstaltung gesetzt.'), - $this->course_title, get_title_for_status('dozent', 1)); - restoreLanguage(); - messaging::sendSystemMessage($user_id, sprintf('%s %s', _('Systemnachricht:'), - _('Anmeldung aufgehoben, auf Warteliste gesetzt')), $message); - // Insert user in waitlist at current position. - if ($course->addToWaitlist($user_id, $which_end)) { - $temp_user = User::find($user_id); - $msgs['success'][] = $temp_user->getFullname('no_title'); - $curpos++; - // Something went wrong on removing the user from course. - } else { - $msgs['error'][] = $temp_user->getFullname('no_title'); - } - // Something went wrong on inserting the user in waitlist. - } else { - $msgs['error'][] = $temp_user->getFullname('no_title'); - } - } - return $msgs; - } + public function moveToWaitlist($users, $which_end) + { + $course = Seminar::getInstance($this->course_id); + foreach ($users as $user_id) { + // Delete member from seminar + if ($course->deleteMember($user_id)) { + setTempLanguage($user_id); + $message = sprintf(_('Sie wurden von der Veranstaltung **%s** von '. + '%s oder der Administration abgemeldet, '. + 'Sie wurden auf die Warteliste dieser Veranstaltung gesetzt.'), + $this->course_title, get_title_for_status('dozent', 1)); + restoreLanguage(); + messaging::sendSystemMessage($user_id, sprintf('%s %s', _('Systemnachricht:'), + _('Anmeldung aufgehoben, auf Warteliste gesetzt')), $message); + // Insert user in waitlist at current position. + if ($course->addToWaitlist($user_id, $which_end)) { + $temp_user = User::find($user_id); + $msgs['success'][] = $temp_user->getFullname('no_title'); + $curpos++; + // Something went wrong on removing the user from course. + } else { + $msgs['error'][] = $temp_user->getFullname('no_title'); + } + // Something went wrong on inserting the user in waitlist. + } else { + $msgs['error'][] = $temp_user->getFullname('no_title'); + } + } + return $msgs; + } /** * Get the positon out of the database diff --git a/lib/classes/Seminar.class.php b/lib/classes/Seminar.class.php index 4190def3c18ff02233721d8e48f0f3bad5288ade..4b8e6e4e441a83f16aee593cd1fc1bfad90e75a3 100644 --- a/lib/classes/Seminar.class.php +++ b/lib/classes/Seminar.class.php @@ -20,7 +20,6 @@ * @category Stud.IP */ -require_once 'lib/admission.inc.php'; require_once 'lib/dates.inc.php'; class Seminar @@ -1530,17 +1529,13 @@ class Seminar // Delete that Seminar. // Alle Benutzer aus dem Seminar rauswerfen. - $query = "DELETE FROM seminar_user WHERE Seminar_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$s_id]); - if (($db_ar = $statement->rowCount()) > 0) { + $db_ar = CourseMember::deleteBySQL('Seminar_id = ?', [$s_id]); + if ($db_ar > 0) { $this->createMessage(sprintf(_("%s Teilnehmende und Lehrende archiviert."), $db_ar)); } // Alle Benutzer aus Wartelisten rauswerfen - $query = "DELETE FROM admission_seminar_user WHERE seminar_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$s_id]); + AdmissionApplication::deleteBySQL('seminar_id = ?', [$s_id]); // Alle beteiligten Institute rauswerfen $query = "DELETE FROM seminar_inst WHERE Seminar_id = ?"; @@ -1596,10 +1591,8 @@ class Seminar // Freie Seite zu diesem Seminar löschen - $query = "DELETE FROM scm WHERE range_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$s_id]); - if (($db_ar = $statement->rowCount()) > 0) { + $db_ar = StudipScmEntry::deleteBySQL('range_id = ?', [$s_id]); + if ($db_ar > 0) { $this->createMessage(_("Freie Seite der Veranstaltung archiviert.")); } @@ -1614,10 +1607,8 @@ class Seminar DataFieldEntry::removeAll($s_id); //kill all wiki-pages - $query = "DELETE FROM wiki WHERE range_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$s_id]); - if (($db_wiki = $statement->rowCount()) > 0) { + $db_wiki = WikiPage::deleteBySQL('range_id = ?', [$s_id]); + if ($db_wiki > 0) { $this->createMessage(sprintf(_("%s Wiki-Seiten archiviert."), $db_wiki)); } @@ -1892,51 +1883,34 @@ class Seminar return false; } } - - if (!$force) { - $query = "SELECT status FROM seminar_user WHERE user_id = ? AND Seminar_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$user_id, $this->id]); - $old_status = $statement->fetchColumn(); - } - - $query = "SELECT MAX(position) + 1 FROM seminar_user WHERE status = ? AND Seminar_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$status, $this->id]); - $new_position = $statement->fetchColumn(); - - $query = "SELECT COUNT(*) FROM seminar_user WHERE Seminar_id = ? AND status = 'dozent'"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->id]); - $numberOfTeachers = $statement->fetchColumn(); - - if (!$old_status) { - $query = "INSERT INTO seminar_user (Seminar_id, user_id, status, position, gruppe, visible, mkdate) - VALUES (?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP())"; - $statement = DBManager::get()->prepare($query); - $statement->execute([ - $this->id, - $user_id, - $status, - $new_position ?: 0, - (int)select_group($this->getSemesterStartTime()), - in_array($status, words('tutor dozent')) ? 'yes' : 'unknown', + $course_member = CourseMember::findOneBySQL('user_id = ? AND Seminar_id = ?', [$user_id, $this->id]); + $new_position = (int) DBManager::get()->fetchColumn( + "SELECT MAX(position) + 1 FROM seminar_user WHERE status = ? AND Seminar_id = ?", + [$status, $this->id] + ); + $numberOfTeachers = CourseMember::countBySql("Seminar_id = ? AND status = 'dozent'", [$this->id]); + + if (!$course_member && !$force) { + CourseMember::create([ + 'Seminar_id' => $this->id, + 'user_id' => $user_id, + 'status' => $status, + 'position' => $new_position?:0, + 'gruppe' => (int) select_group($this->getSemesterStartTime()), + 'visible' => in_array($status, ['tutor', 'dozent']) ? 'yes' : 'unknown', ]); // delete the entries, user is now in the seminar - $stmt = DBManager::get()->prepare('DELETE FROM admission_seminar_user - WHERE user_id = ? AND seminar_id = ?'); - $stmt->execute([$user_id, $this->getId()]); - if ($stmt->rowCount()) { + if (AdmissionApplication::deleteBySQL('user_id = ? AND seminar_id = ?', [$user_id, $this->getId()])) { //renumber the waiting/accepted/lot list, a user was deleted from it - renumber_admission($this->getId()); + AdmissionApplication::renumberAdmission($this->getId()); } $cs = $this->getCourseSet(); if ($cs) { - $prio_delete = AdmissionPriority::unsetPriority($cs->getId(), $user_id, $this->getId()); + AdmissionPriority::unsetPriority($cs->getId(), $user_id, $this->getId()); } CalendarScheduleModel::deleteSeminarEntries($user_id, $this->getId()); - NotificationCenter::postNotification("CourseDidGetMember", $this, $user_id); + NotificationCenter::postNotification('CourseDidGetMember', $this, $user_id); NotificationCenter::postNotification('UserDidEnterCourse', $this->id, $user_id); StudipLog::log('SEM_USER_ADD', $this->id, $user_id, $status, 'Wurde in die Veranstaltung eingetragen'); $this->course->resetRelation('members'); @@ -1949,67 +1923,158 @@ class Seminar } return $this; - } elseif (($force || $rangordnung[$old_status] < $rangordnung[$status]) - && ($old_status !== "dozent" || $numberOfTeachers > 1)) { - $query = "UPDATE seminar_user - SET status = ?, visible = IFNULL(?, visible), position = ? - WHERE Seminar_id = ? AND user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([ - $status, - in_array($status, words('tutor dozent')) ? 'yes' : null, - $new_position, - $this->id, - $user_id, - ]); - - if ($old_status === 'dozent') { - $query = "SELECT termin_id FROM termine WHERE range_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->id]); - $termine = $statement->fetchAll(PDO::FETCH_COLUMN); - - $query = "DELETE FROM termin_related_persons WHERE range_id = ? AND user_id = ?"; - $statement = DBManager::get()->prepare($query); + } elseif ( + ($force || $rangordnung[$course_member->status] < $rangordnung[$status]) + && ($course_member->status !== 'dozent' || $numberOfTeachers > 1) + ) { + $visibility = $course_member->visible; + if (in_array($status, ['tutor', 'dozent'])) { + $visibility = 'yes'; + } + $course_member->status = $status; + $course_member->visible = $visibility; + $course_member->position = $new_position; + $course_member->store(); + + if ($course_member->status === 'dozent') { + $termine = DBManager::get()->fetchFirst( + "SELECT termin_id FROM termine WHERE range_id = ?", + [$this->id] + ); - foreach ($termine as $termin_id) { - $statement->execute([$termin_id, $user_id]); - } + DBManager::get()->execute( + "DELETE FROM termin_related_persons WHERE range_id IN (?) AND user_id = ?", + [$termine, $user_id] + ); } - NotificationCenter::postNotification("CourseDidChangeMember", $this, $user_id); + NotificationCenter::postNotification('CourseDidChangeMember', $this, $user_id); $this->course->resetRelation('members'); $this->course->resetRelation('admission_applicants'); return $this; } else { - if ($old_status === "dozent" && $numberOfTeachers <= 1) { - $this->createError(sprintf(_("Die Veranstaltung muss wenigstens <b>einen/eine</b> VeranstaltungsleiterIn (%s) eingetragen haben!"), + if ($course_member->status === 'dozent' && $numberOfTeachers <= 1) { + $this->createError(sprintf(_('Die Person kann nicht herabgestuft werden, ' . +'da mindestens ein/eine Veranstaltungsleiter/-in (%s) in die Veranstaltung eingetragen sein muss!'), get_title_for_status('dozent', 1, $this->status)) . - ' ' . _("Tragen Sie zunächst einen anderen ein, um diesen herabzustufen.")); + ' ' . sprintf(_('Tragen Sie zunächst eine weitere Person als Veranstaltungsleiter/-in (%s) ein.'), +get_title_for_status('dozent', 1, $this->status))); } return false; } } + /** + * Cancels a subscription to an admission. + * + * @param array $users + * @param string $status + * @return array + * @throws NotificationVetoException + */ + public function cancelAdmissionSubscription(array $users, string $status): array + { + $msgs = []; + $messaging = new messaging; + $course_set = $this->getCourseSet(); + $users = User::findMany($users); + foreach ($users as $user) { + $prio_delete = false; + if ($course_set) { + $prio_delete = AdmissionPriority::unsetPriority($course_set->getId(), $user->id, $this->getId()); + } + $result = AdmissionApplication::deleteBySQL( + 'seminar_id = ? AND user_id = ? AND status = ?', + [$this->getId(), $user->id, $status] + ); + if ($result || $prio_delete) { + setTempLanguage($user->id); + if ($status !== 'accepted') { + $message = sprintf( + _('Sie wurden von der Warteliste der Veranstaltung **%s** gestrichen und sind damit __nicht__ zugelassen worden.'), + $this->getFullName() + ); + } else { + $message = sprintf( + _('Sie wurden aus der Veranstaltung **%s** gestrichen und sind damit __nicht__ zugelassen worden.'), + $this->getFullName() + ); + } + restoreLanguage(); + $messaging->insert_message( + $message, + $user->username, + '____%system%____', + false, + false, + '1', + false, + sprintf('%s %s', _('Systemnachricht:'), _('nicht zugelassen in Veranstaltung')), + true + ); + StudipLog::log('SEM_USER_DEL', $this->getId(), $user->id, 'Wurde aus der Veranstaltung entfernt'); + NotificationCenter::postNotification('UserDidLeaveCourse', $this->getId(), $user->id); + + $msgs[] = $user->getFullName(); + } + } + return $msgs; + } + + /** + * Cancels a subscription to a course + * @param array $users + * @return array + * @throws Exception + */ + public function cancelSubscription(array $users): array + { + $msgs = []; + $messaging = new messaging; + $users = User::findMany($users); + foreach ($users as $user) { + // delete member from seminar + if ($this->deleteMember($user->id)) { + setTempLanguage($user->id); + $message = sprintf( + _('Ihre Anmeldung zur Veranstaltung **%s** wurde aufgehoben.'), + $this->getFullName() + ); + restoreLanguage(); + $messaging->insert_message( + $message, + $user->username, + '____%system%____', + false, + false, + '1', + false, + sprintf('%s %s', _('Systemnachricht:'), _("Anmeldung aufgehoben")), + true + ); + $msgs[] = $user->getFullName(); + } + } + + return $msgs; + } + /** * deletes a user from the seminar by respecting the rule that at least one * user with status "dozent" must stay there - * @param user_id string: user_id of the user to delete - * @param return: false or $this for chaining + * @param string $user_id user_id of the user to delete + * @return boolean */ - public function deleteMember($user_id) + public function deleteMember($user_id): bool { - $dozenten = $this->getMembers('dozent'); + $dozenten = $this->getMembers(); if (count($dozenten) >= 2 || !$dozenten[$user_id]) { - $query = "DELETE FROM seminar_user WHERE Seminar_id = ? AND user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->id, $user_id]); - if ($statement->rowCount() === 0) { - return $this; + $result = CourseMember::deleteBySQL('Seminar_id = ? AND user_id = ?', [$this->id, $user_id]); + if ($result === 0) { + return true; } // If this course is a child of another course... if ($this->parent_course) { - // ... check if user is member in another sibling ... $other = CourseMember::countBySQL( "`user_id` = :user AND `Seminar_id` IN (:courses) AND `Seminar_id` != :this", @@ -2018,7 +2083,7 @@ class Seminar // ... and delete from parent course if this was the only // course membership in this family. - if ($other == 0) { + if ($other === 0) { $s = new Seminar($this->parent); $s->deleteMember($user_id); } @@ -2070,7 +2135,7 @@ class Seminar NotificationCenter::postNotification('UserDidLeaveCourse', $this->id, $user_id); StudipLog::log('SEM_USER_DEL', $this->id, $user_id, 'Wurde aus der Veranstaltung entfernt'); $this->course->resetRelation('members'); - return $this; + return true; } else { $this->createError( sprintf( @@ -2085,40 +2150,23 @@ class Seminar /** * sets the almost never used column position in the table seminar_user - * @param members array: array of user_id's - wrong IDs will be ignored - * @return $this + * @param array $members members array: array of user_id's - wrong IDs will be ignored + * @return Seminar */ - public function setMemberPriority($members) - { - $query = "UPDATE seminar_user - SET position = ? - WHERE Seminar_id = ? AND user_id = ?"; - $statement = DBManager::get()->prepare($query); - - foreach(array_values($members) as $num => $member) { - $statement->execute([$num, $this->id, $member]); - } - + public function setMemberPriority($members): Seminar + { + $counter = 0; + CourseMember::findEachBySQL( + function (CourseMember $membership) use (&$counter) { + $membership->position = $counter++; + $membership->store(); + }, + "Seminar_id = ? AND user_id = IN (?)", + [$this->course_id, $members] + ); return $this; } - public function setLabel($user_id, $label) { - if ($GLOBALS['perm']->have_studip_perm('tutor', $this->getId(), $user_id)) { - $statement = DBManager::get()->prepare( - "UPDATE seminar_user " . - "SET label = :label " . - "WHERE user_id = :user_id " . - "AND Seminar_id = :seminar_id " . - ""); - $statement->execute([ - 'user_id' => $user_id, - 'seminar_id' => $this->getId(), - 'label' => $label - ]); - NotificationCenter::postNotification("CourseDidChangeMemberLabel", $this); - } - } - /** * returns array with information about enrolment to this course for given user_id * ['enrolment_allowed'] : true or false @@ -2386,10 +2434,7 @@ class Seminar case 'first': default: // Move all others on the waitlist up by the number of people to add. - DBManager::get()->execute("UPDATE `admission_seminar_user` - SET `position`=`position`+1 - WHERE `seminar_id`=? - AND `status`='awaiting'", [$this->id]); + AdmissionApplication::renumberAdmission($this->id); $waitpos = 1; } $new_admission_member = new AdmissionApplication(); diff --git a/lib/classes/UserManagement.class.php b/lib/classes/UserManagement.class.php index 236318bbb3b8e6d3afec662f6c7ace1626483d24..e44a71395298b7e79ac7ebf71f6ee121383a155f 100644 --- a/lib/classes/UserManagement.class.php +++ b/lib/classes/UserManagement.class.php @@ -20,7 +20,6 @@ */ // Imports -require_once 'lib/admission.inc.php'; // remove user from waiting lists require_once 'lib/statusgruppe.inc.php'; // remove user from statusgroups require_once 'lib/messaging.inc.php'; // remove messages send or recieved by user require_once 'lib/object.inc.php'; @@ -126,25 +125,29 @@ class UserManagement $this->user->visible = 'yes'; } if ($nperms[$this->user->perms] < $nperms[$this->user->getPristineValue('perms')]) { - $query = "UPDATE seminar_user - INNER JOIN seminare ON (seminare.Seminar_id = seminar_user.Seminar_id) - SET seminar_user.status = :new_max_status - WHERE seminar_user.user_id = :user_id - AND seminar_user.status IN (:old_status) - AND seminare.status NOT IN (:studygroups)"; - $downgrade = DBManager::get()->prepare($query); $old_status = []; foreach ($nperms as $status => $n) { if ($n > $nperms[$this->user->perms] && $n <= $nperms[$this->user->getPristineValue('perms')]) { $old_status[] = $status; } } - $downgrade->execute([ - 'user_id' => $this->user->id, - 'old_status' => $old_status, - 'studygroups' => studygroup_sem_types(), - 'new_max_status' => $this->user->perms, - ]); + $new_status = $this->user->perms; + CourseMember::findEachBySQL( + function (CourseMember $cm) use ($new_status) { + $cm->status = $new_status; + $cm->store(); + }, + 'INNER JOIN seminare ON (seminare.Seminar_id = seminar_user.Seminar_id) + WHERE seminar_user.user_id = ? + AND seminar_user.status IN (?) + AND seminare.status NOT IN (?) + ', + [ + $this->user->id, + $old_status, + studygroup_sem_types() + ] + ); } } } @@ -551,36 +554,35 @@ class UserManagement $this->re_sort_position_in_seminar_user(); // delete all seminar entries - $query = "SELECT seminar_id FROM seminar_user WHERE user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->user_data['auth_user_md5.user_id']]); - $seminar_ids = $statement->fetchAll(PDO::FETCH_COLUMN); - - $query = "DELETE FROM seminar_user WHERE user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->user_data['auth_user_md5.user_id']]); - if ($count = $statement->rowCount()) { + $course_member = SimpleCollection::createFromArray( + CourseMember::findByUser($this->user_data['auth_user_md5.user_id']) + ); + $seminar_ids = $course_member->pluck('seminar_id'); + $count = 0; + foreach($course_member as $member) { + $member->delete(); + $count++; + } + if ($count) { $this->msg .= 'info§' . sprintf(_('%s Einträge aus Veranstaltungen gelöscht.'), $count) . '§'; - array_map('update_admission', $seminar_ids); + array_map('AdmissionApplication::addMembers', $seminar_ids); } // delete all entries from waiting lists - $query = "SELECT seminar_id FROM admission_seminar_user WHERE user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->user_data['auth_user_md5.user_id']]); - $seminar_ids = $statement->fetchAll(PDO::FETCH_COLUMN); - - $query = "DELETE FROM admission_seminar_user WHERE user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->user_data['auth_user_md5.user_id']]); - if ($count = $statement->rowCount()) { + $admission_members = SimpleCollection::createFromArray( + AdmissionApplication::findByUser($this->user_data['auth_user_md5.user_id']) + ); + $seminar_ids = $admission_members->pluck('seminar_id'); + $count = 0; + foreach ($admission_members as $admission_member) { + $admission_member->delete(); + $count++; + } + if ($count) { $this->msg .= 'info§' . sprintf(_('%s Einträge aus Wartelisten gelöscht.'), $count) . '§'; - array_map('update_admission', $seminar_ids); + array_map('AdmissionApplication::addMembers', $seminar_ids); } // delete 'Studiengaenge' - $query = "DELETE FROM user_studiengang WHERE user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->user_data['auth_user_md5.user_id']]); - if ($count = $statement->rowCount()) { + if ($count = UserStudyCourse::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']])) { $this->msg .= 'info§' . sprintf(_('%s Zuordnungen zu Studiengängen gelöscht.'), $count) . '§'; } // delete all private appointments of this user @@ -1113,7 +1115,7 @@ class UserManagement $statement->execute([$user_id]); if ($count = $statement->rowCount()) { $msg .= 'info§' . sprintf(_('%s Einträge aus Wartelisten gelöscht.'), $count) . '§'; - array_map('update_admission', $seminar_ids); + array_map('AdmissionApplication::addMembers', $seminar_ids); } // delete all personal news from this user @@ -1277,9 +1279,9 @@ class UserManagement $statement->execute([$this->user_data['auth_user_md5.user_id']]); while ($row = $statement->fetch(PDO::FETCH_ASSOC)) { if ($row['status'] === 'tutor') { - re_sort_tutoren($row['Seminar_id'], $row['position']); + CourseMember::resortMembership($row['Seminar_id'], (int)$row['position']); } else if ($row['status'] === 'dozent') { - re_sort_dozenten($row['Seminar_id'], $row['position']); + CourseMember::resortMembership($row['Seminar_id'], (int)$row['position'], 'dozent'); } } } @@ -1292,8 +1294,6 @@ class UserManagement */ public function changePassword($password) { - global $perm; - $this->user_data['auth_user_md5.password'] = self::getPwdHasher()->HashPassword($password); $this->storeToDatabase(); diff --git a/lib/functions.php b/lib/functions.php index b47bcec2c076d3d14c232a4f43d957b59965eb3e..bce4215064b93a69e7d4062e456d15a3252a99b1 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -522,6 +522,7 @@ function get_config($key) * * @param string $s_id the seminar to work on * @param int $position the position to start with + * @deprecated since Stud.IP 5.3 * * @return void */ @@ -540,6 +541,7 @@ function re_sort_dozenten($s_id, $position) * * @param string $s_id the seminar to work on * @param int $position the position to start with + * @deprecated since Stud.IP 5.3 * * @return void */ @@ -558,6 +560,7 @@ function re_sort_tutoren($s_id, $position) * * @param string $status can be on of 'tutor', 'dozent', ... * @param string $seminar_id the seminar to work on + * @deprecated since Stud.IP 5.3 * * @return int the next available position */ @@ -569,7 +572,7 @@ function get_next_position($status, $seminar_id) $statement = DBManager::get()->prepare($query); $statement->execute([$seminar_id, $status]); - return $statement->fetchColumn() ?: 0; + return $statement->fetchColumn() ?: 0; } /** diff --git a/lib/models/AdmissionApplication.class.php b/lib/models/AdmissionApplication.class.php index 21cd1f46e3327f48855f5e14cba2bc32a6f81c5e..4da0c6b83a8d714183f220b4d1afc013db0fff75 100644 --- a/lib/models/AdmissionApplication.class.php +++ b/lib/models/AdmissionApplication.class.php @@ -102,4 +102,178 @@ class AdmissionApplication extends SimpleORMap implements PrivacyObject } } } + + /** + * @param string $course_id + * @param string $sort_status + * @param string $order_by + * @return array + */ + public static function getAdmissionMembers(string $course_id, string $sort_status = 'autor', string $order_by = 'nachname asc'): array + { + [$order, $asc] = explode(' ', $order_by); + if ($order === 'nachname') { + $order_by = "nachname {$asc},vorname {$asc}"; + } + + $cs = CourseSet::getSetForCourse($course_id); + $claiming = []; + if (is_object($cs) && !$cs->hasAlgorithmRun()) { + foreach (AdmissionPriority::getPrioritiesByCourse($cs->getId(), $course_id) as $user_id => $p) { + $user = User::find($user_id); + $data = $user->toArray('user_id username vorname nachname email'); + $data['fullname'] = $user->getFullname('full_rev'); + $data['position'] = $cs->hasAdmissionRule('LimitedAdmission') ? $p : '-'; + $data['visible'] = 'unknown'; + $data['status'] = 'claiming'; + $claiming[] = $data; + } + } + + $query = "SELECT asu.user_id, username, Vorname, Nachname, Email, status, + position, asu.mkdate, asu.visible, asu.comment, + {$GLOBALS['_fullname_sql']['full_rev']} AS fullname + FROM admission_seminar_user AS asu + JOIN auth_user_md5 USING (user_id) + JOIN user_info USING (user_id) + WHERE seminar_id = ? + ORDER BY position, Nachname"; + $st = DBManager::get()->prepare($query); + $st->execute([$course_id]); + $application_members = SimpleCollection::createFromArray(array_merge($claiming, $st->fetchAll(PDO::FETCH_ASSOC))); + $filtered_members = []; + foreach (['awaiting', 'accepted', 'claiming'] as $status) { + $filtered_members[$status] = $application_members->findBy('status', $status); + if ($status === $sort_status) { + $filtered_members[$status]->orderBy($order_by, $order !== 'nachname' ? SORT_NUMERIC : SORT_LOCALE_STRING); + } + } + return $filtered_members; + } + + /** + * returns the position for a user on a waiting list + * + * if the user is not found false is returned, return true if the user is found but + * no position is available + * + * @param string $user_id user_id + * @param string $seminar_id seminar_id + * @return bool position in waiting list or false if not found + * + */ + public static function checkMemberPosition(string $user_id, string $seminar_id): bool + { + $position = DBManager::get()->fetchColumn("SELECT IFNULL(position, 'na') + FROM admission_seminar_user + WHERE user_id = ? AND seminar_id = ? AND status = 'awaiting'", + [$user_id, $seminar_id] + ); + + return $position === 'na'; + } + + /** + * @param string $seminar_id + * @param string $send_message + * @return void + * @throws NotificationVetoException + */ + public static function addMembers(string $seminar_id, bool $send_message = true): void + { + $messaging = new messaging; + + //Daten holen / Abfrage ob ueberhaupt begrenzt + $seminar = Seminar::GetInstance($seminar_id, true); + + if($seminar->isAdmissionEnabled()){ + $sem_preliminary = ($seminar->admission_prelim == 1); + $cs = $seminar->getCourseSet(); + //Veranstaltung einfach auffuellen (nach Lostermin und Ende der Kontingentierung) + if (!$seminar->admission_disable_waitlist_move && $cs->hasAlgorithmRun()) { + $count = (int)$seminar->getFreeAdmissionSeats(); + $memberships = self::findBySQL( + "seminar_id = ? AND status = 'awaiting' ORDER BY position LIMIT {$count}", + [$seminar_id] + ); + foreach ($memberships as $membership) { + //ok, here ist the "colored-group" meant (for grouping on meine_seminare), not the grouped seminars as above! + $group = select_group($seminar->getSemesterStartTime()); + if (!$sem_preliminary) { + $course_membership = new CourseMember([$seminar_id, $membership->id]); + $course_membership->setData([ + 'status' => 'autor', + 'gruppe' => $group, + ]); + $affected = $course_membership->store(); + + NotificationCenter::postNotification('UserDidEnterCourse', $seminar->getId(), $membership->user_id); + } else { + $membership->status = 'accepted'; + $affected = $membership->store(); + } + if ($affected > 0) { + $log_message = 'Wurde automatisch aus der Warteliste in die Veranstaltung eingetragen.'; + StudipLog::log('SEM_USER_ADD', $seminar->getId(), $membership->user_id, $sem_preliminary ? 'accepted' : 'autor', $log_message); + if (!$sem_preliminary) { + $affected = $membership->delete(); + } else { + $affected = 0; + } + //User benachrichtigen + if (($sem_preliminary || $affected > 0) && $send_message) { + setTempLanguage($membership->user_id); + if (!$sem_preliminary) { + $message = sprintf (_('Sie sind in die Veranstaltung **%s (%s)** eingetragen worden, da für Sie ein Platz frei geworden ist. Damit sind Sie für die Teilnahme an der Veranstaltung zugelassen. Ab sofort finden Sie die Veranstaltung in der Übersicht Ihrer Veranstaltungen.'), $seminar->getName(), $seminar->getFormattedTurnus(true)); + } else { + $message = sprintf (_('Sie haben den Status vorläufig akzeptiert in der Veranstaltung **%s (%s)** erhalten, da für Sie ein Platz frei geworden ist.'), $seminar->getName(), $seminar->getFormattedTurnus(true)); + } + $subject = sprintf(_("Teilnahme an der Veranstaltung %s"), $seminar->getName()); + restoreLanguage(); + + $messaging->insert_message($message, $membership->username, '____%system%____', false, false, '1', false, $subject, true); + } + } + } + //Warteposition der restlichen User neu eintragen + AdmissionApplication::renumberAdmission($seminar_id, FALSE); + } + $seminar->restore(); + } + } + + /** + * Renumber admissions + * @param string $seminar_id + * @param bool $send_message + * @return void + */ + public static function renumberAdmission (string $seminar_id, bool $send_message = true): void + { + $messaging = new messaging; + $seminar = Seminar::GetInstance($seminar_id); + if ($seminar->isAdmissionEnabled()) { + $admission_users = self::findBySQL( + "seminar_id = ? AND status = 'awaiting' ORDER BY position", + [$seminar->id] + ); + $position = 1; + foreach ($admission_users as $admission) { + $admission->position = $position; + if ($admission->store() && Config::get()->NOTIFY_ON_WAITLIST_ADVANCE && $send_message) { + $username = $admission->user->username; + setTempLanguage($admission->user_id); + $message = sprintf(_('Sie sind auf der Warteliste der Veranstaltung **%s (%s)** hochgestuft worden. Sie stehen zur Zeit auf Position %s.'), + $seminar->name, + $seminar->getFormattedTurnus(), + $position); + $subject = sprintf(_('Ihre Position auf der Warteliste der Veranstaltung %s wurde verändert'), $seminar->name); + restoreLanguage(); + + $messaging->insert_message($message, $username, '____%system%____', FALSE, FALSE, '1', FALSE, $subject); + } + $position += 1; + } + } + } } diff --git a/lib/models/CourseMember.class.php b/lib/models/CourseMember.class.php index 4546e09969bdfbf9f82d4e67e0c0c02c8a4c1b6b..f0ebaaec2574a96d3dfaf7d1ba3d597626832080 100644 --- a/lib/models/CourseMember.class.php +++ b/lib/models/CourseMember.class.php @@ -155,6 +155,145 @@ class CourseMember extends SimpleORMap implements PrivacyObject CourseMemberNotification::deleteBySQL('user_id = ?', [$this->user_id]); } + /** + * Get members of a course + * + * @param string $course_id + * @param string $sort_status + * @param string $order_by + * @return array + */ + public function getMembers(string $course_id, string $sort_status = 'autor', string $order_by = 'nachname asc'): array + { + [$order, $asc] = explode(' ', $order_by); + if ($order === 'nachname') { + $order_by = "Nachname {$asc},Vorname {$asc}"; + } + + $query = "SELECT su.user_id, username, Vorname, Nachname, Email, status, + position, su.mkdate, su.visible, su.comment, + {$GLOBALS['_fullname_sql']['full_rev']} AS fullname + FROM seminar_user AS su + INNER JOIN auth_user_md5 USING (user_id) + INNER JOIN user_info USING (user_id) + WHERE seminar_id = ? + ORDER BY position, Nachname ASC"; + $st = DBManager::get()->prepare($query); + $st->execute([$course_id]); + $members = SimpleCollection::createFromArray($st->fetchAll(PDO::FETCH_ASSOC)); + $filtered_members = []; + + foreach (['user', 'autor', 'tutor', 'dozent'] as $status) { + $filtered_members[$status] = $members->findBy('status', $status); + if ($status === $sort_status) { + $filtered_members[$status]->orderBy($order_by, $order !== 'nachname' ? SORT_NUMERIC : SORT_LOCALE_STRING); + } else { + $filtered_members[$status]->orderBy(in_array($status, ['tutor', 'dozent']) ? 'position,Nachname,Vorname' : 'Nachname,Vorname'); + } + } + return $filtered_members; + } + + /** + * Get user informations by first and last name for csv-import + * @param string $course_id + * @param string $nachname + * @param string $vorname + * @return array + */ + public function getMemberByIdentification(string $course_id, string $nachname, string $vorname = null): array + { + return DBManager::get()->fetchAll("SELECT + auth_user_md5.user_id, + auth_user_md5.username, + auth_user_md5.perms, + seminar_user.Seminar_id AS is_present, + {$GLOBALS['_fullname_sql']['full_rev']} AS fullname + FROM auth_user_md5 + LEFT JOIN user_info USING (user_id) + LEFT JOIN seminar_user ON (seminar_user.user_id = auth_user_md5.user_id AND seminar_user.Seminar_id = ?) + WHERE auth_user_md5.perms IN ('autor', 'tutor', 'dozent') + AND auth_user_md5.visible <> 'never' + AND auth_user_md5.Nachname LIKE ? AND (? IS NULL OR auth_user_md5.Vorname LIKE ?) + ORDER BY auth_user_md5.Nachname, auth_user_md5.Vorname", + [$course_id, $nachname, $vorname, $vorname]); + } + + /** + * Get user informations by username for csv-import + * @param string $course_id + * @param string $username + * @return Array + */ + public function getMemberByUsername(string $course_id, string $username): array + { + return DBManager::get()->fetchAll( + "SELECT auth_user_md5.user_id, + auth_user_md5.username, + auth_user_md5.perms, + seminar_user.Seminar_id AS is_present, + {$GLOBALS['_fullname_sql']['full_rev']} AS fullname + FROM auth_user_md5 + LEFT JOIN user_info USING (user_id) + LEFT JOIN seminar_user ON (seminar_user.user_id = auth_user_md5.user_id AND seminar_user.Seminar_id = ?) + WHERE auth_user_md5.perms IN ('autor', 'tutor', 'dozent') + AND auth_user_md5.visible <> 'never' + AND auth_user_md5.username LIKE ? + ORDER BY auth_user_md5.Nachname, auth_user_md5.Vorname", + [$course_id, $username] + ); + } + + /** + * Get user informations by email for csv-import + * @param String $course_id + * @param String $email + * @return Array + */ + public function getMemberByEmail($course_id, $email): array + { + return DBManager::get()->fetchAll( + "SELECT a.user_id, username, + perms, b.Seminar_id AS is_present + FROM auth_user_md5 AS a + LEFT JOIN user_info USING (user_id) + LEFT JOIN seminar_user AS b ON (b.user_id = a.user_id AND b.Seminar_id = ?) + WHERE auth_user_md5.perms IN ('autor', 'tutor', 'dozent') + AND a.visible <> 'never' + AND email LIKE ? + ORDER BY Nachname, Vorname", + [$course_id, $email] + ); + } + + /** + * Get user informations by generic datafields for csv-import + * @param string $course_id + * @param string $nachname + * @param string $datafield_id + * @return Array + */ + public function getMemberByDatafield(string $course_id, string $nachname, string $datafield_id): array + { + // TODO Fullname + return DBManager::get()->fetchAll( + "SELECT + auth_user_md5.user_id, + auth_user_md5.username, + seminar_user.Seminar_id AS is_present, + {$GLOBALS['_fullname_sql']['full_rev']} AS fullname + FROM datafields_entries + LEFT JOIN auth_user_md5 ON (auth_user_md5.user_id = datafields_entries.range_id) + LEFT JOIN user_info USING (user_id) + LEFT JOIN seminar_user ON (seminar_user.user_id = auth_user_md5.user_id AND seminar_user.Seminar_id = ?) + WHERE auth_user_md5.perms IN ('autor', 'tutor', 'dozent') + AND auth_user_md5.visible <> 'never' + AND datafields_entries.datafield_id = ? AND datafields_entries.content = ? + ORDER BY auth_user_md5.Nachname, auth_user_md5.Vorname", + [$course_id, $datafield_id, $nachname] + ); + } + /** * Export available data of a given user into a storage object * (an instance of the StoredUserData class) for that user. @@ -174,4 +313,130 @@ class CourseMember extends SimpleORMap implements PrivacyObject } } } + + /** + * return the highest position-number increased by one for the + * passed user-group in the passed seminar + * + * @param string $status can be on of 'tutor', 'dozent', ... + * @param string $seminar_id the seminar to work on + * + * @return int the next available position + */ + public static function getNextPosition(string $status, string $seminar_id): int + { + return (int) DBManager::get()->fetchColumn( + "SELECT MAX(position) + 1 + FROM seminar_user + WHERE Seminar_id = ? AND status = ?", + [$seminar_id, $status] + ); + } + + /** + * reset the order-positions for the given status in the passed seminar, + * starting at the passed position + * + * @param string $course_id the seminar to work on + * @param int $position the position to start with + * + * @return void + */ + public static function resortMembership(string $course_id, int $position, string $status = 'tutor') + { + self::findEachBySQL( + function (CourseMember $membership) { + $membership->position = $membership->position - 1; + $membership->store(); + }, + "Seminar_id = ? AND position > ? AND status = ? ", + [$course_id, $position, $status] + ); + } + /** + * Insert a user into a seminar with optional log-message and contingent + * + * @param string $seminar_id + * @param string $user_id + * @param string $status status of user in the seminar (user, autor, tutor, dozent) + * @param boolean $copy_studycourse if true, the studycourse is copied from admission_seminar_user + * to seminar_user. Overrides the $contingent-parameter + * @param string $contingent optional studiengang_id, if no id is given, no contingent is considered + * @param string $log_message optional log-message. if no log-message is given a default one is used + * @return bool + */ + public static function insertCourseMember($seminar_id, $user_id, $status, $copy_studycourse = false, $contingent = false, $log_message = false): bool + { + if (!$user_id) { + return false; + } + // get the seminar-object + $sem = Seminar::GetInstance($seminar_id); + + $admission_status = ''; + $admission_comment = ''; + $mkdate = time(); + + $admission_user = AdmissionApplication::find([$seminar_id, $user_id]); + if ($admission_user) { + // copy the studycourse from admission_seminar_user + if ($copy_studycourse && $admission_user->studiengang_id) { + $contingent = $admission_user->studiengang_id; + } + $admission_status = $admission_user->status; + $admission_comment = $admission_user->comment ?? ''; + $mkdate = $admission_user->mkdate; + } + + // check if there are places left in the submitted contingent (if any) + //ignore if preliminary + if ($admission_status !== 'accepted' && $contingent && $sem->isAdmissionEnabled() && !$sem->getFreeAdmissionSeats()) { + return false; + } + + // get coloured group as used on meine_seminare + $colour_group = $sem->getDefaultGroup(); + + // LOGGING + // if no log message is submitted use a default one + if (!$log_message) { + $log_message = 'Wurde in die Veranstaltung eingetragen, admission_status: '. $admission_status . ' Kontingent: ' . $contingent; + } + StudipLog::log('SEM_USER_ADD', $seminar_id, $user_id, $status, $log_message); + $membership = new self([$seminar_id, $user_id]); + $membership->setData([ + 'Seminar_id' => $seminar_id, + 'user_id' => $user_id, + 'status' => $status, + 'comment' => $admission_comment, + 'gruppe' => $colour_group, + 'mkdate' => $mkdate, + ]); + $membership->store(); + + NotificationCenter::postNotification('UserDidEnterCourse', $seminar_id, $user_id); + + if ($admission_status) { + $admission_user->delete(); + + //renumber the waiting/accepted/lot list, a user was deleted from it + AdmissionApplication::renumberAdmission($seminar_id); + } + $cs = $sem->getCourseSet(); + if ($cs) { + AdmissionPriority::unsetPriority($cs->getId(), $user_id, $sem->getId()); + } + + CalendarScheduleModel::deleteSeminarEntries($user_id, $seminar_id); + + // reload the seminar, the contingents have changed + $sem->restore(); + + // Check if a parent course exists and insert user there. + if ($sem->parent_course) { + self::insertCourseMember($sem->parent_course, $user_id, $status, $copy_studycourse, $contingent, $log_message); + } + + return true; + } } diff --git a/tests/functional/_bootstrap.php b/tests/functional/_bootstrap.php index 8feb13ea66ed8f5cd4dd180b63ba62ba13877c01..e05f87fed79179d89fa6678881442ef392d84113 100644 --- a/tests/functional/_bootstrap.php +++ b/tests/functional/_bootstrap.php @@ -10,6 +10,7 @@ require 'lib/classes/StudipAutoloader.php'; require 'lib/functions.php'; require_once 'lib/language.inc.php'; require 'lib/visual.inc.php'; +require 'lib/messaging.inc.php'; $STUDIP_BASE_PATH = realpath(dirname(__FILE__) . '/../..');