diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d6d5d3c..b10ce88 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -34,5 +34,7 @@ If applicable, add screenshots to help explain your problem. - Browser [e.g. stock browser, safari] - Version [e.g. 22] +**TimeTrack Version**: e.g. v7.9 + **Additional context** Add any other context about the problem here. diff --git a/CHANGELOG.md b/CHANGELOG.md index bca98a0..0ac2d86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,45 @@ # CHANGELOG +## v7.12 + +* Added a simple Favicon +* TimeTrack and API version are now displayed in the settings menu +* Added Events which can be listened to by plugins (see `api/v1/class/events/README.md`) / Developers can now create their own events +* Mails can now be disabled by setting the `smtp` setting to `false` within the `smtp` section of the `app.json` +* Fixed `composer.json` contents for LDAPTools plugin again +* Removed unused `Hooks` plugin class file + +## v7.11 + +* Added plugin to allow NFC PC/SC login (see `api/v1/class/plugins/plugins/nfclogin/README.md`) +* Added db migrations with phinx to update the database schema +* improved overall security with function node system +* API can now handle public endpoints + +## v7.10.2 + +* Added native function to `Benutzer` class to update user proprties which not lets the `userdetail` plugin actually update user properties +* Fixed bug + +## v7.10.1.1 + +* Hotfix preventing to add a worktime in normal mode + +## v7.10.1 + +* Added some CSS to certain elements which were missing it + +## v7.10 + +* Reflected changes from v7.9 release into Mails.md +* You now get redirected when calendar ID is not found +* Fixed being unable to access the "Forgot password" page +* Fixed typo in userdetail plugin preventing save +* Remove usercount plugin entirely +* Removed webedit for app.json +* A warning is now displayed when an admin changes user information within the userdetail plugin +* Fixed an bug causing userdetail plugin to crash when the selected user could not be found + ## v7.9 * Fixed being unable to access "userdetail" plugin diff --git a/README.md b/README.md index 5cb11cd..5fa5c7b 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ Additional functionality can be unlocked with TimeTrack Oval ### Requirements -- at least PHP 8.2 (`curl|gd|gmp|intl|mbstring|mysqli|openssl|pgsql|xsl|gettext|dom|ldap`) -- composer (to install dependencies; phpmailer: for sending emails via smtp, parsedown: markdown parser for the `CHANGELOG.md`, simple-router: does the API routing, yaml: for reading plugin yaml files, ldaptools: for LDAP authentication, dompdf: for PDF generation) +- at least PHP 8.2 (`curl|gd|gmp|intl|mbstring|mysqli|openssl|xsl|gettext|dom|ldap`) +- composer (to install dependencies; phpmailer: for sending emails via smtp, parsedown: markdown parser for the `CHANGELOG.md`, simple-router: does the API routing, yaml: for reading plugin yaml files, ldaptools: for LDAP authentication, dompdf: for PDF generation, phinx: for database migrations) - Apache2.4 with enabled rewrite mod (optional) This software has been tested on Debian 11/12, XAMPP, PHP internal server (e.g. `php -S 0.0.0.0:80`). @@ -36,11 +36,10 @@ Simply install the software by following these steps: - Git clone timetrack to e.g. `/var/www`: `cd /var/www && git clone https://github.com/Ente/timetrack.git && cd timetrack` - Install requirements for composer `composer install` - Create a new database, e.g. with the name `ab` and create a dedicated user, login (`mysql -u root -p`) then e.g. `timetool`: `CREATE DATABASE ab;` and `CREATE USER 'timetool'@'localhost' IDENTIFIED BY 'yourpassword';` and `GRANT ALL PRIVILEGES ON ab.* TO 'timetool'@'localhost';` don't forget to `FLUSH PRIVILEGES;`! -- Import the `setup/sql.sql` into your database, e.g. `mysql -u timetool -p ab < /full/path/to/sql.sql` -- To create your first user, run the `setup/usercreate.php` file, e.g. `php ./usercreate.php admin yourpassword email@admin.com` - `usercreate.php [USERNAME] [PASSWORD] [EMAIL]` -- Run the statement printed by the `usercreate.php` inside your database (`mysql -u root -p` and `use ab;` then the statement). - Configure `app.json` (see below - required changes: `base_url`, `db_user`, `db_password`, `smtp` section and any other if your installation is different) then `mv api/v1/inc/app.json.sample app.json && cd /var/www/timetrack` +- Run DB migrations: `vendor/bin/phinx migrate` - Start webserver e.g. `service apache2 stop && php -S 0.0.0.0:80` or using apache2 (then you have to configure the `sites-available` conf yourself) +- You can then access TimeTrack in your browser at `http://localhost`, default login is `admin` with password `admin`. Create yourself a new admin account, login and delete the default account afterwards. ### Configure app.json @@ -53,9 +52,11 @@ In step 2, you need to configure the `app.json.sample` within the `api/v1/inc` f - `auto_update`: (not yet implemented) - `db_*`: Set the connection details for your mysql instance - `app`: If set to true, users will be able to use the TimeTrack mobile application +- `timezone`: Set the timezone of your application, e.g. `Europe/Berlin` or `America/New_York` (default: `UTC`) #### **SMTP section** +- `smtp`: Set to `true` to enable SMTP functionality (default: false) - `host`: FQDN of your mail server - `username`: Username for the mailbox you want to send emails from - `password`: Self explaining @@ -83,10 +84,16 @@ LDAP authentication works with OpenLDAP and Active Directory. - `ldap_ip`: IP address of your LDAP server (e.g. `1.1.1.1`) - `ldap_domain`: The domain your LDAP server controls (e.g. `example.local`) - `ldap_basedn`: Base DN for your domain (e.g. `dc=example,dc=local`) -- `ldap_group`: Group membership required by LDAP users to be able to authenticate +- `ldap_group`: Group membership required by LDAP users to be able to authenticate (e.g. `Domain Users`, (new group) `TimeTrack Users`) - `saf`: Specify if you only have one LDAP server (true) or another one as fallback (false) - `saf_*`: If `saf` is set to `false`, please specify the corresponding values to the `saf_*` configuration -- `create_user`: If set to `true` it creates an user account automatically if the desired account is authenticated and within specified group. If set to `false` login simply fails, even if authenticated +- `create_user`: If set to `true` it creates an user account automatically if the desired account is authenticated and within specified group. If set to `false` login simply fails, even if authenticated. + +#### **Export** + +##### **PDF** + +- `css`: Full path to the CSS file used for the PDF export (default: `api/v1/class/exports/modules/PDFExportModule/css/index.css`) - **optional value** If done correctly, you should now be able to access the application via http://BASE_URL/ - redirects to http://BASE_URL/suite/ @@ -125,7 +132,6 @@ Already existing local accounts will get their authentication overwritten if an In order to create accounts automatically if `create_user` is `true` make sure to set the user's email address! Otherwise login fails. If above mentioned setting is set to `false` you have to create a user on your own locally and then let the user login with their LDAP credentials. The credentials you have entered will become usable if you disable LDAP or rename the account on your LDAP server. -Please run `run-patch.sh` within the `setup` folder to get LDAP working with php >8.0 ## Export @@ -182,9 +188,4 @@ If downloaded any other way, just make sure to copy and paste the new files into ### Database -You can update the database by downloading the `setup/upgrade.php` file into your local `setup` directory. -From here on just edit the `$missingUpdate` variable to the desired version as specified. - -Please be aware that you are not able to skip an database update. You have to update one by one, e.g. from 1 -> 2, 2 -> 3, ... - - +You can update the database by using `vendor/bin/phinx migrate` to migrate to latest release or `vendor/bin/phinx rollback` to rollback. \ No newline at end of file diff --git a/VERSION b/VERSION index c8357ab..4f294e2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.9 \ No newline at end of file +7.12 \ No newline at end of file diff --git a/api/v1/class/arbeitszeit.inc.php b/api/v1/class/arbeitszeit.inc.php index e70b7f9..0f86ec5 100644 --- a/api/v1/class/arbeitszeit.inc.php +++ b/api/v1/class/arbeitszeit.inc.php @@ -12,6 +12,17 @@ use Arbeitszeit\Sickness; use Arbeitszeit\ExportModule; use Arbeitszeit\Mails; + use Arbeitszeit\Nodes; + use Arbeitszeit\Events\EventDispatcherService; + use Arbeitszeit\Events\EasymodeWorktimeAddedEvent; // "EasymodeWorktimeSTARTED" Event, actually. + use Arbeitszeit\Events\EasymodeWorktimeEndedEvent; + use Arbeitszeit\Events\EasymodeWorktimePauseEndEvent; + use Arbeitszeit\Events\EasymodeWorktimePauseStartEvent; + use Arbeitszeit\Events\WorktimeAddedEvent; + use Arbeitszeit\Events\WorktimeDeletedEvent; + use Arbeitszeit\Events\WorktimeMarkedForReviewEvent; + use Arbeitszeit\Events\WorktimeUnlockedFromReviewEvent; + use Arbeitszeit\Events\FixEasymodeWorktimeEvent; /** * Beinhaltet wesentliche Inhalte, wie Einstellungen, Arbeitszeiten erstellen, etc. * @@ -32,11 +43,20 @@ class Arbeitszeit private $vacation; private $exportModule; private $mails; + private $nodes; public function __construct() { $this->db = new DB(); $this->init_lang() ?? null; + + if(isset($this->get_app_ini()["general"]["timezone"])){ + try { + date_default_timezone_set($this->get_app_ini()["general"]["timezone"]); + } catch (\Exception $e) { + Exceptions::error_rep("Error setting timezone: " . $e->getMessage()); + } + } } public function __destruct() @@ -65,6 +85,9 @@ public function init_lang() */ public function delete_worktime($id) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "delete_worktime")){ + return false; + } $data = $this->db->sendQuery("DELETE FROM arbeitszeiten WHERE id = ?")->execute([$id]); if ($data == false) { Exceptions::error_rep("An error occured while deleting an worktime entry. See previous message for more information."); @@ -75,6 +98,7 @@ public function delete_worktime($id) ] ]; } else { + EventDispatcherService::get()->dispatch(new WorktimeDeletedEvent($_SESSION["username"], (int)$id), WorktimeDeletedEvent::NAME); Exceptions::error_rep("Worktime entry with ID '{$id}' deleted successfully."); return 1; } @@ -83,6 +107,10 @@ public function delete_worktime($id) public static function add_easymode_worktime($username) { + $nodes = new Nodes; + if(!$nodes->checkNode("arbeitszeit.inc", "add_easymode_worktime")) { + return false; + } Exceptions::error_rep("Creating easymode worktime entry for user '{$username}'..."); $date = date("Y-m-d"); $time = date("H:i"); @@ -101,6 +129,7 @@ public static function add_easymode_worktime($username) Exceptions::error_rep("An error occured while creating easymode worktime entry. See previous message for more information"); return false; } else { + EventDispatcherService::get()->dispatch(new EasymodeWorktimeAddedEvent($username), EasymodeWorktimeAddedEvent::NAME); Exceptions::error_rep("Easymode worktime entry created for user '{$username}'"); return true; } @@ -109,6 +138,10 @@ public static function add_easymode_worktime($username) public static function end_easymode_worktime($username, $id) { + $nodes = new Nodes; + if(!$nodes->checkNode("arbeitszeit.inc", "end_easymode_worktime")) { + return false; + } Exceptions::error_rep("Ending easymode worktime for user '{$username}'..."); $time = date("H:i"); $conn = new DB; @@ -125,6 +158,7 @@ public static function end_easymode_worktime($username, $id) Exceptions::error_rep("An error occured while ending easymode worktime. See previous message for more information."); return false; } else { + EventDispatcherService::get()->dispatch(new EasymodeWorktimeEndedEvent($username, (int)$id), EasymodeWorktimeEndedEvent::NAME); Exceptions::error_rep("Easymode worktime ended for user '{$username}'"); return true; } @@ -133,6 +167,9 @@ public static function end_easymode_worktime($username, $id) public function start_easymode_pause_worktime($username, $id) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "start_easymode_pause_worktime")) { + return false; + } Exceptions::error_rep("Starting easymode pause for user '{$username}'..."); $time = date("H:i"); $user = new Benutzer; @@ -148,6 +185,7 @@ public function start_easymode_pause_worktime($username, $id) Exceptions::error_rep("An error occured while starting user pause for worktime with ID '{$id}' for user '{$username}'. See previous message for more information."); return false; } else { + EventDispatcherService::get()->dispatch(new EasymodeWorktimePauseStartEvent($username, (int)$id), EasymodeWorktimePauseStartEvent::NAME); Exceptions::error_rep("Easymode pause started for user '{$username}'"); return true; } @@ -155,6 +193,9 @@ public function start_easymode_pause_worktime($username, $id) } public function end_easymode_pause_worktime($username, $id) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "end_easymode_pause_worktime")) { + return false; + } Exceptions::error_rep("Ending easymode pause for user '{$username}'..."); $time = date("H:i"); $user = new Benutzer; @@ -170,6 +211,7 @@ public function end_easymode_pause_worktime($username, $id) Exceptions::error_rep("An error occured while ending user pause for worktime with ID '{$id}' for user '{$username}'. See previous message for more information."); return false; } else { + EventDispatcherService::get()->dispatch(new EasymodeWorktimePauseEndEvent($username, (int)$id), EasymodeWorktimePauseEndEvent::NAME); Exceptions::error_rep("Easymode pause ended for user '{$username}'"); return true; } @@ -178,6 +220,9 @@ public function end_easymode_pause_worktime($username, $id) public function toggle_easymode($username) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "toggle_easymode")) { + return false; + } Exceptions::error_rep("Toggling easymode for user '{$username}'..."); $sql = "SELECT * FROM `users` WHERE username = ?;"; $res = $this->db->sendQuery($sql); @@ -210,6 +255,9 @@ public function toggle_easymode($username) public function get_easymode_status($username, $mode = 0) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_easymode_status")) { + return false; + } Exceptions::error_rep("Getting easymode status for user '{$username}'..."); $sql = "SELECT * FROM `users` WHERE username = ?;"; $res = $this->db->sendQuery($sql); @@ -246,6 +294,10 @@ public function get_easymode_status($username, $mode = 0) */ public static function check_easymode_worktime_finished($username) { + $nodes = new Nodes; + if(!$nodes->checkNode("arbeitszeit.inc", "check_easymode_worktime_finished")) { + return false; + } Exceptions::error_rep("Checking easymode worktime for user '{$username}'..."); $db = new DB; $sql = "SELECT * FROM `arbeitszeiten` WHERE active = '1' AND username = ?;"; @@ -280,6 +332,9 @@ public static function check_easymode_worktime_finished($username) */ public function add_worktime($start, $end, $location, $date, $username, $type, $pause = null, $meta = null) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "add_worktime")) { + return false; + } $user = new Benutzer; $usr = $user->get_user($username); if ($date > date("y-m-d") /*|| $date < date("y-m-d")*/) { @@ -298,6 +353,7 @@ public function add_worktime($start, $end, $location, $date, $username, $type, $ Exceptions::error_rep("An error occured while creating an worktime entry. See previous message for more information."); return false; } else { + EventDispatcherService::get()->dispatch(new WorktimeAddedEvent($username, ["start" => $start, "end" => $end]), WorktimeAddedEvent::NAME); Exceptions::error_rep("Worktime entry for user '{$username}' created successfully."); return true; } @@ -311,8 +367,8 @@ public function add_worktime($start, $end, $location, $date, $username, $type, $ */ public static function get_app_ini() { - $ini_path = $_SERVER["DOCUMENT_ROOT"] . "/api/v1/inc/app.ini"; - $json_path = $_SERVER["DOCUMENT_ROOT"] . "/api/v1/inc/app.json"; + $ini_path = dirname(__DIR__, 3) . "/api/v1/inc/app.ini"; + $json_path = dirname(__DIR__, 3) . "/api/v1/inc/app.json"; Exceptions::error_rep("Loading application configuration..."); @@ -356,6 +412,9 @@ private static function sanitizeOutput($data) public function get_all_worktime() { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_all_worktime")) { + return false; + } Exceptions::error_rep("Getting all worktimes..."); $sql = "SELECT * FROM `arbeitszeiten`;"; $res = $this->db->sendQuery($sql); @@ -373,6 +432,9 @@ public function get_all_worktime() public function get_all_user_worktime($username) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_all_user_worktime")) { + return false; + } Exceptions::error_rep("Getting all worktimes for user '{$username}'..."); $sql = "SELECT * FROM `arbeitszeiten` WHERE username = ?;"; $res = $this->db->sendQuery($sql); @@ -390,6 +452,9 @@ public function get_all_user_worktime($username) public function get_specific_worktime_html(int $month, int $year) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_specific_worktime_html")) { + return false; + } Exceptions::error_rep("Getting worktimes rendered for month '{$month}' and year '{$year}'..."); $base_url = $ini = Arbeitszeit::get_app_ini()["general"]["base_url"]; $sql = "SELECT * FROM `arbeitszeiten` WHERE YEAR(schicht_tag) = ? AND MONTH(schicht_tag) = ? ORDER BY schicht_tag DESC;"; @@ -460,6 +525,9 @@ public function get_specific_worktime_html(int $month, int $year) } public function get_employee_worktime_html($username) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_employee_worktime_html")) { + return false; + } Exceptions::error_rep("Getting worktimes rendered for user '{$username}'..."); $sql = "SELECT * FROM `arbeitszeiten` WHERE username = ? ORDER BY id DESC;"; $res = $this->db->sendQuery($sql); @@ -519,6 +587,9 @@ public function get_employee_worktime_html($username) public function mark_for_review($id) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "mark_for_review")) { + return false; + } Exceptions::error_rep("Marking worktime with ID '{$id}' for review..."); $sql = "UPDATE `arbeitszeiten` SET `review` = '1' WHERE `id` = ?;"; $res = $this->db->sendQuery($sql)->execute([$id]); @@ -526,12 +597,16 @@ public function mark_for_review($id) Exceptions::error_rep("An error occured while marking an worktime as under review, id '{$id}'. See previous message for more information."); return false; } else { + EventDispatcherService::get()->dispatch(new WorktimeMarkedForReviewEvent($_SESSION["username"], (int)$id), WorktimeMarkedForReviewEvent::NAME); return true; } } public function unlock_for_review($id) { + if(!$this->nodes()->checkNode("arbeitszeit.inc", "unlock_for_review")) { + return false; + } Exceptions::error_rep("Unlocking worktime from review with ID '{$id}'..."); $sql = "UPDATE `arbeitszeiten` SET `review` = '0' WHERE `id` = ?;"; $res = $this->db->sendQuery($sql)->execute([$id]); @@ -539,6 +614,7 @@ public function unlock_for_review($id) Exceptions::error_rep("An error occured while unlocking an worktime from review, id '{$id}'. See previous message for more information."); return false; } else { + EventDispatcherService::get()->dispatch(new WorktimeUnlockedFromReviewEvent($_SESSION["username"], (int)$id), WorktimeUnlockedFromReviewEvent::NAME); return true; } } @@ -628,6 +704,9 @@ public static function check_status_code($url) if (strpos($url, "info=error")) { return "
{$loc["error"]}
"; } + if (strpos($url, "info=notification_not_found")) { + return "{$loc["notification_not_found"]}
"; + } } @@ -652,54 +731,6 @@ public function calculate_hours_specific_time($username, $month, $year) } } - /** - * change_settings() - Allows you to change specific settings in the app.ini - * - * @param array $array Contains the array with changing values - * @return bool Returns true o success and false otherwise - */ - public static function change_settings($array) - { - $ini = self::get_app_ini(); - foreach ($array as $key => $value) { - unset($ini["general"][(string) $key]); - $ini["general"][(string) $key] = $value; - - } - Exceptions::error_rep("Changing settings..."); - $file = fopen($_SERVER["DOCUMENT_ROOT"] . "/api/v1/inc/app.ini", "w"); - $cini = self::arr2ini($ini); - if (fwrite($file, $cini)) { - fclose($file); - return true; - } else { - Exceptions::error_rep("An error occured while chaning settings"); - fclose($file); - return false; - } - } - - private static function arr2ini(array $a, array $parent = array()) - { - Exceptions::error_rep("Writing to app.ini..."); - $out = ''; - foreach ($a as $k => $v) { - if (is_array($v)) { - //subsection case - //merge all the sections into one array... - $sec = array_merge((array) $parent, (array) $k); - //add section information to the output - $out .= '[' . join('.', $sec) . ']' . PHP_EOL; - //recursively traverse deeper - $out .= self::arr2ini($v, $sec); - } else { - //plain key->value case - $out .= "$k=\"$v\"" . PHP_EOL; - } - } - return $out; - } - public static function get_worktime_by_id($id) { $conn = new DB; @@ -744,11 +775,30 @@ public static function fix_easymode_worktime($username) $activateStatement = $db->sendQuery($activateQuery); $activateStatement->bindValue(':latestId', $latestId); $activateStatement->execute(); + EventDispatcherService::get()->dispatch(new FixEasymodeWorktimeEvent($username), FixEasymodeWorktimeEvent::NAME); Exceptions::error_rep("[ARBEITSZEIT] Finished fixing attempt for user '{$username}'"); } } + public function blockIfNotAdmin(){ + if(!Benutzer::is_admin(Benutzer::get_user($_SESSION["username"]))){ + header("Location: /"); + } + return false; + } + + public function global_dispatcher(): \Symfony\Component\EventDispatcher\EventDispatcher { + return \Arbeitszeit\Events\EventDispatcherService::get(); + } + + public function getTimeTrackVersion(){ + return file_get_contents(dirname(__DIR__, 3) . "/VERSION"); + } + + public function getToilVersion(){ + return file_get_contents(dirname(__DIR__, 1) . "/toil/VERSION"); + } public function notifications(): Notifications { @@ -826,6 +876,13 @@ public function mails(): Mails $this->mails = new Mails; return $this->mails; } + + public function nodes(): Nodes + { + if (!$this->nodes) + $this->nodes = new Nodes; + return $this->nodes; + } } } diff --git a/api/v1/class/auth/auth.arbeit.inc.php b/api/v1/class/auth/auth.arbeit.inc.php index 7686eb9..e7163c4 100644 --- a/api/v1/class/auth/auth.arbeit.inc.php +++ b/api/v1/class/auth/auth.arbeit.inc.php @@ -4,6 +4,13 @@ use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\SMTP; use PHPMailer\PHPMailer\Exception; + use Arbeitszeit\Events\EventDispatcherService; + use Symfony\Component\EventDispatcher\EventDispatcher; + use Arbeitszeit\Events\LoggedInUserEvent; + use Arbeitszeit\Events\LoggedOutUserEvent; + use Arbeitszeit\Events\ValidatedLoginEvent; + use LdapTools\Event\Event; + class Auth extends Arbeitszeit{ public $db; @@ -13,14 +20,17 @@ public function __construct(){ } public static function login($username, $password, $option){ # "option"-> array [ "remember" => true/false, ... ] + Exceptions::error_rep("Logging in user '$username'..."); $db = new DB; session_start(); $ini = Arbeitszeit::get_app_ini(); $base_url = $ini["general"]["base_url"]; + $username = preg_replace("/\s+/", "", $username); - if($ini["ldap"]["ldap"] == "true" && !isset($option["LOCAL"])){ + if($ini["ldap"]["ldap"] == "true" && !isset($option["LOCAL"]) && !isset($option["nfclogin"])){ if(LDAP::authenticate($username, $password) != true){ + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "failed")); Exceptions::error_rep("Login failed for username '$username' - Could not authenticate user via LDAP. See errors from before to find issue."); die(header("Location: http://{$ini["general"]["base_url"]}/suite/login.php?error=ldapauth")); } else { @@ -28,8 +38,8 @@ public static function login($username, $password, $option){ # "option"-> array $ldap = true; } } - $username = preg_replace("/\s+/", "", $username); if(!isset($username, $password)){ + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username ?? "N/A", "failed")); Exceptions::error_rep("Login failed for username '$username' - no data supplied. Redirecting..."); die(header("Location: http://{$ini["general"]["base_url"]}/suite/login.php?error=nodata")); } else { @@ -38,12 +48,14 @@ public static function login($username, $password, $option){ # "option"-> array $res->execute([$username]); if($res == false){ + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "failed")); Exceptions::error_rep("Login failed for username '$username' - Database connection error. Redirecting..."); die(header("Location: http://{$ini["general"]["base_url"]}/suite/login.php?error=nodata")); } $count = $res->rowCount(); if($count != 1){ + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "failed")); Exceptions::error_rep("Login failed for username '$username' - User not found. Redirecting..."); die(header("Location: http://{$ini["general"]["base_url"]}/suite/login.php?error=nodata")); } @@ -61,6 +73,7 @@ public static function login($username, $password, $option){ # "option"-> array if(@isset($option["remember"])){ if($ini["general"]["app"] == "true"){ + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "success")); Exceptions::error_rep("Successfully authenticated user '" . $username . "' - LDAP Auth"); @ini_set("session.cookie_samesite", "None"); @session_set_cookie_params(["path" => "/", "domain" => $ini["general"]["base_url"], "secure" => true, "samesite" => "None"]); @@ -68,20 +81,29 @@ public static function login($username, $password, $option){ # "option"-> array setcookie("username", $username, ["samesite" => "None", "secure" => true, "domain" => $ini["general"]["base_url"], "expires" => $ts + (60*60*24*30), "path" => "/"]); header("Refresh: 1; url=http://{$ini["general"]["base_url"]}/suite"); } else { + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "success")); Exceptions::error_rep("Successfully authenticated user '" . $username . "' - LDAP Auth"); setcookie("erinnern", "true", $ts+(60*60*24*30), "/"); setcookie("username", $username, $ts + (60*60*24*30), "/"); header("Refresh: 1; url=http://{$ini["general"]["base_url"]}/suite"); } } else { + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "success")); Exceptions::error_rep("Successfully authenticated user '" . $username . "' - LDAP Auth"); header("Refresh: 1; url=http://{$ini["general"]["base_url"]}/suite"); } die(); } - + if(isset($option["nfclogin"])){ + goto nfclogin; + } if(password_verify($password, $data["password"])){ + if($option["nfclogin"]){ + nfclogin: + Exceptions::error_rep("Authenticated user via NFC login '" . $username . "'"); + } Exceptions::error_rep("Successfully authenticated user '" . $username . "'"); + $ini = Arbeitszeit::get_app_ini(); $ts = time(); $_SESSION["logged_in"] = true; $_SESSION["username"] = $username; @@ -91,6 +113,7 @@ public static function login($username, $password, $option){ # "option"-> array if(@isset($option["remember"])){ if($ini["general"]["app"] == "true"){ + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "success")); Exceptions::error_rep("Successfully authenticated user '" . $username . "'"); @ini_set("session.cookie_samesite", "None"); @session_set_cookie_params(["path" => "/", "domain" => $ini["general"]["base_url"], "secure" => true, "samesite" => "None"]); @@ -98,16 +121,19 @@ public static function login($username, $password, $option){ # "option"-> array setcookie("username", $username, ["samesite" => "None", "secure" => true, "domain" => $ini["general"]["base_url"], "expires" => $ts + (60*60*24*30), "path" => "/"]); header("Refresh: 1; url=http://{$ini["general"]["base_url"]}/suite"); } else { + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "success")); Exceptions::error_rep("Successfully authenticated user '" . $username . "'"); setcookie("erinnern", "true", $ts+(60*60*24*30), "/"); setcookie("username", $username, $ts + (60*60*24*30), "/"); header("Refresh: 1; url=http://{$ini["general"]["base_url"]}/suite"); } } else { + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "success")); Exceptions::error_rep("Successfully authenticated user '" . $username . "'"); header("Refresh: 1; url=http://{$ini["general"]["base_url"]}/suite"); } } else { + EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "failed")); Exceptions::error_rep("Login failed for username '$username' - Incorrect credentials. Redirecting..."); die(header("Location: http://{$base_url}/suite/login.php?error=wrongdata")); } @@ -125,26 +151,28 @@ public function login_validation(){ } @session_start(); if(isset($_SESSION["logged_in"]) == false){ + EventDispatcherService::get()->dispatch(new ValidatedLoginEvent($_SESSION["username"] ?? "N/A", "failed")); Exceptions::error_rep("User not logged in. Redirecting..."); header("Location: http://{$baseurl}/suite/login.php?error=notloggedin"); } if($this->get_state($_SESSION["username"]) != $_COOKIE["state"]){ + EventDispatcherService::get()->dispatch(new ValidatedLoginEvent($_SESSION["username"] ?? "N/A", "failed")); $this->remove_state($_SESSION["username"]); Exceptions::error_rep("State mismatch on user {$_SESSION["username"]}. Removing state and redirecting..."); header("Location: http://{$baseurl}/suite/login.php?error=statemismatch"); - }# temp removed this as it causes errors + } } /** - * logout() - Loggt den Nutzer aus seiner aktuellen Sitzung aus + * logout() - Logs out user * - * @return bool Gibt true bei Erfolg zurück + * @return bool Returns true on success */ public function logout(){ + EventDispatcherService::get()->dispatch(new LoggedOutUserEvent($_SESSION["username"])); session_start(); session_unset(); session_destroy(); - return true; } @@ -248,7 +276,6 @@ public function mail_init($user, $html = false){ return false; } - return $mail; } diff --git a/api/v1/class/auth/plugins/ldap/ldap.auth.arbeit.inc.php b/api/v1/class/auth/plugins/ldap/ldap.auth.arbeit.inc.php index 31601de..f32e323 100644 --- a/api/v1/class/auth/plugins/ldap/ldap.auth.arbeit.inc.php +++ b/api/v1/class/auth/plugins/ldap/ldap.auth.arbeit.inc.php @@ -4,6 +4,8 @@ use LdapTools\LdapManager; use LdapTools\DomainConfiguration; use LdapTools\Operation\AuthenticationOperation; + use Arbeitszeit\Events\LDAPAuthenticationEvent; + use Arbeitszeit\Events\EventDispatcherService; class LDAP extends Auth { public static function get_bind(){ @@ -45,9 +47,10 @@ public static function authenticate($username, $password){ Exceptions::error_rep("Authenticating user '{$username}' through LDAP..."); $operation = (new AuthenticationOperation())->setUsername($username)->setPassword($password); $response = self::get_bind()->getConnection()->execute($operation); - + $code1 = null; if(!$response->isAuthenticated()){ $code = $response->getErrorCode(); + $code = $code1; switch($code){ case "1317": Exceptions::error_rep("Could not authenticate user '{$username}': Account does not exist."); @@ -126,6 +129,7 @@ public static function authenticate($username, $password){ $_SESSION["provider"] = "LDAP"; $_SESSION["provider"] .= "." . $user->get("upn") . "+" . $user->get("sid"); Exceptions::error_rep("Successfully authenticated user '{$username}' through LDAP."); + EventDispatcherService::get()->dispatch(new LDAPAuthenticationEvent($username, $user->get("mail"), "LDAP" . $code1), LDAPAuthenticationEvent::NAME); return true; } } diff --git a/api/v1/class/benutzer/benutzer.arbeit.inc.php b/api/v1/class/benutzer/benutzer.arbeit.inc.php index 9e02b93..aba0f34 100644 --- a/api/v1/class/benutzer/benutzer.arbeit.inc.php +++ b/api/v1/class/benutzer/benutzer.arbeit.inc.php @@ -1,11 +1,17 @@ db = $this->db(); $this->i18n = $this->i18n()->loadLanguage(null, "class/benutzer"); } @@ -17,26 +23,28 @@ public function __construct(){ * @param string $name First name of the employee * @return array|bool Returns true on success and an array otherwise */ - public function create_user($username, $name, $email, $password, $isAdmin = 0){ - #$originalFunction = function($username, $name, $email, $password, $isAdmin){ - Exceptions::error_rep("Creating user '$username'..."); - $password = password_hash($password, PASSWORD_DEFAULT); - $sql = "INSERT INTO `users` (`name`, `username`, `email`, `password`, `email_confirmed`, `isAdmin`) VALUES (?, ?, ?, ?, '1', ?);"; - $data = $this->db->sendQuery($sql)->execute([$name, $username, $email, $password, $isAdmin]); - if($data == false){ - Exceptions::error_rep("An error occured while creating a user. See previous message for more information"); - return [ - "error" => [ - "error_code" => 3, - "error_message" => "Error while creating a user!" - ] - ]; - } else { - Exceptions::error_rep("User '$username' created successfully."); - return true; - } - #}; - #return Hooks::executeWithHooks('create_user', $originalFunction, $username, $name, $email, $password, $isAdmin); + public function create_user($username, $name, $email, $password, $isAdmin = 0) + { + if($this->nodes()->checkNode("benutzer.inc", "create_user") == false){ + return false; + } + Exceptions::error_rep("Creating user '$username'..."); + $password = password_hash($password, PASSWORD_DEFAULT); + $sql = "INSERT INTO `users` (`name`, `username`, `email`, `password`, `email_confirmed`, `isAdmin`) VALUES (?, ?, ?, ?, '1', ?);"; + $data = $this->db->sendQuery($sql)->execute([$name, $username, $email, $password, $isAdmin]); + if ($data == false) { + Exceptions::error_rep("An error occured while creating a user. See previous message for more information"); + return [ + "error" => [ + "error_code" => 3, + "error_message" => "Error while creating a user!" + ] + ]; + } else { + Exceptions::error_rep("User '$username' created successfully."); + EventDispatcherService::get()->dispatch(new UserCreatedEvent($username, $email, $isAdmin), UserCreatedEvent::NAME); + return true; + } } /** @@ -47,11 +55,18 @@ public function create_user($username, $name, $email, $password, $isAdmin = 0){ * * @note This function only deletes the user but not their other data. */ - public function delete_user($id){ + public function delete_user($id) + { + if($this->nodes()->checkNode("benutzer.inc", "delete_user") == false){ + return false; + } + $user = $this->get_user_from_id($id); + $username = $user["username"]; + $email = $user["email"]; Exceptions::error_rep("Deleting user with id '$id'..."); $sql = "DELETE FROM `users` WHERE id = ?;"; $data = $this->db->sendQuery($sql)->execute([$id]); - if($data == false){ + if ($data == false) { Exceptions::error_rep("An error occured while deleting an user. See previous message for more information"); return [ "error" => [ @@ -61,6 +76,7 @@ public function delete_user($id){ ]; } else { Exceptions::error_rep("User with id '$id' deleted successfully."); + EventDispatcherService::get()->dispatch(new UserDeletedEvent($username, $email), UserDeletedEvent::NAME); return true; } } @@ -70,14 +86,15 @@ public function delete_user($id){ * @param string $username * @return array|bool Returns false on failure and an array otherwise */ - public static function get_user($username){ + public static function get_user($username) + { Exceptions::error_rep("Getting user '$username'..."); $sql = "SELECT * FROM `users` WHERE username = ?;"; $db = new DB; $res = $db->sendQuery($sql); $res->execute([$username]); $count = $res->rowCount(); - if($count == 1){ + if ($count == 1) { $data = $res->fetch(\PDO::FETCH_ASSOC); Exceptions::error_rep("User '$username' found."); return $data; @@ -92,13 +109,14 @@ public static function get_user($username){ * @param int $id * @return array|bool Returns false on failure and an array otherwise */ - public static function get_user_from_id($id){ + public static function get_user_from_id($id) + { Exceptions::error_rep("Getting user with id '$id'..."); $conn = new DB(); $sql = "SELECT * FROM `users` WHERE id = ?;"; $res = $conn->sendQuery($sql); $res->execute([$id]); - if($res->rowCount() == 1){ + if ($res->rowCount() == 1) { $data = $res->fetch(\PDO::FETCH_ASSOC); Exceptions::error_rep("User with id '$id' found."); return $data; @@ -113,13 +131,14 @@ public static function get_user_from_id($id){ * @param string $email * @return array|bool Returns false on failure and an array otherwise */ - public static function get_user_from_email($email){ + public static function get_user_from_email($email) + { Exceptions::error_rep("Getting user with email '$email'..."); $conn = new DB(); $sql = "SELECT * FROM `users` WHERE email = ?;"; $res = $conn->sendQuery($sql); $res->execute([$email]); - if($res->rowCount() == 1){ + if ($res->rowCount() == 1) { $data = $res->fetch(\PDO::FETCH_ASSOC); Exceptions::error_rep("User with email '$email' found."); return $data; @@ -134,13 +153,14 @@ public static function get_user_from_email($email){ * @param string $email * @return string|bool Returns false on failure and a string otherwise */ - public function get_username_from_email($email){ + public function get_username_from_email($email) + { Exceptions::error_rep("Getting username with email '$email'..."); $conn = new DB(); $sql = "SELECT username FROM `users` WHERE email = ?;"; $res = $conn->sendQuery($sql); $res->execute([$email]); - if($res->rowCount() == 1){ + if ($res->rowCount() == 1) { $data = $res->fetch(\PDO::FETCH_ASSOC); Exceptions::error_rep("Username with email '$email' found."); return $data["username"]; @@ -154,15 +174,16 @@ public function get_username_from_email($email){ * get_all_users() - Gets all users from the database * @return array|bool Returns false on failure and an array otherwise */ - public function get_all_users(){ + public function get_all_users() + { Exceptions::error_rep("Getting all users..."); $sql = "SELECT * FROM `users`;"; $res = $this->db->sendQuery($sql); $res->execute(); $count = $res->rowCount(); $dat = []; - if($count >= 1){ - while($data = $res->fetch(\PDO::FETCH_ASSOC)){ + if ($count >= 1) { + while ($data = $res->fetch(\PDO::FETCH_ASSOC)) { $dat[$data["id"]] = $data; } Exceptions::error_rep("Users found and returing data."); @@ -177,7 +198,11 @@ public function get_all_users(){ * get_all_users_html() - Gets all users from the database * @return string Returns a string (rendered HTML) on success */ - public function get_all_users_html(){ + public function get_all_users_html() + { + if($this->nodes()->checkNode("benutzer.inc", "get_all_users_html") == false){ + return false; + } Exceptions::error_rep("Getting all users..."); $base_url = $ini = $this->get_app_ini()["general"]["base_url"]; $sql = "SELECT * FROM `users`;"; @@ -185,12 +210,12 @@ public function get_all_users_html(){ $res->execute(); $count = $res->rowCount(); - if($count == 0){ + if ($count == 0) { Exceptions::failure("ERR-NO-USERS", "No users found?", "N/A"); return "{$this->i18n["no_users"]}
"; } - while($data = $res->fetch(\PDO::FETCH_ASSOC)){ - if($data["username"] == "api"){ + while ($data = $res->fetch(\PDO::FETCH_ASSOC)) { + if ($data["username"] == "api") { $data["email"] = "System user"; } @@ -199,7 +224,7 @@ public function get_all_users_html(){ $username = $data["username"]; $id = $data["id"]; - $html = <<< DOC + $html = <<{$this->i18n["unknown_error"]}
"; } - while($data){ + while ($data) { $reww = $data["name"]; $rol = $data["id"]; $ral = $data["email"]; $rww = $data["username"]; - $html = <<< DATA + $html = <<{$this->i18n["name"]}: {$reww}
@@ -259,10 +288,59 @@ public function get_user_html($username){ * @param array $user * @return bool Returns true if the user is an admin and false otherwise */ - public function is_admin($user){ - if($user["isAdmin"] == true){ + public static function is_admin($user) + { + if ($user["isAdmin"] == true) { + return true; + } else { + return false; + } + } + + public function editUserProperties(mixed $username_or_id, string $name, mixed $value): bool + { + if($this->nodes()->checkNode("benutzer.inc", "editUserProperties") == false){ + return false; + } + if ( + !isset($_SESSION["username"]) || + !$this->is_admin($this->get_user($_SESSION["username"])) + ) { + Exceptions::error_rep("Unauthorized attempt by user '{$_SESSION["username"]}' to update user properties!"); + return false; + } + + $allowed_types = ["username", "email", "isAdmin", "name"]; + if (!in_array($name, $allowed_types)) { + Exceptions::error_rep("Could not update user entry – invalid property '{$name}'"); + return false; + } + + if (is_int($username_or_id)) { + $search_type = "id"; + } elseif (is_string($username_or_id)) { + $search_type = "username"; + } else { + Exceptions::error_rep("Invalid identifier type for user update."); + return false; + } + + Exceptions::error_rep("Updating user entry '{$username_or_id}' (Type: {$search_type}); Property: {$name}; Value: {$value}"); + + $sql = "UPDATE `users` SET `$name` = ? WHERE `$search_type` = ?"; + try { + $stmt = $this->db->sendQuery($sql); + $success = $stmt->execute([$value, $username_or_id]); + } catch (\Exception $e) { + Exceptions::error_rep("Database exception while updating user: " . $e->getMessage()); + return false; + } + + if ($success) { + Exceptions::error_rep("Successfully updated user entry '{$username_or_id}' (Type: {$search_type})"); return true; } else { + Exceptions::error_rep("Update failed for user '{$username_or_id}' (Type: {$search_type})"); return false; } } diff --git a/api/v1/class/events/EventDispatcherService.events.arbeit.inc.php b/api/v1/class/events/EventDispatcherService.events.arbeit.inc.php new file mode 100644 index 0000000..0a3c3f4 --- /dev/null +++ b/api/v1/class/events/EventDispatcherService.events.arbeit.inc.php @@ -0,0 +1,15 @@ +addListener(UserCreatedEvent::class, function (UserCreatedEvent $event){ + // do something + $user = $event->getUsername(); +}); +``` + +## Register own event + +Create event class: + +```php + +namespace Yourplugin\plugin; +use Symfony\Contracts\EventDispatcher\Event; + +class YourEvent extends Event +{ + public const NAME = 'yourplugin.your_event'; + + private $data; + + public function __construct($data) + { + $this->data = $data; + } + + public function getData() + { + return $this->data; + } +} + +``` + +Inside your Main.php / plugin main file: + +```php +// require_once "path/to/event.php"; +// use Yourplugin\plugin\YourEvent; + + +... onLoad(): void { // which is called when the plugin is loaded, place below line somewhere else if you want to trigger it later + EventDispatcherService::get()->dispatch(new YourEvent($data), YourEvent::NAME); +} + +``` diff --git a/api/v1/class/events/auth/LDAPAuthenticationEvent.php b/api/v1/class/events/auth/LDAPAuthenticationEvent.php new file mode 100644 index 0000000..ffaed6d --- /dev/null +++ b/api/v1/class/events/auth/LDAPAuthenticationEvent.php @@ -0,0 +1,21 @@ +username = $username; + } + + public function getUsername(): string + { + return $this->username; + } +} diff --git a/api/v1/class/events/auth/LoggedInUserEvent.php b/api/v1/class/events/auth/LoggedInUserEvent.php new file mode 100644 index 0000000..09a175a --- /dev/null +++ b/api/v1/class/events/auth/LoggedInUserEvent.php @@ -0,0 +1,33 @@ +username = $username; + $this->type = $type; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getType(): string + { + return $this->type; + } +} diff --git a/api/v1/class/events/auth/LoggedOutUserEvent.php b/api/v1/class/events/auth/LoggedOutUserEvent.php new file mode 100644 index 0000000..37abff1 --- /dev/null +++ b/api/v1/class/events/auth/LoggedOutUserEvent.php @@ -0,0 +1,21 @@ +username = $username; + } + + public function getUsername(): string + { + return $this->username; + } +} diff --git a/api/v1/class/events/auth/ValidatedLoginEvent.php b/api/v1/class/events/auth/ValidatedLoginEvent.php new file mode 100644 index 0000000..0ea01b1 --- /dev/null +++ b/api/v1/class/events/auth/ValidatedLoginEvent.php @@ -0,0 +1,28 @@ +username = $username; + $this->type = $type; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getType(): string + { + return $this->type; + } +} diff --git a/api/v1/class/events/exports/generatedExportEvent.php b/api/v1/class/events/exports/generatedExportEvent.php new file mode 100644 index 0000000..1f7e4c2 --- /dev/null +++ b/api/v1/class/events/exports/generatedExportEvent.php @@ -0,0 +1,29 @@ +username = $username; + $this->module = $module; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getModule(): string + { + return $this->module; + } +} diff --git a/api/v1/class/events/loader.events.arbeit.inc.php b/api/v1/class/events/loader.events.arbeit.inc.php new file mode 100644 index 0000000..aa3aa3d --- /dev/null +++ b/api/v1/class/events/loader.events.arbeit.inc.php @@ -0,0 +1,39 @@ + \ No newline at end of file diff --git a/api/v1/class/events/mails/SentMailEvent.php b/api/v1/class/events/mails/SentMailEvent.php new file mode 100644 index 0000000..5a05299 --- /dev/null +++ b/api/v1/class/events/mails/SentMailEvent.php @@ -0,0 +1,36 @@ +username = $username; + $this->email = $email; + $this->type = $type; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getEmail(): string + { + return $this->email; + } + + public function getType(): string + { + return $this->type; + } +} diff --git a/api/v1/class/events/notifications/CreatedNotificationEvent.php b/api/v1/class/events/notifications/CreatedNotificationEvent.php new file mode 100644 index 0000000..5f71aec --- /dev/null +++ b/api/v1/class/events/notifications/CreatedNotificationEvent.php @@ -0,0 +1,42 @@ +username = $username; + $this->date = $date; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getTitle(): int + { + return $this->title; + } + + public function getDate(): string + { + return $this->date; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/notifications/DeletedNotificationEvent.php b/api/v1/class/events/notifications/DeletedNotificationEvent.php new file mode 100644 index 0000000..a8d142a --- /dev/null +++ b/api/v1/class/events/notifications/DeletedNotificationEvent.php @@ -0,0 +1,28 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/notifications/DeletedObsoleteNotificationsEvent.php b/api/v1/class/events/notifications/DeletedObsoleteNotificationsEvent.php new file mode 100644 index 0000000..04484e7 --- /dev/null +++ b/api/v1/class/events/notifications/DeletedObsoleteNotificationsEvent.php @@ -0,0 +1,28 @@ +username = 'system'; + $this->type = 0; // Assuming 0 is the type for system events + } + + public function getUsername(): string + { + return $this->username; + } + + public function getType(): int + { + return $this->type; + } +} diff --git a/api/v1/class/events/notifications/EditedNotificationEvent.php b/api/v1/class/events/notifications/EditedNotificationEvent.php new file mode 100644 index 0000000..103e1f9 --- /dev/null +++ b/api/v1/class/events/notifications/EditedNotificationEvent.php @@ -0,0 +1,28 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/sickness/SicknessCreatedEvent.php b/api/v1/class/events/sickness/SicknessCreatedEvent.php new file mode 100644 index 0000000..efc8a69 --- /dev/null +++ b/api/v1/class/events/sickness/SicknessCreatedEvent.php @@ -0,0 +1,35 @@ +username = $username; + $this->start = $start; + $this->end = $end; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getStart(): string + { + return $this->start; + } + public function getEnd(): string + { + return $this->end; + } + +} diff --git a/api/v1/class/events/sickness/SicknessDeletedEvent.php b/api/v1/class/events/sickness/SicknessDeletedEvent.php new file mode 100644 index 0000000..18e5f48 --- /dev/null +++ b/api/v1/class/events/sickness/SicknessDeletedEvent.php @@ -0,0 +1,28 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/sickness/SicknessUpdatedEvent.php b/api/v1/class/events/sickness/SicknessUpdatedEvent.php new file mode 100644 index 0000000..081ab1b --- /dev/null +++ b/api/v1/class/events/sickness/SicknessUpdatedEvent.php @@ -0,0 +1,34 @@ +username = $username; + $this->id = $id; + $this->status = $status; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } + + public function getStatus(): string + { + return $this->status; + } +} diff --git a/api/v1/class/events/users/UserCreatedEvent.php b/api/v1/class/events/users/UserCreatedEvent.php new file mode 100644 index 0000000..ff57d52 --- /dev/null +++ b/api/v1/class/events/users/UserCreatedEvent.php @@ -0,0 +1,36 @@ +username = $username; + $this->email = $email; + $this->isAdmin = $isAdmin; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getEmail(): string + { + return $this->email; + } + + public function getIsAdmin(): int + { + return $this->isAdmin; + } +} diff --git a/api/v1/class/events/users/UserDeletedEvent.php b/api/v1/class/events/users/UserDeletedEvent.php new file mode 100644 index 0000000..92cdc7c --- /dev/null +++ b/api/v1/class/events/users/UserDeletedEvent.php @@ -0,0 +1,30 @@ +username = $username; + $this->email = $email; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getEmail(): string + { + return $this->email; + } +} diff --git a/api/v1/class/events/vacation/VacationCreatedEvent.php b/api/v1/class/events/vacation/VacationCreatedEvent.php new file mode 100644 index 0000000..c2728b5 --- /dev/null +++ b/api/v1/class/events/vacation/VacationCreatedEvent.php @@ -0,0 +1,34 @@ +username = $username; + $this->start = $start; + $this->end = $end; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getStart(): string + { + return $this->start; + } + public function getEnd(): string + { + return $this->end; + } +} diff --git a/api/v1/class/events/vacation/VacationDeletedEvent.php b/api/v1/class/events/vacation/VacationDeletedEvent.php new file mode 100644 index 0000000..ff4c8fd --- /dev/null +++ b/api/v1/class/events/vacation/VacationDeletedEvent.php @@ -0,0 +1,28 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/vacation/VacationUpdatedEvent.php b/api/v1/class/events/vacation/VacationUpdatedEvent.php new file mode 100644 index 0000000..a43ad3d --- /dev/null +++ b/api/v1/class/events/vacation/VacationUpdatedEvent.php @@ -0,0 +1,34 @@ +username = $username; + $this->id = $id; + $this->status = $status; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } + + public function getStatus(): string + { + return $this->status; + } +} diff --git a/api/v1/class/events/worktimes/EasymodeWorktimeAddedEvent.php b/api/v1/class/events/worktimes/EasymodeWorktimeAddedEvent.php new file mode 100644 index 0000000..36ecc37 --- /dev/null +++ b/api/v1/class/events/worktimes/EasymodeWorktimeAddedEvent.php @@ -0,0 +1,20 @@ +username = $username; + } + + public function getUsername(): string + { + return $this->username; + } + +} diff --git a/api/v1/class/events/worktimes/EasymodeWorktimeEndedEvent.php b/api/v1/class/events/worktimes/EasymodeWorktimeEndedEvent.php new file mode 100644 index 0000000..7e9fbbc --- /dev/null +++ b/api/v1/class/events/worktimes/EasymodeWorktimeEndedEvent.php @@ -0,0 +1,27 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/worktimes/EasymodeWorktimePauseEndEvent.php b/api/v1/class/events/worktimes/EasymodeWorktimePauseEndEvent.php new file mode 100644 index 0000000..63b4139 --- /dev/null +++ b/api/v1/class/events/worktimes/EasymodeWorktimePauseEndEvent.php @@ -0,0 +1,27 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/worktimes/EasymodeWorktimePauseStartEvent.php b/api/v1/class/events/worktimes/EasymodeWorktimePauseStartEvent.php new file mode 100644 index 0000000..989b07d --- /dev/null +++ b/api/v1/class/events/worktimes/EasymodeWorktimePauseStartEvent.php @@ -0,0 +1,27 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/worktimes/FixEasymodeWorktimeEvent.php b/api/v1/class/events/worktimes/FixEasymodeWorktimeEvent.php new file mode 100644 index 0000000..10484c7 --- /dev/null +++ b/api/v1/class/events/worktimes/FixEasymodeWorktimeEvent.php @@ -0,0 +1,20 @@ +username = $username; + } + + public function getUsername(): string + { + return $this->username; + } +} diff --git a/api/v1/class/events/worktimes/WorktimeAddedEvent.php b/api/v1/class/events/worktimes/WorktimeAddedEvent.php new file mode 100644 index 0000000..b686454 --- /dev/null +++ b/api/v1/class/events/worktimes/WorktimeAddedEvent.php @@ -0,0 +1,27 @@ +username = $username; + $this->dates = $dates; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getDates(): array + { + return $this->dates; + } +} diff --git a/api/v1/class/events/worktimes/WorktimeDeletedEvent.php b/api/v1/class/events/worktimes/WorktimeDeletedEvent.php new file mode 100644 index 0000000..e164419 --- /dev/null +++ b/api/v1/class/events/worktimes/WorktimeDeletedEvent.php @@ -0,0 +1,27 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/worktimes/WorktimeMarkedForReviewEvent.php b/api/v1/class/events/worktimes/WorktimeMarkedForReviewEvent.php new file mode 100644 index 0000000..101c543 --- /dev/null +++ b/api/v1/class/events/worktimes/WorktimeMarkedForReviewEvent.php @@ -0,0 +1,27 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/events/worktimes/WorktimeUnlockedFromReviewEvent.php b/api/v1/class/events/worktimes/WorktimeUnlockedFromReviewEvent.php new file mode 100644 index 0000000..f93fec5 --- /dev/null +++ b/api/v1/class/events/worktimes/WorktimeUnlockedFromReviewEvent.php @@ -0,0 +1,27 @@ +username = $username; + $this->id = $id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/api/v1/class/exports/ExportModule.arbeit.inc.php b/api/v1/class/exports/ExportModule.arbeit.inc.php index 99bf527..e48a5b1 100644 --- a/api/v1/class/exports/ExportModule.arbeit.inc.php +++ b/api/v1/class/exports/ExportModule.arbeit.inc.php @@ -1,5 +1,7 @@ getExportModule($args['module']); if ($module) { + EventDispatcherService::get()->dispatch(new generatedExportEvent($_SESSION["username"], $args['module']), generatedExportEvent::NAME); return $module->export($args); } return false; diff --git a/api/v1/class/i18n/suite/status/snippets_DE.json b/api/v1/class/i18n/suite/status/snippets_DE.json index 3cc1e08..1fd374c 100644 --- a/api/v1/class/i18n/suite/status/snippets_DE.json +++ b/api/v1/class/i18n/suite/status/snippets_DE.json @@ -25,5 +25,6 @@ "error": "Hinweis: Ein Fehler ist aufgetreten. Bitte überprüfe den Log, falls möglich.", "statemismatch": "Fehler: Sicherheitsfehler", "ldapauth": "Fehler: Die Authentifizierung über LDAP ist fehlgeschlagen.", - "ldapcreated": "Hinweis: Bitte melde dich erneut an. Dein Benutzerkonto wurde nun erstellt. (LDAP allowed self-login)" + "ldapcreated": "Hinweis: Bitte melde dich erneut an. Dein Benutzerkonto wurde nun erstellt. (LDAP allowed self-login)", + "notification_not_found": "Fehler: Benachrichtigung nicht gefunden." } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/status/snippets_EN.json b/api/v1/class/i18n/suite/status/snippets_EN.json index 7d01ccb..319c372 100644 --- a/api/v1/class/i18n/suite/status/snippets_EN.json +++ b/api/v1/class/i18n/suite/status/snippets_EN.json @@ -25,6 +25,7 @@ "error": "Note: An error has occurred. Please check the log if possible.", "statemismatch": "Error: Security error", "ldapauth": "Error: The authentication via LDAP failed!", - "ldapcreated": "Note: Please login again. Your account has just been created. (LDAP allowed self-login)" + "ldapcreated": "Note: Please login again. Your account has just been created. (LDAP allowed self-login)", + "notification_not_found": "Error: Notification not found." } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/status/snippets_NL.json b/api/v1/class/i18n/suite/status/snippets_NL.json index 576e8bb..4a9d771 100644 --- a/api/v1/class/i18n/suite/status/snippets_NL.json +++ b/api/v1/class/i18n/suite/status/snippets_NL.json @@ -25,5 +25,6 @@ "error": "Opmerking: er is een fout opgetreden. Controleer indien mogelijk het logboek.", "statemismatch": "Fout: beveiligingsfout", "ldapauth": "Fout: Authenticatie via LDAP mislukt.", - "ldapcreated": "Opmerking: log opnieuw in. Uw gebruikersaccount is nu aangemaakt. (LDAP staat zelfinloggen toe)" + "ldapcreated": "Opmerking: log opnieuw in. Uw gebruikersaccount is nu aangemaakt. (LDAP staat zelfinloggen toe)", + "notification_not_found": "Fout: melding niet gevonden." } \ No newline at end of file diff --git a/api/v1/class/mails/Mails.arbeit.inc.php b/api/v1/class/mails/Mails.arbeit.inc.php index 1f7203c..77f7e01 100644 --- a/api/v1/class/mails/Mails.arbeit.inc.php +++ b/api/v1/class/mails/Mails.arbeit.inc.php @@ -2,7 +2,12 @@ namespace Arbeitszeit; use Arbeitszeit\Mails\MailsProviderInterface; +use Arbeitszeit\Arbeitszeit; use Arbeitszeit\Mails\MailTemplateData; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Arbeitszeit\Events\EventDispatcherService; +use Arbeitszeit\Events\SentMailEvent; + class Mails { @@ -52,7 +57,14 @@ public static function sendMail(string $templateName, array $data) $mailContent = $template->render($data); - return self::$provider->send($mailContent->toArray()); + $ini = Arbeitszeit::get_app_ini()["smtp"]; + if(!$ini["smtp"]){ + Exceptions::error_rep("SMTP disabled, not sending mail."); + return true; + } else { + EventDispatcherService::get()->dispatch(new SentMailEvent($mailContent->toArray()["to"], $mailContent->toArray()["subject"], $mailContent->toArray()["body"]), SentMailEvent::NAME); + return self::$provider->send($mailContent->toArray()); + } } diff --git a/api/v1/class/mails/Mails.md b/api/v1/class/mails/Mails.md index f869567..b2d1f45 100644 --- a/api/v1/class/mails/Mails.md +++ b/api/v1/class/mails/Mails.md @@ -35,20 +35,17 @@ You can implement your own MailTemplate like this: "Subject of the mail", - "body" => "Body of the mail", - "username" => "Username" - ] + return new MailTemplateData($data["subject"], $data["body"], $data["username"]); } } diff --git a/api/v1/class/mails/templates/WorktimeUncompliantTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/WorktimeUncompliantTemplate.mails.arbeit.inc.php index d155f97..92afc06 100644 --- a/api/v1/class/mails/templates/WorktimeUncompliantTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/WorktimeUncompliantTemplate.mails.arbeit.inc.php @@ -18,7 +18,7 @@ public function render(array $data): MailTemplateData $conn = $arbeit->db(); $sql = "SELECT * FROM `users` WHERE username = ?;"; $res = $conn->sendQuery($sql); - $res->execute($data["username"]); + $res->execute([$data["username"]]); $count = $res->rowCount(); $ii = Arbeitszeit::get_app_ini()["general"]["app_name"]; diff --git a/api/v1/class/mode/mode.arbeit.inc.php b/api/v1/class/mode/mode.arbeit.inc.php index 92da39a..44d9bed 100644 --- a/api/v1/class/mode/mode.arbeit.inc.php +++ b/api/v1/class/mode/mode.arbeit.inc.php @@ -18,12 +18,16 @@ public static function check($username){ private static function get_normal_mode_html(){ + $nodes = new Nodes; + if($nodes->checkNode("mode.inc", "get_normal_mode_html") == false){ + return; + } $i18n = new i18n; $loc = $i18n->loadLanguage(null, "mode/easymode"); $data = <<< DATA