needlogin = true; $me = new User(); $me->loadMe(true); $this->me = $me; $this->layout()->set("me", $me); if (!$me->isAdmin() && ($this->action != "" || $request != null)) $this->redirect("Dashboard"); if ($_SERVER['REQUEST_METHOD'] === 'POST') $this->postData = json_decode(file_get_contents('php://input'), true); } protected function indexAction($request) { if (!$this->isAdmin()) { throw new Exception("Forbidden", 403); } Helper::renderVue($this, "User", "Benutzer", [ "IS_ADMIN" => $this->me->isAdmin(), "USERS" => array_map(fn($user) => [ "username" => $user->username, "name" => $user->name, "address" => ($user->address->company) ? $user->address->company : $user->address->getFullName(), "email" => $user->email, "mobile" => $user->mobile, "twofactor" => [1 => 'Mail', 2 => 'SMS'][$user->twofactor] ?? 'N/A', "isAdmin" => $user->isAdmin(), "isTechnician" => $user->is("Technician"), "isActive" => $user->active, "id" => $user->id ], UserModel::getAll()), "ADD_URL" => self::getUrl("User", "Form"), "EDIT_URL" => self::getUrl("User", "Form"), "IMPERSONATE_URL" => self::getUrl("User", "impersonate"), "SEND_LOGIN_EMAIL_URL" => self::getUrl("User", "sendLoginEmail"), ]); } protected function formAction() { if (!$this->isAdmin()) $this->redirect("Dashboard"); $id = $this->request->id; $user = ($id && is_numeric($id) && $id > 0) ? new User($id) : new User(); if ($user->id) { $pageTitle = "Benutzer bearbeiten: " . $user->name; } else { $user->id = null; $user->permissions = (object)['data' => []]; $pageTitle = "Benutzer erstellen"; } if ($user->id && !$user->id) throw new Exception("User not found.", 404); $flags = $user->id ? $this->getFlags($user) : []; $userData = array_merge( $user->toArray(), $flags, ['permissions' => (array)$user->permissions->data] ); $lookups = [ "addresses" => array_map(fn($addr) => ['value' => $addr->id, 'text' => $addr->company ?: $addr->getFullName()], AddressModel::getAll()), "networks" => array_map(fn($net) => ['value' => $net->id, 'text' => $net->name], NetworkModel::getAll()), "consentProjects" => array_map(fn($proj) => ['value' => $proj->id, 'text' => $proj->name], ConstructionConsentProject::getAll()), "permissionTemplates" => UserPermissionTemplateModel::getAll([], null, 0, ['key' => 'name', 'order' => 'asc']), "users" => array_map(fn($u) => ['value' => $u->id, 'text' => $u->name], UserModel::search(['active' => 1])), ]; Helper::renderVue($this, "UserEdit", $pageTitle, [ "USER_DATA" => $userData, "LOOKUPS" => $lookups, "PERMISSIONS_CONFIG" => TT_USER_PERMISSION, "SAVE_URL" => self::getUrl("User", "save"), "API_KEY_URL" => self::getUrl("User", "generateApikey"), ]); } private function getFlags(User $user): array { $flags = [ 'preorder_networks' => $user->getFlag("preorder_networks")->value(), 'constructionconsent_projects' => $user->getFlag("constructionConsent_projects")->value(), 'employee_number' => $user->getFlag("employee_number")->value(), 'project_api_key' => $user->getFlag("project_api_key")->value(), 'vodia_identity_domain' => $user->getFlag("vodia_identity_domain")->value(), 'vodia_identity_username' => $user->getFlag("vodia_identity_username")->value(), 'vodia_identity_default' => $user->getFlag("vodia_identity_default")->value(), ]; $jsonKeys = ['preorder_networks', 'constructionconsent_projects']; foreach ($flags as $key => &$value) if (in_array($key, $jsonKeys) && $value) $value = json_decode($value, true); return $flags; } protected function getUserDataForTemplateAction() { $id = $this->request->id; if (!$id) self::sendError("User ID is required."); $user = new User($id); if (!$user->id) self::sendError("User not found."); $preorderNetworks = $user->getFlag("preorder_networks")->value(); $consentProjects = $user->getFlag("constructionConsent_projects")->value(); self::returnJson([ 'permissions' => (array)$user->permissions->data, 'preorder_networks' => $preorderNetworks ? json_decode($preorderNetworks, true) : [], 'constructionconsent_projects' => $consentProjects ? json_decode($consentProjects, true) : [], 'vodia_identity_domain' => $user->getFlag("vodia_identity_domain")->value(), 'vodia_identity_default' => $user->getFlag("vodia_identity_default")->value(), ]); } protected function managePermissionTemplatesAction() { Helper::renderVue($this, "UserPermissionTemplate", "Berechtigungsvorlagen", ["PERMISSIONS_CONFIG" => TT_USER_PERMISSION]); } protected function getPermissionTemplatesAction() { self::returnJson(array_map( function ($perm) { $perm = (array)$perm; $perm['permissions'] = json_decode($perm['permissions'], true) ?: []; return $perm; }, UserPermissionTemplateModel::getAll([], null, 0, ['key' => 'name', 'order' => 'asc']) )); } protected function savePermissionTemplateAction() { if (empty($this->postData['name'])) self::sendError("Template name is required."); $data = [ 'name' => $this->postData['name'], 'permissions' => json_encode($this->postData['permissions'] ?? []), ]; if (empty($this->postData['id'])) { $data += ['createBy' => $this->user->id, 'create' => time()]; $id = UserPermissionTemplateModel::create($data); self::returnJson(['success' => true, 'message' => 'Vorlage erstellt.', 'id' => $id]); } $template = UserPermissionTemplateModel::get($this->postData['id']); $data += [ 'id' => $this->postData['id'], 'create' => $template->create, 'createBy' => $template->createBy, ]; UserPermissionTemplateModel::update($data); self::returnJson(['success' => true, 'message' => 'Vorlage gespeichert.']); } protected function deletePermissionTemplateAction() { $post = json_decode(file_get_contents('php://input'), true); if (empty($post['id'])) self::sendError("Template ID is required."); UserPermissionTemplateModel::delete($post['id']); self::returnJson(['success' => true, 'message' => 'Vorlage gelöscht.']); } protected function generateApikeyAction($request) { if (!$this->isAdmin()) $this->redirect("Dashboard"); $id = $request['id']; if (!is_numeric($id) || $id < 1) { $this->layout()->setFlash("User nicht gefunden.", "error"); $this->redirect("User"); } $user = new User($id); if (!$user->id) { $this->layout()->setFlash("User nicht gefunden.", "error"); $this->redirect("User"); } $user->apikey = $user->createApiKey(); $user->save(); self::returnJson(['success' => true]); } protected function saveAction() { $r = $this->request; $id = $r->id; if (!$this->isAdmin()) { $id = $this->me->id; $request['username'] = $this->me->username; unset($r->address_id); } if (!$id && !$r->username) self::redirect('User'); $user = new User($id); if ($this->isAdmin() && !$r->id) { $tu = new User(); $tu->loadByUsername($r->username); if ($tu->id) { $this->layout()->setFlash("Benutzer mit diesem Benutzername bereits vorhanden!", "error"); $this->redirect("User"); } } $user->active = $r->active === "true" ? 1 : 0; if (!$user->permissions) $user->permissions = new WorkerPermission(); if ($r->username) $user->username = $r->username; if ($r->name) $user->name = $r->name; if ($r->email) $user->email = $r->email; if ($r->mobile) $user->mobile = $r->mobile; else $user->mobile = NULL; if ($this->isAdmin()) { if ($r->address_id) { $user->address_id = intval($r->address_id); $address = new Address($user->address_id); if (!$address->id) { throw new Exception("Unbekannte Firma/Person"); } } else { $user->address_id = null; } $user->twofactorrequired = ($r->twofactorrequired == "true") ? 1 : 0; } if ($r->password) { if ($r->password === $r->password2) { $user->password = mfLoginController::generatePasswordHash($r->password); } else { $this->layout()->setFlash("Passwörter stimmen nicht überein!", "error"); } } $user->edit_by = $this->me->id; if (!$id) { $user->create_by = $this->me->id; } $id = $user->save(); if ($this->isAdmin()) { $user->permissions->admin = ($r->admin == "true" || $user->id == 1) ? "true" : "false"; $user->permissions->employee = ($r->employee == "true") ? "true" : "false"; $user->permissions->technician = ($r->technician == "true") ? "true" : "false"; $user->permissions->preorderfront = ($r->preorderfront == "true") ? "true" : "false"; $user->permissions->preorderlogistics = ($r->preorderlogistics == "true") ? "true" : "false"; $user->permissions->preorderaddressreporting = ($r->preorderaddressreporting == "true") ? "true" : "false"; $user->permissions->preorderreadonly = ($r->preorderreadonly == "true") ? "true" : "false"; $canPermissions = [ 'Building', 'Pipework', 'Linework', 'Patching', 'Filestore', 'Cpeprovisioning', 'Cpeshipping', 'DevicesDisabled', 'Voipnumbering', 'Preorder', 'Preorderpricing', 'PreorderpricingReadonly', 'Preorderbilling', 'PreorderbillingReadonly', 'Order', 'Billing', 'Fibu', 'Statistics', 'WarehouseAdmin', 'WarehouseEShop', 'WarehouseUser', 'ADBExtended', 'AssetAdmin', 'RMLAdmin', 'RMLCompany' ]; foreach ($canPermissions as $perm) { $user->permissions->{"can" . $perm} = "false"; } if ($r->get("can") && is_array($r->can)) { foreach ($r->can as $key => $can) { if ($can) { $user->permissions->{"can" . $key} = "true"; } } } $user->permissions->save(); function handleWorkerFlag(User $user, $request, string $flagName, $requestKey, $permissionCheck = null) { $flag = new WorkerFlag($user->id, $flagName); $value = $request->$requestKey; if ($value && (!$permissionCheck || $user->permissions->$permissionCheck === "true")) { $flag->value(is_array($value) ? json_encode($value) : $value); $flag->save(); return true; } $flag->delete(); return false; } $preorderNetworks = handleWorkerFlag($user, $r, "preorder_networks", "preorder_networks"); if ($preorderNetworks) { $user->permissions->canPreorder = "true"; $user->permissions->save(); } handleWorkerFlag($user, $r, "constructionConsent_projects", "constructionconsent_projects"); handleWorkerFlag($user, $r, "employee_number", "employee_number", "employee"); handleWorkerFlag($user, $r, "project_api_key", "project_api_key"); handleWorkerFlag($user, $r, "vodia_identity_domain", "vodia_identity_domain"); handleWorkerFlag($user, $r, "vodia_identity_username", "vodia_identity_username"); handleWorkerFlag($user, $r, "vodia_identity_default", "vodia_identity_default"); } $this->layout()->setFlash("Benutzer gespeichert.", "success"); self::redirect('User'); } protected function pwchangeAction($request) { $me = new User(); $me->loadMe(); $pw1 = $request['password']; $pw2 = $request['password2']; if (!$pw1 == $pw2) { throw new Exception("Passwords don't match! Password change aborted."); } if (strlen($pw1) < 8) { throw new Exception("Passwords must be 8 characters minimum!"); } if ($pw1 == "12345678" || $pw1 == "123456789" || $pw1 == "password" || $pw1 == "passwort") { throw new Exception("Be a little more creative with your password please..."); } $me->password = mfLoginController::generatePasswordHash($pw1); $me->save(); $this->redirect("Dashboard"); } public function getUsers() { $users = array(); $res = $this->db()->select(MFUSERTABLE, '*', '1=1 ORDER BY username'); if ($this->db()->num_rows($res)) { while ($data = $this->db()->fetch_object($res)) { $users[$data->id] = new User($data); } } return $users; } private function isAdmin() { $me = new User(); $this->layout->set("me", $me); $me->loadMe(); return $me->isAdmin(); } protected function apiAction() { $do = $this->request->do; $data = []; $me = new User(); $me->loadMe(); $return = false; switch($do) { case "sse": $me->is(["Admin"]) && $return = $this->startSuperexpertApi(); break; case "ese": $me->is(["Admin"]) && $return = $this->extendSuperexpertApi(); break; case "endse": $me->is(["Admin"]) && $return = $this->endSuperexpertApi(); break; case "getVodiaIdentity": $return = $this->getVodiaIdentityApi(); break; case "setVodiaIdentity": $return = $this->setVodiaIdentityApi(); break; case "getVodiaCall": $return = $this->getVodiaCallApi(); break; default: $return = false; } if(!is_array($return) || !count($return)) { $data = ["status" => "error"]; $this->returnJson($data); } $data['status'] = "OK"; $data['result'] = $return; $this->returnJson($data); } private function getVodiaIdentityApi() { if(!ENABLE_VODIA_IDENTITY_SWITCHER) { return ["enabled" => false]; } $me = new User(); $me->loadMe(); $vodia = new Vodia_Api(VODIA_API_URL, VODIA_API_ADMIN_USER, VODIA_API_ADMIN_PASS); $domain = $me->getFlag("vodia_identity_domain")->value(); $username = $me->getFlag("vodia_identity_username")->value(); $default = $me->getFlag("vodia_identity_default")->value(); if(!$domain || !$username || !$default) { return ["enabled" => false]; } $current = $vodia->getUsersetting($domain, $username, "ani"); if($current) { if(str_replace(" ", "", $current) == str_replace(" ", "", $default)) { $current = $default; } } else { $current = $default; } return [ "enabled" => true, "domain" => $domain, "username" => $username, "default" => $default, "default_number" => str_replace(" ", "", $default), "current" => $current, "identities" => VODIA_OUTBOUND_IDENTITIES, ]; } private function setVodiaIdentityApi() { if(!ENABLE_VODIA_IDENTITY_SWITCHER) { return ["enabled" => false]; } $number = $this->request->number; if(!$number) { return false; } // expects number to start with + if(!substr($number, 0, 1) == "+") { return false; } $me = new User(); $me->loadMe(); $domain = $me->getFlag("vodia_identity_domain")->value(); $username = $me->getFlag("vodia_identity_username")->value(); if(!$domain || !$username) { return ["enabled" => false]; } $vodia = new Vodia_Api(VODIA_API_URL, VODIA_API_ADMIN_USER, VODIA_API_ADMIN_PASS); if(!$vodia->setUsersettings($domain, $username, ["ani" => $number])) { return false; } return ["enabled" => true, "number" => $number]; } private function getVodiaCallApi() { if(!ENABLE_VODIA_IDENTITY_SWITCHER) { return ["enabled" => false]; } $domain = $this->me->getFlag("vodia_identity_domain")->value(); $username = $this->me->getFlag("vodia_identity_username")->value(); if(!$domain || !$username) { return ["enabled" => false]; } $vodia = new Vodia_Api(VODIA_API_URL, VODIA_API_ADMIN_USER, VODIA_API_ADMIN_PASS); $calls = $vodia->getActiveCalls($domain, $username); if(!$calls) { return ["enabled" => true, "calls" => []]; } $from = null; foreach($calls as $call) { if(isset($call['from'])) { $from = $call['from']; if(preg_match('/]+)>/', $from, $m)) { $from = $m[1]; } else { $from = str_replace('"', '', $from); } break; // only return the first call's from number } } return ["enabled" => true, "number" => $from]; } private function startSuperexpertApi() { $me = new User(); $me->loadMe(); if($me->superexpertEnabled() ) { // superexpert mode started already return false; } $me->superexpertStart(1800); return ["valid_to" => $me->getFlag("superexpert_lock_date")->value()]; } private function extendSuperexpertApi() { $me = new User(); $me->loadMe(); if(!$me->superexpertEnabled() ) { // superexpert mode must be started already $this->log->debug("se not started"); return false; } $this->log->debug("ese"); $me->superexpertExtend(1800); return ["valid_to" => $me->getFlag("superexpert_lock_date")->value()]; } private function endSuperexpertApi() { $me = new User(); $me->loadMe(); if($me->superexpertEnabled() ) { $me->superexpertStop(); } return ["valid_to" => null]; } protected function getByIdAction() { $id = $this->request->id; $user = new User($id); $this->returnJson($user->toArray()); } protected function impersonateAction() { if(!$this->me->isAdmin() || $this->me->address_id != 1) { header("HTTP/1.1 403 Forbidden"); exit; } if($this->request->unimpersonate) { unset($_SESSION[MFAPPNAME.'_impersonate']); $this->redirect("User"); } if(!$this->request->username || strlen($this->request->username) < 3) { header("HTTP/1.1 500 Internal Server Error"); exit; } $_SESSION[MFAPPNAME.'_impersonate'] = $this->request->username; $this->redirect("Dashboard"); } protected function sendLoginEmailAction() { $id = $this->request->id; if (!$id || !is_numeric($id)) { self::sendError("Benutzer-ID fehlt oder ist ungültig."); } $user = new User($id); if (!$user->id || !$user->email) { self::sendError("Benutzer nicht gefunden oder keine E-Mail-Adresse hinterlegt."); } $userFullName = htmlspecialchars($user->name); $userUsername = htmlspecialchars($user->username); $firstName = htmlspecialchars(explode(' ', $user->name)[0]); $subject = "Ihr Zugang zu TheTOOL by XINON, {$firstName}!"; $logoToolPath = LIBDIR . '/../public/assets/images/the-tool-logo.png'; $logoXinonPath = LIBDIR . '/../public/assets/images/xinon-full.png'; $logoToolTag = file_exists($logoToolPath) ? 'TheTOOL Logo' : ''; $logoXinonTag = file_exists($logoXinonPath) ? 'XINON Logo' : ''; $currentYear = date("Y"); $xinonBlue = '#005384'; $loginLink = 'https://thetool.xinon.at'; $passwordResetLink = 'https://thetool.xinon.at/UserPasswordReset/forgotPassword'; $html = << {$subject}
{$logoToolTag} {$logoXinonTag}

Willkommen an Bord, {$firstName}!

Wir freuen uns, Sie im Team zu haben. Ihr Zugang zu einer smarteren Arbeitsweise ist freigeschaltet.

Ihr persönlicher Benutzername lautet:

{$userUsername}

Um zu starten, legen Sie bitte Ihr persönliches Passwort fest.

Passwort erstmalig setzen

Oder besitzen Sie bereits ein Passwort?

Direkt zur Login-Seite

© {$currentYear} XINON GmbH | Impressum

HTML; $altBody = "Willkommen an Bord, {$firstName}!\n\n" . "Wir freuen uns, Sie im Team zu haben. Ihr Zugang zu einer smarteren Arbeitsweise ist freigeschaltet.\n\n" . "Ihr persönlicher Benutzername lautet: {$userUsername}\n\n" . "Um zu starten, legen Sie bitte Ihr persönliches Passwort fest. Besuchen Sie dazu folgende Seite:\n" . "{$passwordResetLink}\n\n" . "Oder besitzen Sie bereits ein Passwort? Hier geht es direkt zum Login:\n" . "{$loginLink}\n\n" . "© {$currentYear} XINON GmbH | Impressum: https://xinon.at/impressum/"; $mail = new PHPMailer(true); try { $mail->isSMTP(); $mail->Host = TT_PIPEWORK_SMTP_HOST; $mail->SMTPAuth = true; $mail->Username = TT_PIPEWORK_SMTP_USER; $mail->Password = TT_PIPEWORK_SMTP_PASS; $mail->CharSet = PHPMailer::CHARSET_UTF8; $mail->Encoding = 'base64'; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; $mail->Port = 587; if (file_exists($logoToolPath)) $mail->addEmbeddedImage($logoToolPath, 'logo_thetool'); if (file_exists($logoXinonPath)) $mail->addEmbeddedImage($logoXinonPath, 'logo_xinon'); $mail->setFrom('thetool@xinon.at', 'TheTOOL by XINON'); $mail->addReplyTo('office@xinon.at', 'XINON Office'); $mail->addAddress($user->email, $user->name); $mail->isHTML(true); $mail->Subject = $subject; $mail->Body = $html; $mail->AltBody = $altBody; $mail->send(); self::returnJson(['success' => true, 'message' => 'Willkommens-E-Mail wurde erfolgreich an ' . htmlspecialchars($user->email) . ' gesendet.']); } catch (Exception $e) { error_log("Mailer Error in sendLoginEmailAction for user ID {$id}: " . $mail->ErrorInfo); self::sendError("E-Mail konnte nicht gesendet werden. Bitte kontaktieren Sie den Support."); } } }