aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/UserManagement.php
diff options
context:
space:
mode:
authorPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
committerPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
commit4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch)
tree5c07151ae61276d334e88f6309c30d439a85c12e /lib/classes/UserManagement.php
parentda0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff)
parent97a188592c679890a25c37ab78463add76a52ff7 (diff)
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'lib/classes/UserManagement.php')
-rw-r--r--lib/classes/UserManagement.php1353
1 files changed, 1353 insertions, 0 deletions
diff --git a/lib/classes/UserManagement.php b/lib/classes/UserManagement.php
new file mode 100644
index 0000000..4bd47ec
--- /dev/null
+++ b/lib/classes/UserManagement.php
@@ -0,0 +1,1353 @@
+<?php
+# Lifter007: TODO
+/**
+ * UserManagement.php
+ *
+ * Management for the Stud.IP global users
+ *
+ * LICENSE
+ *
+ * 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 Stefan Suchi <suchi@data-quest>
+ * @author Suchi & Berg GmbH <info@data-quest.de>
+ * @copyright 2009 Stud.IP
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL Licence 2
+ * @category Stud.IP
+ */
+
+// Imports
+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';
+
+/**
+ * UserManagement.php
+ *
+ * Management for the Stud.IP global users
+ *
+ */
+class UserManagement
+{
+ private $user;
+ private $validator;
+ private $user_data;
+
+ public $msg;
+
+ private static $pwd_hasher;
+
+ public static function getPwdHasher()
+ {
+ if (self::$pwd_hasher === null) {
+ self::$pwd_hasher = new PasswordHash(8, Config::get()->PHPASS_USE_PORTABLE_HASH);
+ }
+ return self::$pwd_hasher;
+ }
+
+ /**
+ * Constructor
+ *
+ * Pass nothing to create a new user, or the user_id from an existing user to change or delete
+ * @param string $user_id the user which should be retrieved
+ */
+ public function __construct($user_id = false)
+ {
+ $this->validator = new email_validation_class();
+ $this->validator->timeout = 10; // How long do we wait for response of mailservers?
+ $this->getFromDatabase($user_id);
+ }
+
+ public function __get($attr)
+ {
+ if ($attr === 'user_data') {
+ return $this->user_data;
+ }
+ }
+
+ public function __set($attr, $value)
+ {
+ if ($attr === 'user_data') {
+ if (!is_array($value)) {
+ throw new InvalidArgumentException('user_data only accepts array');
+ }
+ return $this->user_data->setData($value, true);
+ }
+ }
+
+ /**
+ * load user data from database into internal array
+ *
+ * @param string $user_id the user which should be retrieved
+ */
+ public function getFromDatabase($user_id)
+ {
+ $this->user = User::toObject($user_id);
+ if (!$this->user) {
+ $this->user = new User();
+ }
+ $this->user_data = new UserDataAdapter($this->user);
+ }
+
+ /**
+ * store user data from internal array into database
+ *
+ * @access private
+ * @return bool all data stored?
+ */
+ private function storeToDatabase()
+ {
+ if ($this->user->isNew()) {
+ if ($this->user->store()) {
+ StudipLog::log('USER_CREATE', $this->user->id, null, implode(';', $this->user->toArray('username vorname nachname perms email')));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ $nperms = [
+ 'user' => 0,
+ 'autor' => 1,
+ 'tutor' => 2,
+ 'dozent' => 3
+ ];
+
+ if ($this->user->isDirty('perms')) {
+ if ($this->user->perms === 'dozent' && in_array($this->user->getPristineValue('perms'), ['user','autor','tutor'])) {
+ $this->logInstUserDel($this->user->id, "inst_perms = 'user'");
+ $this->user->institute_memberships->unsetBy('inst_perms', 'user');
+ // make user visible globally if dozent may not be invisible (StEP 00158)
+ if (Config::get()->DOZENT_ALWAYS_VISIBLE && $this->user->visible !== 'never') {
+ $this->user->visible = 'yes';
+ }
+ if ($nperms[$this->user->perms] < $nperms[$this->user->getPristineValue('perms')]) {
+ $old_status = [];
+ foreach ($nperms as $status => $n) {
+ if ($n > $nperms[$this->user->perms] && $n <= $nperms[$this->user->getPristineValue('perms')]) {
+ $old_status[] = $status;
+ }
+ }
+ $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()
+ ]
+ );
+ }
+ }
+ }
+ foreach (words('username vorname nachname perms email title_front title_rear password') as $field) {
+ // logging
+ if ($this->user->isFieldDirty($field)) {
+ $old_value = $this->user->getPristineValue($field);
+ $value = $this->user->getValue($field);
+ switch ($field) {
+ case 'username':
+ StudipLog::log('USER_CHANGE_USERNAME', $this->user->id, null, "{$old_value} -> {$value}");
+ break;
+ case 'vorname':
+ StudipLog::log('USER_CHANGE_NAME', $this->user->id, null, "Vorname: {$old_value} -> {$value}");
+ break;
+ case 'nachname':
+ StudipLog::log('USER_CHANGE_NAME', $this->user->id, null, "Nachname: {$old_value} -> {$value}");
+ break;
+ case 'perms':
+ StudipLog::log('USER_CHANGE_PERMS', $this->user->id, null, "{$old_value} -> {$value}");
+ break;
+ case 'email':
+ StudipLog::log('USER_CHANGE_EMAIL', $this->user->id, null, "{$old_value} -> {$value}");
+ break;
+ case 'title_front':
+ StudipLog::log('USER_CHANGE_TITLE', $this->user->id, null, "title_front: {$old_value} -> {$value}");
+ break;
+ case 'title_rear':
+ StudipLog::log('USER_CHANGE_TITLE', $this->user->id, null, "title_rear: {$old_value} -> {$value}");
+ case 'password':
+ StudipLog::log('USER_CHANGE_PASSWORD', $this->user->id, null, "password: {$old_value} -> {$value}");
+ break;
+ }
+ }
+ }
+
+ $changed = $this->user->store();
+ return (bool) $changed;
+ }
+
+
+ /**
+ * generate a secure password of $length characters [a-z0-9]
+ *
+ * @param integer $length number of characters
+ * @return string password
+ */
+ public function generate_password($length)
+ {
+ $pass = "";
+ mt_srand((double) microtime() * 1000000);
+ for ($i = 1; $i <= $length; $i++) {
+ $temp = mt_rand() % 36;
+ if ($temp < 10) {
+ $temp += 48; // 0 = chr(48), 9 = chr(57)
+ } else {
+ $temp += 87; // a = chr(97), z = chr(122)
+ }
+ $pass .= chr($temp);
+ }
+ return $pass;
+ }
+
+
+ /**
+ * Check if Email-Adress is valid and reachable
+ *
+ * @param string Email-Adress to check
+ * @return bool Email-Adress valid and reachable?
+ */
+ private function checkMail($Email)
+ {
+ // Adress correct?
+ if (!$this->validator->ValidateEmailAddress($Email)) {
+ $this->msg .= 'error§' . _('E-Mail-Adresse syntaktisch falsch!') . '§';
+ return false;
+ }
+
+ // E-Mail reachable?
+ if (!$this->validator->ValidateEmailHost($Email)) {
+ // Mailserver nicht erreichbar, ablehnen
+ $this->msg .= 'error§' . _('Mailserver ist nicht erreichbar!') . '§';
+ return false;
+ }
+
+ if (!$this->validator->ValidateEmailBox($Email)) {
+ // Nutzer unbekannt, ablehnen
+ $this->msg .= 'error§' . sprintf(_('E-Mail an <em>%s</em> ist nicht zustellbar!'), $Email) . '§';
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Create a new studip user with the given parameters
+ *
+ * @param array structure: array('string table_name.field_name'=>'string value')
+ * @return bool Creation successful?
+ */
+ public function createNewUser($newuser)
+ {
+ global $perm;
+
+ // Do we have permission to do so?
+ if (!$perm->have_perm('admin')) {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung Accounts anzulegen.') . '§';
+ return false;
+ }
+
+ if (!$perm->is_fak_admin() && $newuser['auth_user_md5.perms'] === 'admin') {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung, <em>Admin-Accounts</em> anzulegen.') . '§';
+ return false;
+ }
+
+ if (!$perm->have_perm('root') && $newuser['auth_user_md5.perms'] === 'root') {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung, <em>Root-Accounts</em> anzulegen.') . '§';
+ return false;
+ }
+
+ // Do we have all necessary data?
+ if (empty($newuser['auth_user_md5.username']) || empty($newuser['auth_user_md5.perms']) || empty($newuser['auth_user_md5.Email'])) {
+ $this->msg .= 'error§' . _('Bitte geben Sie <em>Username</em>, <em>Status</em> und <em>E-Mail</em> an!') . '§';
+ return false;
+ }
+
+ // Is the username correct?
+ if (!$this->validator->ValidateUsername($newuser['auth_user_md5.username'])) {
+ $this->msg .= 'error§' . _('Der gewählte Benutzername ist zu kurz oder enthält unzulässige Zeichen!') . '§';
+ return false;
+ }
+
+ // Can we reach the email?
+ if (!$this->checkMail($newuser['auth_user_md5.Email'])) {
+ return false;
+ }
+
+ if (!$newuser['auth_user_md5.auth_plugin']) {
+ $newuser['auth_user_md5.auth_plugin'] = 'standard';
+ }
+
+ // Store new values in internal array
+ $this->getFromDatabase(null);
+ $this->user_data->setData($newuser);
+
+ if ($this->user_data['auth_user_md5.auth_plugin'] === 'standard') {
+ $password = $this->generate_password(8);
+ $this->user_data['auth_user_md5.password'] = self::getPwdHasher()->HashPassword($password);
+ }
+
+ // Does the user already exist?
+ // NOTE: This should be a transaction, but it is not...
+ $temp = User::findByUsername($newuser['auth_user_md5.username']);
+ if ($temp) {
+ $this->msg .= 'error§' . sprintf(_('BenutzerIn <em>%s</em> ist schon vorhanden!'), $newuser['auth_user_md5.username']) . '§';
+ return false;
+ }
+
+ if (!$this->storeToDatabase()) {
+ $this->msg .= 'error§' . sprintf(_('BenutzerIn "%s" konnte nicht angelegt werden.'), $newuser['auth_user_md5.username']) . '§';
+ return false;
+ }
+
+ $this->msg .= 'msg§' . sprintf(_('BenutzerIn "%s" angelegt.'), $newuser['auth_user_md5.username']) . '§';
+
+ // Automated entering new users, based on their status (perms)
+ $result = AutoInsert::instance()->saveUser($this->user_data['auth_user_md5.user_id'], $this->user_data['auth_user_md5.perms']);
+
+ foreach ($result['added'] as $item) {
+ $this->msg .= 'msg§' . sprintf(_('Das automatische Eintragen in die Veranstaltung <em>%s</em> wurde durchgeführt.'), $item) . '§';
+ }
+ foreach ($result['removed'] as $item) {
+ $this->msg .= 'msg§' . sprintf(_('Das automatische Austragen aus der Veranstaltung <em>%s</em> wurde durchgeführt.'), $item) . '§';
+ }
+
+ // include language-specific subject and mailbody
+ $user_language = $this->user_data['user_info.preferred_language'] ?: Config::get()->DEFAULT_LANGUAGE;
+
+ // send mail with password generation link
+ self::sendPasswordMail($this->user, true);
+ $this->msg .= 'msg§' . _('Es wurde eine Mail mit Anweisungen zum Setzen des Passworts durch die/den Nutzer/in verschickt.') . '§';
+
+ // add default visibility settings
+ Visibility::createDefaultCategories($this->user_data['auth_user_md5.user_id']);
+
+ return true;
+ }
+
+ /**
+ * Create a new preliminary studip user with the given parameters
+ *
+ * @param array structure: array('string table_name.field_name'=>'string value')
+ * @return bool Creation successful?
+ */
+ public function createPreliminaryUser($newuser)
+ {
+ global $perm;
+
+ $this->getFromDatabase(null);
+ $this->user_data->setData($newuser);
+ // Do we have permission to do so?
+ if (!$perm->have_perm('admin')) {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung Accounts anzulegen.') . '§';
+ return false;
+ }
+ if (in_array($this->user->perms, words('root admin'))) {
+ $this->msg .= 'error§' . _('Es können keine vorläufigen Administrationsaccounts angelegt werden.') . '§';
+ return false;
+ }
+ if (!$this->user->id) {
+ $this->user->setId($this->user->getNewId());
+ }
+ if (!$this->user->username) {
+ $this->user->username = $this->user->id;
+ }
+ $this->user->auth_plugin = null;
+ $this->user->visible = 'never';
+
+ // Do we have all necessary data?
+ if (empty ($this->user->perms) || empty ($this->user->vorname) || empty ($this->user->nachname)) {
+ $this->msg .= 'error§' . _('Bitte geben Sie <em>Status</em>, <em>Vorname</em> und <em>Nachname</em> an!') . '§';
+ return false;
+ }
+
+ // Is the username correct?
+ if (!$this->validator->ValidateUsername($this->user->username)) {
+ $this->msg .= 'error§' . _('Der gewählte Benutzername ist zu kurz oder enthält unzulässige Zeichen!') . '§';
+ return false;
+ }
+
+ // Does the user already exist?
+ // NOTE: This should be a transaction, but it is not...
+ $temp = User::findByUsername($this->user->username);
+ if ($temp) {
+ $this->msg .= 'error§' . sprintf(_('BenutzerIn <em>%s</em> ist schon vorhanden!'), $this->user->username) . '§';
+ return false;
+ }
+
+ if (!$this->storeToDatabase()) {
+ $this->msg .= 'error§' . sprintf(_('BenutzerIn "%s" konnte nicht angelegt werden.'), $this->user->username) . '§';
+ return false;
+ }
+
+ $this->msg .= 'msg§' . sprintf(_('BenutzerIn "%s" (vorläufig) angelegt.'), $this->user->username) . '§';
+
+ // add default visibility settings
+ Visibility::createDefaultCategories($this->user->id);
+
+ return true;
+ }
+
+ /**
+ * Change an existing studip user according to the given parameters
+ *
+ * @param array structure: array('string table_name.field_name'=>'string value')
+ * @return bool Change successful?
+ */
+ public function changeUser($newuser)
+ {
+ global $perm;
+
+ // Do we have permission to do so?
+ if (!$perm->have_perm('admin')) {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung Accounts zu verändern.') . '§';
+ return false;
+ }
+
+ if (!$perm->is_fak_admin() && $newuser['auth_user_md5.perms'] === 'admin') {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung, <em>Admin-Accounts</em> anzulegen.') . '§';
+ return false;
+ }
+
+ if (!$perm->have_perm('root') && $newuser['auth_user_md5.perms'] === 'root') {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung, <em>Root-Accounts</em> anzulegen.') . '§';
+ return false;
+ }
+
+ if (!$perm->have_perm('root')) {
+ if (!$perm->is_fak_admin() && $this->user_data['auth_user_md5.perms'] === 'admin') {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung <em>Admin-Accounts</em> zu verändern.') . '§';
+ return false;
+ }
+
+ if ($this->user_data['auth_user_md5.perms'] === 'root') {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung <em>Root-Accounts</em> zu verändern.') . '§';
+ return false;
+ }
+
+ if ($perm->is_fak_admin() && $this->user_data['auth_user_md5.perms'] === 'admin') {
+ if (!$this->adminOK()) {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung diesen Admin-Account zu verändern.') . '§';
+ return false;
+ }
+ }
+ }
+
+ // active dozent? (ignore the studygroup guys)
+ $status = studygroup_sem_types();
+
+ if (empty($status)) {
+ $count = 0;
+ } else {
+ $query = "SELECT COUNT(*)
+ FROM seminar_user AS su
+ LEFT JOIN seminare AS s USING (Seminar_id)
+ WHERE su.user_id = ?
+ AND s.status NOT IN (?)
+ AND su.status = 'dozent'
+ AND (SELECT COUNT(*) FROM seminar_user su2 WHERE Seminar_id = su.Seminar_id AND su2.status = 'dozent') = 1
+ GROUP BY user_id";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([
+ $this->user_data['auth_user_md5.user_id'],
+ $status,
+ ]);
+ $count = $statement->fetchColumn();
+ }
+ if ($count && isset($newuser['auth_user_md5.perms']) && $newuser['auth_user_md5.perms'] !== 'dozent') {
+ $this->msg .= 'error§' . sprintf(_('Der Benutzer <em>%s</em> ist alleiniger Lehrperson in %s aktiven Veranstaltungen und kann daher nicht in einen anderen Status versetzt werden!'), $this->user_data['auth_user_md5.username'], $count) . '§';
+ return false;
+ }
+
+ // active admin?
+ if ($this->user_data['auth_user_md5.perms'] === 'admin' && $newuser['auth_user_md5.perms'] !== 'admin') {
+ // count number of institutes where the user is admin
+ $query = "SELECT COUNT(*)
+ FROM user_inst
+ WHERE user_id = ? AND inst_perms = 'admin'
+ GROUP BY Institut_id";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+
+ // if there are institutes with admin-perms, add error-message and deny change
+ if ($count = $statement->fetchColumn()) {
+ $this->msg .= 'error§'. sprintf(_('Der Benutzer <em>%s</em> ist Admin in %s Einrichtungen und kann daher nicht in einen anderen Status versetzt werden!'), $this->user_data['auth_user_md5.username'], $count) . '§';
+ return false;
+ }
+ }
+
+ // Is the username correct?
+ if (isset($newuser['auth_user_md5.username'])) {
+ if ($this->user_data['auth_user_md5.username'] != $newuser['auth_user_md5.username']) {
+ if (!$this->validator->ValidateUsername($newuser['auth_user_md5.username'])) {
+ $this->msg .= 'error§' . _('Der gewählte Benutzername ist zu kurz oder enthält unzulässige Zeichen!') . '§';
+ return false;
+ }
+ $check_uname = StudipAuthAbstract::CheckUsername($newuser['auth_user_md5.username']);
+ if ($check_uname['found']) {
+ $this->msg .= 'error§' . _('Der Benutzername wird bereits von einem anderen Benutzer verwendet. Bitte wählen Sie einen anderen Benutzernamen!') . '§';
+ return false;
+ } else {
+ //$this->msg .= "info§" . $check_uname['error'] ."§";
+ }
+ } else
+ unset($newuser['auth_user_md5.username']);
+ }
+
+ // Can we reach the email?
+ if (isset($newuser['auth_user_md5.Email'])) {
+ if (!$this->checkMail($newuser['auth_user_md5.Email'])) {
+ return false;
+ }
+ }
+
+ // Store changed values in internal array if allowed
+ $old_perms = $this->user_data['auth_user_md5.perms'];
+ $auth_plugin = $this->user_data['auth_user_md5.auth_plugin'];
+ foreach ($newuser as $key => $value) {
+ if (!StudipAuthAbstract::CheckField($key, $auth_plugin)) {
+ $this->user_data[$key] = $value;
+ } else if ($this->user_data[$key] !== $value) {
+ $this->msg .= 'error§' . sprintf(_('Das Feld <em>%s</em> können Sie nicht ändern!'), $key) . '§';
+ return false;
+ }
+ }
+
+ if (!$this->storeToDatabase()) {
+ $this->msg .= 'info§' . _('Es wurden keine Veränderungen der Grunddaten vorgenommen.') . '§';
+ return true;
+ }
+
+ $this->msg .= 'msg§' . sprintf(_('Benutzer "%s" verändert.'), $this->user_data['auth_user_md5.username']) . '§';
+ if ($auth_plugin !== null) {
+ // Automated entering new users, based on their status (perms)
+ $result = AutoInsert::instance()->saveUser($this->user_data['auth_user_md5.user_id'], $newuser['auth_user_md5.perms']);
+ foreach ($result['added'] as $item) {
+ $this->msg .= 'msg§' . sprintf(_('Das automatische Eintragen in die Veranstaltung <em>%s</em> wurde durchgeführt.'), $item) . '§';
+ }
+ foreach ($result['removed'] as $item) {
+ $this->msg .= 'msg§' . sprintf(_('Das automatische Austragen aus der Veranstaltung <em>%s</em> wurde durchgeführt.'), $item) . '§';
+ }
+ // include language-specific subject and mailbody
+ $user_language = getUserLanguagePath($this->user_data['auth_user_md5.user_id']);
+ $Zeit = strftime('%x, %X');
+
+ // TODO: This should be refactored so that the included file returns an array
+ include "locale/{$user_language}/LC_MAILS/change_mail.inc.php"; // Defines $subject and $mailbody
+
+ // send mail
+ StudipMail::sendMessage($this->user_data['auth_user_md5.Email'], $subject ?? '', $mailbody ?? '');
+ }
+ // Upgrade to admin or root?
+ if (in_array($newuser['auth_user_md5.perms'], ['admin', 'root'])) {
+ $this->re_sort_position_in_seminar_user();
+
+ // delete all seminar entries
+ $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('AdmissionApplication::addMembers', $seminar_ids);
+ }
+ // delete all entries from waiting lists
+ $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('AdmissionApplication::addMembers', $seminar_ids);
+ }
+ // delete 'Studiengaenge'
+ 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) . '§';
+ }
+ }
+
+ if ($newuser['auth_user_md5.perms'] === 'admin') {
+
+ $this->logInstUserDel($this->user_data['auth_user_md5.user_id'], "inst_perms != 'admin'");
+ $query = "DELETE FROM user_inst WHERE user_id = ? AND inst_perms != 'admin'";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+ if ($count = $statement->rowCount()) {
+ $this->msg .= 'info§' . sprintf(_('%s Einträge aus MitarbeiterInnenlisten gelöscht.'), $count) . '§';
+ }
+ }
+ if ($newuser['auth_user_md5.perms'] === 'root') {
+ $this->logInstUserDel($this->user_data['auth_user_md5.user_id']);
+
+ $query = "DELETE FROM user_inst WHERE user_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+ if ($count = $statement->rowCount()) {
+ $this->msg .= 'info§' . sprintf(_('%s Einträge aus MitarbeiterInnenlisten gelöscht.'), $count) . '§';
+ }
+ }
+
+ return true;
+ }
+
+ private function logInstUserDel($user_id, $condition = null)
+ {
+ $query = "SELECT Institut_id FROM user_inst WHERE user_id = ?";
+ if (isset($condition)) {
+ $query .= ' AND ' . $condition;
+ }
+
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$user_id]);
+ while ($institute_id = $statement->fetchColumn()) {
+ StudipLog::log('INST_USER_DEL', $institute_id, $user_id);
+ }
+ }
+
+ /**
+ * Mail a password generation link to the user
+ *
+ * @return bool Password change successful?
+ */
+ public function setPassword()
+ {
+ global $perm;
+
+ // Do we have permission to do so?
+ if (!$perm->have_perm('admin')) {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung Accounts zu verändern.') . '§';
+ return false;
+ }
+
+ if (!$perm->have_perm('root')) {
+ if ($this->user_data['auth_user_md5.perms'] === "root") {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung <em>Root-Accounts</em> zu verändern.') . '§';
+ return false;
+ }
+ if ($perm->is_fak_admin() && $this->user_data['auth_user_md5.perms'] === 'admin') {
+ if (!$this->adminOK()) {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung diesen Admin-Account zu verändern.') . '§';
+ return false;
+ }
+ }
+ }
+
+ // Can we reach the email?
+ if (!$this->checkMail($this->user_data['auth_user_md5.Email'])) {
+ return false;
+ }
+
+ self::sendPasswordMail($this->user);
+
+ return true;
+ }
+
+ /**
+ * Send a mail to the user denoted by the passed user-object with a link
+ * to reset the password. For admin, root and non-standard-auth a notification
+ * is sent instead.
+ *
+ * @param User $user
+ *
+ * @return void
+ */
+ public static function sendPasswordMail($user, $new = false)
+ {
+ setTempLanguage($user->user_id);
+
+ // always generate a token, so root, admin and all other users profit from the abuse protection
+ if ($new) {
+ $expiration_in_hours = 24;
+ $spoken_expiration = _('24 Stunden');
+ } else {
+ $expiration_in_hours = 7 * 24;
+ $spoken_expiration = _('eine Woche');
+ }
+ $token = Token::create($expiration_in_hours * 60 * 60, $user->id, true);
+
+ // new users alawys receive a link to generate a password
+ if ($new) {
+ $subject = sprintf(
+ _("[Stud.IP - %s] Es wurde ein Zugang für sie erstellt - Setzen sie ein Passwort"),
+ Config::get()->UNI_NAME_CLEAN
+ );
+
+ $mailbody = sprintf(
+ _("Dies ist eine Bestätigungsmail des Stud.IP-Systems\n"
+ ."(Studienbegleitender Internetsupport von Präsenzlehre)\n- %1\$s -\n\n"
+ ."Es wurde für sie ein Zugang zum System erstellt, Ihr Nutzername lautet:\n\n"
+ ."%2\$s\n\n"
+ ."Um den Zugang nutzen zu können, müssen sie ein Passwort setzen.\n"
+ ."Öffnen Sie dafür bitte folgenden Link\n\n"
+ ."%3\$s\n\n"
+ ."in Ihrem Browser.\n\n"
+ ."Der Link ist %4\$s (bis %5\$s) gültig.\n\n"
+ ."Wahrscheinlich unterstützt Ihr E-Mail-Programm ein einfaches Anklicken des Links.\n"
+ ."Ansonsten müssen Sie Ihren Browser öffnen und den Link komplett in die Zeile\n"
+ ."\"Location\" oder \"URL\" kopieren.\n\n"
+ ),
+ Config::get()->UNI_NAME_CLEAN,
+ $user->username,
+ $GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/new_password/set/'. $token->token .'?cancel_login=1',
+ $spoken_expiration,
+ strftime('%x %X', $token->expiration)
+ );
+ } else
+
+ // only users with auth-type standard cann reset their password
+ if ($user->auth_plugin !== 'standard') {
+
+ // inform user, that their password cannot be reset via mail
+ $subject = sprintf(
+ _("[Stud.IP - %s] Passwortänderung angefordert"),
+ Config::get()->UNI_NAME_CLEAN
+ );
+
+ $mailbody = sprintf(
+ _("Dies ist eine Informationsmail des Stud.IP-Systems\n"
+ ."(Studienbegleitender Internetsupport von Präsenzlehre)\n- %s -\n\n"
+ . "Sie haben einen Link angefordert\n"
+ . "um das Passwort zurückzusetzen.\n"
+ . "Dies ist aber für den mit dieser Mail \n"
+ . "verknüpften Account so nicht möglich.\n\n"
+ . "Wenden sie sich bitte stattdessen an\n%s"
+ ),
+ Config::get()->UNI_NAME_CLEAN,
+ $GLOBALS['UNI_CONTACT']
+ );
+
+ } else {
+
+ $subject = sprintf(
+ _("[Stud.IP - %s] Neues Passwort setzen"),
+ Config::get()->UNI_NAME_CLEAN
+ );
+
+ $mailbody = sprintf(
+ _("Dies ist eine Bestätigungsmail des Stud.IP-Systems\n"
+ ."(Studienbegleitender Internetsupport von Präsenzlehre)\n- %1\$s -\n\n"
+ ."Sie haben um die Zurücksetzung des Passwortes zu Ihrem Benutzernamen %5\$s gebeten.\n\n"
+ ."Diese E-Mail wurde Ihnen zugesandt um sicherzustellen,\n"
+ ."dass die angegebene E-Mail-Adresse tatsächlich Ihnen gehört.\n\n"
+ ."Wenn Sie um die Zurücksetzung Ihres Passwortes gebeten haben,\n"
+ ."dann öffnen Sie bitte folgenden Link\n\n"
+ ."%2\$s\n\n"
+ ."in Ihrem Browser. Auf der Seite können Sie ein neues Passwort setzen.\n\n"
+ ."Der Link ist %3\$s (bis %4\$s) gültig.\n\n"
+ ."Wahrscheinlich unterstützt Ihr E-Mail-Programm ein einfaches Anklicken des Links.\n"
+ ."Ansonsten müssen Sie Ihren Browser öffnen und den Link komplett in die Zeile\n"
+ ."\"Location\" oder \"URL\" kopieren.\n\n"
+ ."Falls Sie nicht diese Mail nicht angefordert haben\n"
+ ."oder überhaupt nicht wissen, wovon hier die Rede ist,\n"
+ ."dann hat jemand Ihre E-Mail-Adresse fälschlicherweise verwendet!\n"
+ ."Ignorieren Sie in diesem Fall diese E-Mail. Es werden dann keine\n"
+ ."Änderungen an Ihren Zugangsdaten vorgenommen.\n\n"
+ ),
+ Config::get()->UNI_NAME_CLEAN,
+ $GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/new_password/set/'. $token->token .'?cancel_login=1',
+ $spoken_expiration,
+ strftime('%x %X', $token->expiration),
+ $user->username
+ );
+ }
+
+ StudipMail::sendMessage($user->email, $subject, $mailbody);
+
+ restoreLanguage();
+ }
+
+ /**
+ * Delete an existing user from the database and tidy up
+ *
+ * @param $delete_documents bool delete all documents in course context belonging to the user
+ * @param $delete_content_from_course bool delete all course content belonging to the user
+ * @param $delete_personal_documents bool delete all personal documents belonging to the user
+ * @param $delete_personal_content bool delete all personal content belonging to the user
+ * @param $delete_names bool delete all names identifying the user
+ * @param $delete_memberships bool delete all memberships of the user
+ * @param bool $send_email_notification bool send an email that the account has been deleted
+ * @return bool Removal successful?
+ */
+ public function deleteUser(
+ bool $delete_documents = true,
+ bool $delete_content_from_course = true,
+ bool $delete_personal_documents = true,
+ bool $delete_personal_content = true,
+ bool $delete_names = true,
+ bool $delete_memberships = true,
+ bool $send_email_notification = true,
+ bool $delete_courseware = true
+ ): bool {
+ global $perm;
+
+ // Do we have permission to do so?
+ if (!$perm->have_perm('admin')) {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung Accounts zu löschen.') . '§';
+ return FALSE;
+ }
+
+ if (!$perm->have_perm('root')) {
+ if ($this->user_data['auth_user_md5.perms'] === 'root') {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung <em>Root-Accounts</em> zu löschen.') . '§';
+ return false;
+ }
+ if ($this->user_data['auth_user_md5.perms'] === 'admin' && !$this->adminOK()) {
+ $this->msg .= 'error§' . _('Sie haben keine Berechtigung diesen Admin-Account zu löschen.') . '§';
+ return false;
+ }
+ }
+
+ // active dozent?
+ $query = "SELECT COUNT(*)
+ FROM (
+ SELECT 1
+ FROM `seminar_user` AS `su1`
+ -- JOIN seminar_user to check for other teachers
+ INNER JOIN `seminar_user` AS `su2`
+ ON (`su1`.`seminar_id` = `su2`.`seminar_id` AND `su2`.`status` = 'dozent')
+ -- JOIN seminare to check the status for studygroup mode
+ INNER JOIN `seminare`
+ ON (`su1`.`seminar_id` = `seminare`.`seminar_id`)
+ WHERE `su1`.`user_id` = :user_id
+ AND `su1`.`status` = 'dozent'
+ AND `seminare`.`status` NOT IN (
+ -- Select all status ids for studygroups
+ SELECT `id`
+ FROM `sem_classes`
+ WHERE `studygroup_mode` = 1
+ )
+ GROUP BY `su1`.`seminar_id`
+ HAVING COUNT(*) = 1
+ ORDER BY NULL
+ ) AS `sub`";
+ $statement = DBManager::get()->prepare($query);
+ $statement->bindValue(':user_id', $this->user_data['auth_user_md5.user_id']);
+ $statement->execute();
+ $active_count = $statement->fetchColumn() ?: 0;
+
+ if ($active_count && $delete_memberships) {
+ $this->msg .= 'error§' . sprintf(_('<em>%s</em> ist Lehrkraft in %s aktiven Veranstaltungen und kann daher nicht gelöscht werden.'), $this->user_data['auth_user_md5.username'], $active_count) . '§';
+ return false;
+ //founder of studygroup?
+ } elseif (Config::get()->STUDYGROUPS_ENABLE) {
+ $status = studygroup_sem_types();
+
+ if (empty($status)) {
+ $group_ids = [];
+ } else {
+ $query = "SELECT Seminar_id
+ FROM seminare AS s
+ LEFT JOIN seminar_user AS su USING (Seminar_id)
+ WHERE su.status = 'dozent' AND su.user_id = ? AND s.status IN (?)";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([
+ $this->user_data['auth_user_md5.user_id'],
+ $status,
+ ]);
+ $group_ids = $statement->fetchAll(PDO::FETCH_COLUMN);
+ }
+
+ foreach ($group_ids as $group_id) {
+ $sem = Seminar::GetInstance($group_id);
+ if (StudygroupModel::countMembers($group_id) > 1) {
+ // check whether there are tutors or even autors that can be promoted
+ $tutors = $sem->getMembers('tutor');
+ $autors = $sem->getMembers('autor');
+ if (count($tutors) > 0) {
+ $new_founder = current($tutors);
+ StudygroupModel::promote_user($new_founder['username'], $sem->getId(), 'dozent');
+ continue;
+ }
+ // if not promote an autor
+ elseif (count($autors) > 0) {
+ $new_founder = current($autors);
+ StudygroupModel::promote_user($new_founder['username'], $sem->getId(), 'dozent');
+ continue;
+ }
+ // since no suitable successor was found, we are allowed to remove the studygroup
+ } else {
+ $sem->delete();
+ }
+ unset($sem);
+ }
+ }
+
+ // store user preferred language for sending mail
+ $user_language = getUserLanguagePath($this->user_data['auth_user_md5.user_id']);
+
+ // Load privacy plugins to ensure all event handlers can react to the
+ // UserDataDidRemove event
+ PluginEngine::getPlugins(PrivacyPlugin::class);
+
+ // delete user from instituts
+ $this->logInstUserDel($this->user_data['auth_user_md5.user_id']);
+
+ if ($delete_memberships) {
+ $query = "DELETE FROM user_inst WHERE user_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+ if ($count = $statement->rowCount()) {
+ $this->msg .= 'info§' . sprintf(_('%s Einträge aus MitarbeiterInnenlisten gelöscht.'), $count) . '§';
+ }
+
+ // delete user from Statusgruppen
+ if ($count = StatusgruppeUser::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']])) {
+ $this->msg .= 'info§' . sprintf(_('%s Einträge aus Funktionen / Gruppen gelöscht.'), $count) . '§';
+ }
+
+ // delete user from archiv
+ $query = "DELETE FROM archiv_user WHERE user_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+ if ($count = $statement->rowCount()) {
+ $this->msg .= 'info§' . sprintf(_('%s Einträge aus den Zugriffsberechtigungen für das Archiv gelöscht.'), $count) . '§';
+ }
+
+ // 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()) {
+ $this->msg .= 'info§' . sprintf(_('%s Zuordnungen zu Studiengängen gelöscht.'), $count) . '§';
+ }
+
+ $this->re_sort_position_in_seminar_user();
+
+ // delete user from seminars (postings will be preserved)
+ $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()) {
+ $this->msg .= 'info§' . sprintf(_('%s Einträge aus Veranstaltungen gelöscht.'), $count) . '§';
+ }
+
+ $query = "DELETE FROM `termin_related_persons` WHERE `user_id` = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+ if ($count = $statement->rowCount()) {
+ $this->msg .= 'info§' . sprintf(_('%u Terminzuordnungen gelöscht.'), $count) . '§';
+ }
+
+ // delete visibility settings
+ Visibility::removeUserPrivacySettings($this->user_data['auth_user_md5.user_id']);
+
+ // delete deputy entries if necessary
+ $query = "DELETE FROM deputies WHERE ? IN (user_id, range_id)";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+ $deputyEntries = $statement->rowCount();
+ if ($deputyEntries) {
+ $this->msg .= 'info§' . sprintf(_('%s Einträge in den Vertretungseinstellungen gelöscht.'), $deputyEntries) . '§';
+ }
+
+ // delete all remaining user data
+ $queries = [
+ "DELETE FROM user_userdomains WHERE user_id = ?",
+ ];
+ foreach ($queries as $query) {
+ DBManager::get()->execute($query, [$this->user_data['auth_user_md5.user_id']]);
+ }
+ NotificationCenter::postNotification('UserDataDidRemove', $this->user_data['auth_user_md5.user_id'], 'memberships');
+ }
+
+ // delete documents of this user
+ if ($delete_documents) {
+ $db_filecount = FileRef::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ if ($db_filecount > 0) {
+ $this->msg .= 'info§' . sprintf(_('%s Dateien aus Veranstaltungen und Einrichtungen gelöscht.'), $db_filecount) . '§';
+ }
+ NotificationCenter::postNotification('UserDataDidRemove', $this->user_data['auth_user_md5.user_id'], 'course_documents');
+ }
+
+ // always delete personal courseware elements of this user
+ \Courseware\Unit::deleteBySQL('range_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\UserDataField::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\UserProgress::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\Bookmark::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\Task::deleteBySQL(
+ '`solver_id` = ? AND `solver_type`= "autor"',
+ [$this->user_data['auth_user_md5.user_id']]
+ );
+ // delete courseware elements in courses of this user
+ if ($delete_courseware) {
+ \Courseware\Unit::deleteBySQL('creator_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\StructuralElement::deleteBySQL('owner_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\StructuralElementFeedback::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\StructuralElementComment::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\Container::deleteBySQL('owner_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\Block::deleteBySQL('owner_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\BlockFeedback::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ \Courseware\BlockComment::deleteBySQL('user_id = ?', [$this->user_data['auth_user_md5.user_id']]);
+ }
+
+ // delete all remaining user data in course context if option selected
+ if ($delete_content_from_course) {
+ $queries = [
+ "DELETE FROM questionnaires WHERE user_id = ?",
+ "DELETE FROM questionnaire_answers WHERE user_id = ?",
+ "DELETE FROM questionnaire_assignments WHERE user_id = ?",
+ "DELETE FROM questionnaire_anonymous_answers WHERE user_id = ?",
+ "DELETE FROM etask_assignment_attempts WHERE user_id = ?",
+ "DELETE FROM etask_responses WHERE user_id = ?",
+ "DELETE FROM etask_tasks WHERE user_id = ?",
+ "DELETE FROM etask_tests WHERE user_id = ?",
+ ];
+ foreach ($queries as $query) {
+ DBManager::get()->execute($query, [$this->user_data['auth_user_md5.user_id']]);
+ }
+ NotificationCenter::postNotification('UserDataDidRemove', $this->user_data['auth_user_md5.user_id'], 'course_contents');
+ }
+
+ if ($delete_personal_documents) {
+ $user_folder = Folder::findTopFolder($this->user->id);
+ if ($user_folder) {
+ $this->msg .= 'info§' . _('Persönlicher Dateibereich gelöscht.') . '§';
+ $user_folder->delete();
+ }
+ NotificationCenter::postNotification('UserDataDidRemove', $this->user_data['auth_user_md5.user_id'], 'personal_documents');
+ }
+
+ if ($delete_personal_content) {
+ $this->msg .= $this->deletePersonalData($this->user_data['auth_user_md5.user_id']);
+ NotificationCenter::postNotification('UserDataDidRemove', $this->user_data['auth_user_md5.user_id'], 'personal_contents');
+ }
+
+ if ($delete_names) {
+ $query = "UPDATE auth_user_md5
+ SET username = ?, Vorname = '', Nachname = '', Email = ''
+ WHERE user_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([
+ md5($this->user_data['auth_user_md5.username'].uniqid('delete_user')),
+ $this->user_data['auth_user_md5.user_id']
+ ]);
+ if ($statement->rowCount() > 0) {
+ $this->msg .= 'info§' . _('Benutzername anonymisiert.') . '§';
+ }
+ NotificationCenter::postNotification('UserDataDidRemove', $this->user_data['auth_user_md5.user_id'], 'names');
+ }
+
+ if ($delete_personal_documents && $delete_personal_content && $delete_names && $delete_memberships) {
+ // Delete the user from resource property entries of type "user":
+ ResourceProperty::deleteBySQL(
+ "`property_id` IN (
+ SELECT `property_id`
+ FROM `resource_property_definitions`
+ WHERE `type` = 'user'
+ )
+ AND `state` = :user_id",
+ ['user_id' => $this->user_data['auth_user_md5.user_id']]
+ );
+
+ // delete Stud.IP account
+ $query = "DELETE FROM user_info WHERE user_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+
+ $query = "DELETE FROM auth_user_md5 WHERE user_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+ if (!$statement->rowCount()) {
+ $this->msg .= 'error§<em>' . _('Fehler:') . '</em> ' . $query . '§';
+ return false;
+ } else {
+ $this->msg .= 'msg§' . sprintf(_('Benutzer "%s" gelöscht.'), $this->user_data['auth_user_md5.username']) . '§';
+ }
+ StudipLog::log('USER_DEL', $this->user_data['auth_user_md5.user_id'], null, sprintf('%s %s (%s)', $this->user_data['auth_user_md5.Vorname'], $this->user_data['auth_user_md5.Nachname'], $this->user_data['auth_user_md5.username'])); //log with Vorname Nachname (username) as info string
+
+ // Can we reach the email?
+ if (
+ $send_email_notification
+ && $this->checkMail($this->user_data['auth_user_md5.Email'])
+ ) {
+ // include language-specific subject and mailbody
+ $Zeit = strftime('%x, %X');
+
+ // TODO: This should be refactored so that the included file returns an array
+ include "locale/$user_language/LC_MAILS/delete_mail.inc.php"; // Defines $subject and $mailbody
+
+ // send mail
+ StudipMail::sendMessage($this->user_data['auth_user_md5.Email'], $subject ?? '', $mailbody ?? '');
+ }
+
+ // Remove plugin associations/activations
+ PluginManager::getInstance()->deactivateAllPluginsForRange(
+ 'user',
+ $this->user_data['auth_user_md5.user_id']
+ );
+
+ // Trigger delete on sorm object which will fire notifications
+ //
+ // TODO: Remove everything from this method that would also be
+ // deleted in User::delete() (TODO!!!)
+ $this->user->delete();
+
+ unset($this->user_data);
+ }
+
+ return true;
+ }
+
+ /**
+ * Delete personal userdata
+ *
+ * @param string $user_id the user which should be retrieved
+ * @return string Removal messages
+ */
+ private function deletePersonalData($user_id)
+ {
+ $msg = '';
+
+ // delete the datafields
+ $localEntries = DataFieldEntry::removeAll($user_id);
+
+ // delete user from waiting lists
+ $query = "SELECT seminar_id FROM admission_seminar_user WHERE user_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$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([$user_id]);
+ if ($count = $statement->rowCount()) {
+ $msg .= 'info§' . sprintf(_('%s Einträge aus Wartelisten gelöscht.'), $count) . '§';
+ array_map('AdmissionApplication::addMembers', $seminar_ids);
+ }
+
+ // delete all personal news from this user
+ if ($count = StudipNews::DeleteNewsByAuthor($user_id)) {
+ $msg .= 'info§' . sprintf(_('%s Einträge aus den Ankündigungen gelöscht.'), $count) . '§';
+ }
+ if ($count = StudipNews::DeleteNewsRanges($user_id)) {
+ $msg .= 'info§' . sprintf(_('%s Verweise auf Ankündigungen gelöscht.'), $count) . '§';
+ }
+
+ //delete entry in news_rss_range
+ StudipNews::UnsetRssId($user_id);
+
+ // delete all private appointments of this user
+ if (Config::get()->CALENDAR_ENABLE) {
+ // delete private appointments (omit group appointments)
+ $count = CalendarDate::deleteBySQL(
+ '`id` IN (
+ SELECT `id`
+ FROM (
+ SELECT `id`, COUNT(*)
+ FROM `calendar_dates`
+ JOIN `calendar_date_assignments`
+ ON `calendar_dates`.`id` = `calendar_date_assignments`.`calendar_date_id`
+ WHERE `calendar_dates`.`author_id` = :user_id
+ GROUP BY `id`
+ HAVING COUNT(*) = 1
+ ORDER BY NULL
+ ) AS `cal_date_delete`
+ )',
+ [':user_id' => $user_id]
+ );
+ // delete assignments to group appointments
+ $count += CalendarDateAssignment::deleteBySQL('`range_id` = ?', [$user_id]);
+ if ($count) {
+ $msg .= 'info§' . sprintf(_('%s Einträge aus den Terminen gelöscht.'), $count) . '§';
+ }
+ }
+
+ // delete all messages send or received by this user
+ $messaging = new messaging();
+ $messaging->delete_all_messages($user_id);
+
+ // delete user from all foreign adressbooks and empty own adressbook
+ $count = Contact::deleteBySQL('user_id = ?', [$user_id]);
+ if ($count > 0) {
+ $msg .= 'info§' . sprintf(_('%s Einträge aus Adressbüchern gelöscht.'), $count) . '§';
+ }
+ $count = Contact::deleteBySQL('owner_id = ?', [$user_id]);
+ if ($count) {
+ $msg .= 'info§' . sprintf(_('Adressbuch mit %d Einträgen gelöscht.'), $count) . '§';
+ }
+
+ // delete users groups
+ Statusgruppen::deleteBySQL('range_id = ?', [$user_id]);
+
+ // remove user from any groups
+ StatusgruppeUser::deleteBySQL('user_id = ?', [$user_id]);
+
+ // delete user config values
+ ConfigValue::deleteBySQL('range_id = ?', [$user_id]);
+
+ // delete all remaining user data
+ $queries = [
+ "DELETE FROM kategorien WHERE range_id = ?",
+ "DELETE FROM user_visibility WHERE user_id = ?",
+ "DELETE FROM user_online WHERE user_id = ?",
+ "DELETE FROM auto_insert_user WHERE user_id = ?",
+ "DELETE FROM roles_user WHERE userid = ?",
+ "DELETE FROM schedule WHERE user_id = ?",
+ "DELETE FROM schedule_seminare WHERE user_id = ?",
+ "DELETE FROM termin_related_persons WHERE user_id = ?",
+ "DELETE FROM priorities WHERE user_id = ?",
+ "DELETE FROM api_oauth_user_mapping WHERE user_id = ?",
+ "DELETE FROM api_user_permissions WHERE user_id = ?",
+ "DELETE FROM help_tour_user WHERE user_id = ?",
+ "DELETE FROM personal_notifications_user WHERE user_id = ?",
+ "DELETE FROM forum_abo_users WHERE user_id = ?",
+ "DELETE FROM forum_favorites WHERE user_id = ?",
+
+ "DELETE FROM comments WHERE user_id = ?",
+ "DELETE questionnaires FROM questionnaires LEFT JOIN questionnaire_assignments qa USING (`questionnaire_id`) WHERE qa.range_id = ?",
+ "DELETE questionnaire_answers FROM questionnaire_answers LEFT JOIN questionnaire_questions USING (`question_id`) LEFT JOIN questionnaire_assignments qa USING (`questionnaire_id`) WHERE qa.range_id = ?",
+ "DELETE questionnaire_anonymous_answers FROM questionnaire_anonymous_answers LEFT JOIN questionnaire_assignments qa USING (`questionnaire_id`) WHERE qa.range_id = ?",
+ "DELETE FROM questionnaire_assignments WHERE user_id = ?",
+ "DELETE etask_assignment_attempts FROM etask_assignment_attempts LEFT JOIN etask_assignments ea ON (`assignment_id` = ea.id) WHERE ea.range_type = 'user' AND user_id = ?",
+ "DELETE etask_responses FROM etask_responses LEFT JOIN etask_assignments ea ON (`assignment_id` = ea.id) WHERE ea.range_type = 'user' AND user_id = ?",
+ "DELETE etask_tasks FROM etask_tasks LEFT JOIN etask_test_tasks tt ON (etask_tasks.id = tt.task_id) LEFT JOIN etask_assignments ea ON (tt.`test_id` = ea.test_id) WHERE ea.range_type = 'user' AND user_id = ?",
+ "DELETE etask_tests FROM etask_tests LEFT JOIN etask_assignments ea ON (`test_id` = ea.test_id) WHERE ea.range_type = 'user' AND user_id = ?",
+
+ "UPDATE forum_entries SET author = '' WHERE user_id = ?",
+ "UPDATE auth_user_md5 SET visible = 'never' WHERE user_id = ?",
+
+ "REPLACE INTO `user_info` (`user_id`, `hobby`, `lebenslauf`, `publi`, `schwerp`, `Home`, `privatnr`, `privatcell`, `privadr`, `score`, `geschlecht`, `mkdate`, `chdate`, `title_front`, `title_rear`, `preferred_language`, `smsforward_copy`, `smsforward_rec`, `email_forward`, `motto`, `lock_rule`) VALUES(?, '', '', '', '', '', '', '', '', 0, 0, 0, 0, '', '', NULL, 1, '', 0, '', '');"
+ ];
+ foreach ($queries as $query) {
+ DBManager::get()->execute($query, [$user_id]);
+ }
+
+ // Clean up orphaned items
+ $queries = [
+ "DELETE FROM personal_notifications WHERE personal_notification_id NOT IN (
+ SELECT personal_notification_id FROM personal_notifications_user
+ )",
+ ];
+ foreach ($queries as $query) {
+ DBManager::get()->exec($query);
+ }
+
+ object_kill_visits($user_id);
+ object_kill_views($user_id);
+
+ // delete picture
+ $avatar = Avatar::getAvatar($user_id);
+ if ($avatar->is_customized()) {
+ $avatar->reset();
+ $msg .= 'info§' . _('Bild gelöscht.') . '§';
+ }
+
+ //delete connected users
+ if (Config::get()->ELEARNING_INTERFACE_ENABLE) {
+ if (ELearningUtils::initElearningInterfaces()) {
+ foreach ($GLOBALS['connected_cms'] as $cms){
+ if ($cms->auth_necessary && $cms->user instanceOf ConnectedUser) {
+ $user_auto_create = $cms->USER_AUTO_CREATE;
+ $cms->USER_AUTO_CREATE = false;
+ $userclass = mb_strtolower(get_class($cms->user));
+ $connected_user = new $userclass($cms->cms_type, $user_id);
+ if ($connected_user->deleteUser() && $connected_user->is_connected) {
+ $msg .= 'info§' . sprintf(_('Der verknüpfte Nutzer %s wurde im System %s gelöscht.'), $connected_user->login, $connected_user->cms_type) . '§';
+ }
+ $cms->USER_AUTO_CREATE = $user_auto_create;
+ }
+ }
+ }
+ }
+
+ return $msg;
+ }
+
+ private function adminOK()
+ {
+ static $ok = null;
+
+ if ($ok === null) {
+ $query = "SELECT COUNT(a.Institut_id) = COUNT(c.inst_perms)
+ FROM user_inst AS a
+ LEFT JOIN Institute b ON (a.Institut_id = b.Institut_id AND b.Institut_id != b.fakultaets_id)
+ LEFT JOIN user_inst AS c ON (b.fakultaets_id = c.Institut_id AND c.user_id = ?
+ AND c.inst_perms = 'admin')
+ WHERE a.user_id = ? AND a.inst_perms = 'admin'";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([
+ $GLOBALS['auth']->auth['uid'],
+ $this->user_data['auth_user_md5.user_id'],
+ ]);
+ $ok = $statement->fetchColumn();
+ }
+
+ return $ok;
+ }
+
+ private function re_sort_position_in_seminar_user()
+ {
+ $query = "SELECT Seminar_id, position, status
+ FROM seminar_user
+ WHERE user_id = ? AND status IN ('tutor', 'dozent')";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->user_data['auth_user_md5.user_id']]);
+ while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
+ if ($row['status'] === 'tutor') {
+ CourseMember::resortMembership($row['Seminar_id'], (int)$row['position']);
+ } else if ($row['status'] === 'dozent') {
+ CourseMember::resortMembership($row['Seminar_id'], (int)$row['position'], 'dozent');
+ }
+ }
+ }
+
+ /**
+ * Change an existing user password
+ *
+ * @param string $password
+ * @return bool change successful?
+ */
+ public function changePassword($password)
+ {
+ $this->user_data['auth_user_md5.password'] = self::getPwdHasher()->HashPassword($password);
+ $this->storeToDatabase();
+
+ $this->msg .= 'msg§' . _('Das Passwort wurde neu gesetzt.') . '§';
+
+ // include language-specific subject and mailbody
+ setTempLanguage($this->user_data['auth_user_md5.user_id']);
+
+ $subject = sprintf(
+ _("[Stud.IP - %s] Passwortänderung"),
+ Config::get()->UNI_NAME_CLEAN
+ );
+
+ $mailbody = sprintf(
+ _("Dies ist eine Informationsmail des Stud.IP-Systems\n"
+ ."(Studienbegleitender Internetsupport von Präsenzlehre)\n- %s -\n\n"
+ ."Ihr Passwort wurde soeben von Ihnen oder einem/einer Administrator/in geändert.\n"
+ ),
+ Config::get()->UNI_NAME_CLEAN
+ );
+
+ // send mail
+ StudipMail::sendMessage($this->user_data['auth_user_md5.Email'], $subject, $mailbody);
+
+ restoreLanguage();
+
+ StudipLog::log('USER_NEWPWD', $this->user_data['auth_user_md5.user_id']);
+
+ return true;
+ }
+}