diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b10ce88..ad0c2ff 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,40 +1,45 @@ --- -name: Bug report -about: Create a report to help us improve -title: "[BUG]" -labels: '' + +name: 🐞 Bug Report +about: Report something that’s broken or not working as expected in TimeTrack +title: "[BUG] Short and clear description" +labels: bug assignees: '' --- -**Describe the bug** -A clear and concise description of what the bug is. +## 🔍 Describe the Bug +What exactly is going wrong? +> Example: When exporting a report, the total hours show "NaN". -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' +## 🧪 Steps to Reproduce +How can we see the bug happen? +1. Go to "..." +2. Click on "..." +3. Scroll down to "..." 4. See error -**Expected behavior** -A clear and concise description of what you expected to happen. +## 🤔 Expected Behavior +What should have happened instead? +> Example: The total hours should be calculated and displayed correctly. -**Screenshots** -If applicable, add screenshots to help explain your problem. +## 🖼️ Screenshots (if applicable) +Add screenshots or logs if they help explain the issue. -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] +## 🖥️ Environment +**Desktop:** +- OS: [e.g. Windows 11] +- Browser: [e.g. Firefox] +- Version: [e.g. 124.0] -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] +**Mobile (if applicable):** +- Device: [e.g. Pixel 6] +- OS: [e.g. Android 13] +- Browser: [e.g. Chrome] +- Version: [e.g. 124.0] -**TimeTrack Version**: e.g. v7.9 +## 🧭 TimeTrack Version +> Example: v7.12 (API v1.11) -**Additional context** -Add any other context about the problem here. +## 🧩 Additional Context +Any extra info, logs, console errors, or related issues? diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e0c0168..8cd0de6 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,20 +1,25 @@ --- -name: Feature request -about: Suggest an idea for this project -title: "[FEATURE]" -labels: '' + +name: 💡 Feature Request +about: Suggest an idea or improvement for TimeTrack (OpenDucks edition) +title: "[FEATURE] Brief and clear summary" +labels: enhancement assignees: '' --- -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +## 🧩 Problem Description +What problem are you currently facing? +> Example: It's hard to quickly see how many hours I worked last week. -**Describe the solution you'd like** -A clear and concise description of what you want to happen. +## ✅ Proposed Solution +Describe how you’d like TimeTrack to solve this. +> Example: Add a weekly export button that generates a CSV or PDF. -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. +## 🔁 Alternatives Considered +Have you tried any workarounds or alternative ideas? +> Example: I export daily logs manually, but it’s tedious. -**Additional context** -Add any other context or screenshots about the feature request here. +## 🧠 Additional Context +Any screenshots, mockups, or links that help explain your idea better? +> Drop them here 👇 diff --git a/.github/workflows/phpmd.yml b/.github/workflows/phpmd.yml new file mode 100644 index 0000000..fad56fd --- /dev/null +++ b/.github/workflows/phpmd.yml @@ -0,0 +1,57 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# PHPMD is a spin-off project of PHP Depend and +# aims to be a PHP equivalent of the well known Java tool PMD. +# What PHPMD does is: It takes a given PHP source code base +# and look for several potential problems within that source. +# These problems can be things like: +# Possible bugs +# Suboptimal code +# Overcomplicated expressions +# Unused parameters, methods, properties +# More details at https://phpmd.org/ + +name: PHPMD + +on: + push: + branches: [ "develop" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "develop" ] + schedule: + - cron: '38 18 * * 1' + +permissions: + contents: read + +jobs: + PHPMD: + name: Run PHPMD scanning + runs-on: ubuntu-latest + permissions: + contents: read # for checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@aa1fe473f9c687b6fb896056d771232c0bc41161 + with: + coverage: none + tools: phpmd + + - name: Run PHPMD + run: phpmd . sarif codesize --reportfile phpmd-results.sarif + continue-on-error: true + + - name: Upload analysis results to GitHub + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: phpmd-results.sarif + wait-for-processing: true diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml new file mode 100644 index 0000000..cbe47d5 --- /dev/null +++ b/.github/workflows/psalm.yml @@ -0,0 +1,41 @@ +name: Psalm Security Scan + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + schedule: + - cron: '27 5 * * 1' + +permissions: + contents: read + +jobs: + php-security: + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + actions: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' # or your required PHP version + extensions: ldap + + - name: Install dependencies + run: composer install --no-progress --prefer-dist + + - name: Psalm Security Scan + uses: psalm/psalm-github-security-scan@f3e6fd9432bc3e44aec078572677ce9d2ef9c287 + + - name: Upload Security Analysis results to GitHub + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac2d86..5b89914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # CHANGELOG +## v7.13.1 + +* Fixed issues with LDAP authentication +* Fixed an issue with setting the status of vacations +* Fixed a php leak for the log file causing the settings page to crash when the log file is too large and php memory limit is too low + +## v7.13 + +* You can now set different types of worktimes. You can specify your own ones in the `app/v1/inc/config/worktime_types.json` file. If none is set, like when using the easymode, mode `0` will be used. Added `Wtype` parameter to `WorktimeAdded` event. +* Toil API release `1.12`: added Bearer token authentication, fixed an issue with the `addOwnWorktime` and `addWorktime` endpoints. +* Fixed an issue allowing normal users to reset all PINs for the CodeClock plugin +* Fixed some typos in some of the error messages +* Reworked the way status messages are displayed and used. Implemented a new `StatusMessage` class to handle status messages which can also be used by plugins. + +## v7.12.1 + +* Updated `README.md` +* Updated Plugin template +* You can now set the timezone within `app.json` (see `README.md` for more information) +* Fixed an issue with the generatedExport event +* Fixed db migrations for first user creation + ## v7.12 * Added a simple Favicon diff --git a/README.md b/README.md index 5fa5c7b..89f8afb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TimeTrack - small enterprise time recording -TimeTrack aims to be a easy-to-use time recording software for small enterprises. +TimeTrack aims to be an easy-to-use time recording software for small enterprises. It's a fork from TimeTrack Oval, v6.2 (license-based model, within cloud & more features) ## Features @@ -14,9 +14,9 @@ It's a fork from TimeTrack Oval, v6.2 (license-based model, within cloud & more - Maintenance mode - Easy and fast installation - LDAP Authentication - -That's not even all of it, you can also generate timesheets (PDF) to export, user creation menu, an "easymode" to make it even easier to track your time and a mobile-friendly UI. -Additional functionality can be unlocked with TimeTrack Oval +- Supporting NFC Login +- Plugin Support +- Exporting to PDF/CSV ## Installation @@ -63,7 +63,7 @@ In step 2, you need to configure the `app.json.sample` within the `api/v1/inc` f - `port`: Specify a custom port or change the port if you do not want to use encryption - `usessl`: Specify if you want to use STARTTLS (false) after initial communication or use SSL (true) -If you plan to use this system with a Gmail-Account, please be aware that you are not able to use your usual password. You would have to create a seperate `App Password`, you should note down. +If you plan to use this system with a Gmail-Account, please be aware that you are not able to use your usual password. You would have to create a seperete `App Password`, you should note down. You can do this following this link: or by navigating from to `Security` > `2-Factor Authentication` > `App Passwords`. If you do not see this option on screen, use the link. #### **Plugins** (Read more at `/api/v1/classes/plugins/docs`) @@ -71,7 +71,7 @@ You can do this following this link: exportModule()->getExportModule("MyExportExportModule")->export($data); ``` -As there is currently no Export Area in the UI you have to create the GUI elements on your own. +All existing export modules can be accessed with the `ExportManager` Plugin. You can specify your own CSS file within the `app.json` `exports -> pdf -> css` setting (full path) - the default is `api/v1/class/exports/modules/PDFExportModule/css/index.css` ## QR codes @@ -188,4 +188,22 @@ If downloaded any other way, just make sure to copy and paste the new files into ### Database -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 +You can update the database by using `vendor/bin/phinx migrate` to migrate to latest release or `vendor/bin/phinx rollback` to rollback. + +## Managed Hosting + +If you don't want to worry about installation, updates and maintenance - let us do it for you. + +With our managed TimeTrack hosting, you get: + +* 🛡️ Fully GDPR-compliant hosting in Germany or Netherlands +* 🚀 Always up-to-date with the latest features and security patches +* 🔐 Secure HTTPS out-of-the-box +* ☁ Backups, monitoring and support included +* 🧩 Custom plugins, integration, branding + +Want to move your team to the cloud? Check out our website for more information. + +## License + +The original project is licensed under the GPLv3 license - see the [LICENSE](LICENSE) file for details. diff --git a/VERSION b/VERSION index 4f294e2..15958dd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.12 \ No newline at end of file +7.13.1 \ No newline at end of file diff --git a/api/v1/class/arbeitszeit.inc.php b/api/v1/class/arbeitszeit.inc.php index 0f86ec5..2338780 100644 --- a/api/v1/class/arbeitszeit.inc.php +++ b/api/v1/class/arbeitszeit.inc.php @@ -13,6 +13,7 @@ use Arbeitszeit\ExportModule; use Arbeitszeit\Mails; use Arbeitszeit\Nodes; + use Arbeitszeit\StatusMessages; use Arbeitszeit\Events\EventDispatcherService; use Arbeitszeit\Events\EasymodeWorktimeAddedEvent; // "EasymodeWorktimeSTARTED" Event, actually. use Arbeitszeit\Events\EasymodeWorktimeEndedEvent; @@ -44,12 +45,12 @@ class Arbeitszeit private $exportModule; private $mails; private $nodes; + private $statusMessages; 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"]); @@ -80,8 +81,6 @@ public function init_lang() * @param int $id ID des zu löschenden Eintrags * @return bool|array Wirft "true" bei erfolg und ein Array bei einem Fehler zurück * - * @Hinweis **In der Originalen Datei wurde zu `alle_arbeitszeiten.php?info=deleted_worktime` verwiesen, evt. mal ändern** - * */ public function delete_worktime($id) { @@ -90,7 +89,7 @@ public function delete_worktime($id) } $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."); + Exceptions::error_rep("An error occurred while deleting an worktime entry. See previous message for more information."); return [ "error" => [ "error_code" => 1, @@ -119,14 +118,14 @@ public static function add_easymode_worktime($username) $usr = $user->get_user($username); if (!$user->get_user($username)) { - Exceptions::error_rep("An error occured while creating easymode worktime entry for user '{$username}'. User does not exist."); + Exceptions::error_rep("An error occurred while creating easymode worktime entry for user '{$username}'. User does not exist."); return false; } else { Exceptions::error_rep("Creating easymode worktime entry for user '{$username}'..."); $sql = "INSERT INTO `arbeitszeiten` (`name`, `id`, `email`, `username`, `schicht_tag`, `schicht_anfang`, `schicht_ende`, `ort`, `active`, `review`) VALUES ( ?, '0', ?, ?, ?, ?, '00:00', '-', '1', '0');"; $data = $conn->sendQuery($sql)->execute([$usr["name"], $usr["email"], $username, $date, $time]); if ($data == false) { - Exceptions::error_rep("An error occured while creating easymode worktime entry. See previous message for more information"); + Exceptions::error_rep("An error occurred while creating easymode worktime entry. See previous message for more information"); return false; } else { EventDispatcherService::get()->dispatch(new EasymodeWorktimeAddedEvent($username), EasymodeWorktimeAddedEvent::NAME); @@ -148,14 +147,14 @@ public static function end_easymode_worktime($username, $id) $user = new Benutzer(); if (!$user->get_user($username)) { - Exceptions::error_rep("An error occured while ending easymode worktime for user '{$username}'. User does not exist."); + Exceptions::error_rep("An error occurred while ending easymode worktime for user '{$username}'. User does not exist."); return false; } else { Exceptions::error_rep("Ending easymode worktime for user '{$username}'..."); $sql = "UPDATE `arbeitszeiten` SET `schicht_ende` = ?, `active` = '0' WHERE `id` = ?;"; $data = $conn->sendQuery($sql)->execute([$time, $id]); if (!$data) { - Exceptions::error_rep("An error occured while ending easymode worktime. See previous message for more information."); + Exceptions::error_rep("An error occurred while ending easymode worktime. See previous message for more information."); return false; } else { EventDispatcherService::get()->dispatch(new EasymodeWorktimeEndedEvent($username, (int)$id), EasymodeWorktimeEndedEvent::NAME); @@ -175,14 +174,14 @@ public function start_easymode_pause_worktime($username, $id) $user = new Benutzer; if (!$user->get_user($username)) { - Exceptions::error_rep("An error occured while starting user pause for worktime with ID '{$id}' for user '{$username}'. User does not exist."); + Exceptions::error_rep("An error occurred while starting user pause for worktime with ID '{$id}' for user '{$username}'. User does not exist."); return false; } else { Exceptions::error_rep("Starting easymode pause for user '{$username}'..."); $sql = "UPDATE `arbeitszeiten` SET `pause_start` = ? WHERE id = ?;"; $data = $this->db->sendQuery($sql)->execute([$time, $id]); if (!$data) { - Exceptions::error_rep("An error occured while starting user pause for worktime with ID '{$id}' for user '{$username}'. See previous message for more information."); + Exceptions::error_rep("An error occurred 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); @@ -201,14 +200,14 @@ public function end_easymode_pause_worktime($username, $id) $user = new Benutzer; if (!$user->get_user($username)) { - Exceptions::error_rep("An error occured while ending user pause for worktime with ID '{$id}' for user '{$username}'. User does not exist."); + Exceptions::error_rep("An error occurred while ending user pause for worktime with ID '{$id}' for user '{$username}'. User does not exist."); return false; } else { Exceptions::error_rep("Ending easymode pause for user '{$username}'..."); $sql = "UPDATE `arbeitszeiten` SET `pause_end` = ? WHERE id = ?;"; $data = $this->db->sendQuery($sql)->execute([$time, $id]); if (!$data) { - Exceptions::error_rep("An error occured while ending user pause for worktime with ID '{$id}' for user '{$username}'. See previous message for more information."); + Exceptions::error_rep("An error occurred 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); @@ -228,7 +227,7 @@ public function toggle_easymode($username) $res = $this->db->sendQuery($sql); $res->execute([$username]); if (!$res) { - Exceptions::error_rep("An error occured while toggling easymode for user '{$username}'!"); + Exceptions::error_rep("An error occurred while toggling easymode for user '{$username}'!"); return false; } else { $data = $res->fetch(\PDO::FETCH_ASSOC); @@ -236,7 +235,7 @@ public function toggle_easymode($username) $sql1 = "UPDATE `users` SET `easymode` = '1' WHERE username = ?;"; $res1 = $this->db->sendQuery($sql1)->execute([$username]); if (!$res1) { - Exceptions::error_rep("An error occured while toggling easymode for user '{$username}'! Could not enable mode."); + Exceptions::error_rep("An error occurred while toggling easymode for user '{$username}'! Could not enable mode."); return false; } return true; @@ -244,7 +243,7 @@ public function toggle_easymode($username) $sql1 = "UPDATE `users` SET `easymode` = '0' WHERE username = ?;"; $res1 = $this->db->sendQuery($sql1)->execute([$username]); if (!$res1) { - Exceptions::error_rep("An error occured while toggling easymode for user '{$username}'! Could not disable mode."); + Exceptions::error_rep("An error occurred while toggling easymode for user '{$username}'! Could not disable mode."); return false; } Exceptions::error_rep("Easymode disabled for user '{$username}'"); @@ -263,7 +262,7 @@ public function get_easymode_status($username, $mode = 0) $res = $this->db->sendQuery($sql); $res->execute([$username]); if (!$res) { - Exceptions::error_rep("An error occured while getting status easymode for user '{$username}'!"); + Exceptions::error_rep("An error occurred while getting status easymode for user '{$username}'!"); return false; } else { $data = $res->fetch(\PDO::FETCH_ASSOC); @@ -304,11 +303,11 @@ public static function check_easymode_worktime_finished($username) $res = $db->sendQuery($sql); $res->execute([$username]); if (!$res) { - Exceptions::error_rep("An error occured while checking user entries for easymode worktime"); + Exceptions::error_rep("An error occurred while checking user entries for easymode worktime"); return false; } else { if ($res->rowCount() > 1) { - Exceptions::error_rep("An error occured while checking user entries for easymode worktime. Duplicated easymode entry found, please fix manually."); + Exceptions::error_rep("An error occurred while checking user entries for easymode worktime. Duplicated easymode entry found, please fix manually."); return false; } elseif ($res->rowCount() < 1) { Exceptions::error_rep("No easymode worktime found for user '{$username}'"); @@ -328,9 +327,10 @@ public static function check_easymode_worktime_finished($username) * @param string $location Ort der Schicht * @param date $date Datum der Schicht * @param string $username Username des Mitarbeiters + * @param string $Wtype Type of worktime * @return bool Gibt true bei Erfolg und false bei einem Fehler zurück */ - public function add_worktime($start, $end, $location, $date, $username, $type, $pause = null, $meta = null) + public function add_worktime($start, $end, $location, $date, $username, $type, $Wtype = 0, $pause = null, $meta = null) { if(!$this->nodes()->checkNode("arbeitszeit.inc", "add_worktime")) { return false; @@ -338,28 +338,55 @@ public function add_worktime($start, $end, $location, $date, $username, $type, $ $user = new Benutzer; $usr = $user->get_user($username); if ($date > date("y-m-d") /*|| $date < date("y-m-d")*/) { - Exceptions::error_rep("An error occured while creating an worktime entry. The date is in the future."); + Exceptions::error_rep("An error occurred while creating an worktime entry. The date is in the future."); return false; } if (!$user->get_user($username)) { - Exceptions::error_rep("An error occured while creating an worktime entry. The user does not exist."); + Exceptions::error_rep("An error occurred while creating an worktime entry. The user does not exist."); return false; } else { + + if($this->type_from_int($Wtype) == false){ + Exceptions::error_rep("An error occurred while creating an worktime entry. The worktime type does not exist."); + return false; + } + Exceptions::error_rep("Creating worktime entry for user '{$username}'..."); - $sql = "INSERT INTO `arbeitszeiten` (`name`, `id`, `email`, `username`, `schicht_tag`, `schicht_anfang`, `schicht_ende`, `ort`, `review`, `active`, `type`, `pause_start`, `pause_end`, `attachements`) VALUES ( ?, '0', ?, ?, ?, ?, ?, ?, '0', '0', ?, ?, ?, ?);"; + $sql = "INSERT INTO `arbeitszeiten` (`name`, `id`, `email`, `username`, `schicht_tag`, `schicht_anfang`, `schicht_ende`, `ort`, `review`, `active`, `type`, `Wtype`, `pause_start`, `pause_end`, `attachements`) VALUES ( ?, '0', ?, ?, ?, ?, ?, ?, '0', '0', ?, ?, ?, ?, ?);"; $data = $this->db->sendQuery($sql); - $data->execute([$usr["name"], $usr["email"], $username, $date, $start, $end, $location, $type, $pause["start"], $pause["end"], $meta]); + $data->execute([$usr["name"], $usr["email"], $username, $date, $start, $end, $location, $type, $Wtype ,$pause["start"], $pause["end"], $meta]); if (!$data) { - Exceptions::error_rep("An error occured while creating an worktime entry. See previous message for more information."); + Exceptions::error_rep("An error occurred 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); + EventDispatcherService::get()->dispatch(new WorktimeAddedEvent($username, ["start" => $start, "end" => $end], $Wtype), WorktimeAddedEvent::NAME); Exceptions::error_rep("Worktime entry for user '{$username}' created successfully."); return true; } } } + public static function type_from_int($int){ + $types = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . self::get_app_ini()["config"]["worktime_types"]), true); + if (isset($types[$int])) { + return $types[$int]; + } else { + Exceptions::error_rep("An error occurred while getting worktime type from int. The type does not exist. Returning default"); + return $types[0]; + } + + } + + public static function get_all_types(){ + $types = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . self::get_app_ini()["config"]["worktime_types"]), true); + if (isset($types)) { + return $types; + } else { + Exceptions::error_rep("An error occurred while getting worktime types. The types do not exist. Returning default"); + return []; + } + } + /** * get_app_ini - Reads the app.json file * @@ -480,6 +507,7 @@ public function get_specific_worktime_html(int $month, int $year) $rum = $row["ort"]; $rqw = $row["id"]; $rbn = $row["username"]; + $rtn = $this->type_from_int($row["Wtype"]) ?? "N/A"; $rps = @strftime("%H:%M", strtotime($row["pause_start"])); $rpe = @strftime("%H:%M", strtotime($row["pause_end"])); @@ -508,6 +536,7 @@ public function get_specific_worktime_html(int $month, int $year) $rps $rpe $rum $rno + $rtn @@ -544,6 +573,7 @@ public function get_employee_worktime_html($username) $rol = $row["schicht_ende"]; $rum = $row["ort"]; $rqw = $row["id"]; + $rtn = $this->type_from_int($row["Wtype"]) ?? "N/A"; $rps = @strftime("%H:%M", strtotime($row["pause_start"])); $rpe = @strftime("%H.%M", strtotime($row["pause_end"])); @@ -570,6 +600,7 @@ public function get_employee_worktime_html($username) $rps $rpe $rum $rno + $rtn @@ -594,7 +625,7 @@ public function mark_for_review($id) $sql = "UPDATE `arbeitszeiten` SET `review` = '1' WHERE `id` = ?;"; $res = $this->db->sendQuery($sql)->execute([$id]); if (!$res) { - Exceptions::error_rep("An error occured while marking an worktime as under review, id '{$id}'. See previous message for more information."); + Exceptions::error_rep("An error occurred 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); @@ -611,7 +642,7 @@ public function unlock_for_review($id) $sql = "UPDATE `arbeitszeiten` SET `review` = '0' WHERE `id` = ?;"; $res = $this->db->sendQuery($sql)->execute([$id]); if (!$res) { - Exceptions::error_rep("An error occured while unlocking an worktime from review, id '{$id}'. See previous message for more information."); + Exceptions::error_rep("An error occurred 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); @@ -619,97 +650,6 @@ public function unlock_for_review($id) } } - public static function check_status_code($url) - { - $i18n = new i18n; - $loc = $i18n->loadLanguage(null, "status"); - if (strpos($url, "info=worktime_deleted")) { - return "

{$loc["worktime_deleted"]}

"; - } - if (strpos($url, "info=logged_out")) { - return "

{$loc["logged_out"]}

"; - } - if (strpos($url, "info=error_sickness")) { - return "

{$loc["error_sickness"]}

"; - } - if (strpos($url, "info=error_vacation")) { - return "

{$loc["error_vacation"]}

"; - } - if (strpos($url, "info=vacation_added")) { - return "

{$loc["vacation_added"]}

"; - } - if (strpos($url, "info=password_reset")) { - return "

{$loc["password_reset"]}

"; - } - if (strpos($url, "info=sickness_added")) { - return "

{$loc["sickness_added"]}

"; - } - if (strpos($url, "info=notifications_entry_deleted")) { - return "

{$loc["notifications_entry_deleted"]}

"; - } - if (strpos($url, "info=noperms")) { - return "

{$loc["noperms"]}

"; - } - if (strpos($url, "info=user_deleted")) { - return "

{$loc["user_deleted"]}

"; - } - if (strpos($url, "info=created_user")) { - return "

{$loc["created_user"]}

"; - } - if (strpos($url, "info=worktime_added")) { - return "

{$loc["worktime_added"]}

"; - } - if (strpos($url, "info=password_changed")) { - return "

{$loc["password_changed"]}

"; - } - if (strpos($url, "info=password_change_failed")) { - return "

{$loc["password_change_failed"]}

"; - } - if (strpos($url, "error=nodata")) { - return "

{$loc["nodata"]}

"; - } - if (strpos($url, "info=statemismatch")) { - return "

{$loc["statemismatch"]}

"; - } - if (strpos($url, "error=wrongdata")) { - return "

{$loc["wrongdata"]}

"; - } - if (strpos($url, "error=ldapauth")) { - return "

{$loc["ldapauth"]}

"; - } - if (strpos($url, "info=ldapcreated")) { - return "

{$loc["ldapcreated"]}

"; - } - if (strpos($url, "info=worktime_review")) { - return "

{$loc["worktime_review"]}

"; - } - if (strpos($url, "info=worktime_review_unlock")) { - return "

{$loc["worktime_review_unlock"]}

"; - } - if (strpos($url, "info=worktime_easymode_start")) { - return "

{$loc["worktime_easymode_start"]}

"; - } - if (strpos($url, "info=worktime_easymode_end")) { - return "

{$loc["worktime_easymode_end"]}

"; - } - if (strpos($url, "info=worktime_easymode_pause_start")) { - return "

{$loc["worktime_easymode_pause_start"]}

"; - } - if (strpos($url, "info=worktime_easymode_pause_end")) { - return "

{$loc["worktime_easymode_pause_end"]}

"; - } - if (strpos($url, "info=easymode_toggled")) { - return "

{$loc["easymode_toggled"]}

"; - } - if (strpos($url, "info=error")) { - return "

{$loc["error"]}

"; - } - if (strpos($url, "info=notification_not_found")) { - return "

{$loc["notification_not_found"]}

"; - } - - } - public function calculate_hours_specific_time($username, $month, $year) { Exceptions::error_rep("Calculating hours for user '{$username}' in month '{$month}' and year '{$year}'..."); @@ -883,6 +823,13 @@ public function nodes(): Nodes $this->nodes = new Nodes; return $this->nodes; } + + public function statusMessages(): StatusMessages + { + if (!$this->statusMessages) + $this->statusMessages = new StatusMessages; + return $this->statusMessages; + } } } diff --git a/api/v1/class/auth/auth.arbeit.inc.php b/api/v1/class/auth/auth.arbeit.inc.php index e7163c4..e56169d 100644 --- a/api/v1/class/auth/auth.arbeit.inc.php +++ b/api/v1/class/auth/auth.arbeit.inc.php @@ -23,6 +23,7 @@ public static function login($username, $password, $option){ # "option"-> array Exceptions::error_rep("Logging in user '$username'..."); $db = new DB; + $sM = new StatusMessages; session_start(); $ini = Arbeitszeit::get_app_ini(); $base_url = $ini["general"]["base_url"]; @@ -32,7 +33,7 @@ public static function login($username, $password, $option){ # "option"-> array 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")); + die(header("Location: http://{$ini["general"]["base_url"]}/suite/login.php?" . $sM->URIBuilder("ldapauth"))); } else { Exceptions::error_rep("Successfully authenticated user '" . $username . "' - LDAP Auth"); $ldap = true; @@ -41,7 +42,7 @@ public static function login($username, $password, $option){ # "option"-> array 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")); + die(header("Location: http://{$ini["general"]["base_url"]}/suite/login.php?" . $sM->URIBuilder("nodata"))); } else { $sql = "SELECT * FROM users WHERE username = ?;"; $res = $db->sendQuery($sql); @@ -50,14 +51,14 @@ public static function login($username, $password, $option){ # "option"-> array 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")); + die(header("Location: http://{$ini["general"]["base_url"]}/suite/login.php?" . $sM->URIBuilder("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")); + die(header("Location: http://{$ini["general"]["base_url"]}/suite/login.php?" . $sM->URIBuilder("nodata"))); } $data = $res->fetch(\PDO::FETCH_ASSOC); @@ -135,7 +136,7 @@ public static function login($username, $password, $option){ # "option"-> array } 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")); + die(header("Location: http://{$base_url}/suite/login.php?" . $sM->URIBuilder("wrongdata"))); } } } @@ -153,13 +154,13 @@ public function login_validation(){ 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"); + header("Location: http://{$baseurl}/suite/login.php?" . $this->statusMessages()->URIBuilder("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"); + header("Location: http://{$baseurl}/suite/login.php?" . $this->statusMessages()->URIBuilder("statemismatch")); } } 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 f32e323..de5b8a4 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 @@ -49,8 +49,8 @@ public static function authenticate($username, $password){ $response = self::get_bind()->getConnection()->execute($operation); $code1 = null; if(!$response->isAuthenticated()){ - $code = $response->getErrorCode(); $code = $code1; + $code = $response->getErrorCode(); switch($code){ case "1317": Exceptions::error_rep("Could not authenticate user '{$username}': Account does not exist."); @@ -106,16 +106,20 @@ public static function authenticate($username, $password){ Exceptions::error_rep("Could not authenticate user '{$username}': User not found in DB."); if(Arbeitszeit::get_app_ini()["ldap"]["create_user"] == "true"){ $benutzer = new Benutzer; - Exceptions::error_rep("User authenticated but not existent locally '{$username}': Trying to create user istead..."); + Exceptions::error_rep("User authenticated but not existent locally '{$username}': Trying to create user instead..."); if($user->get("mail") == ""){ Exceptions::error_rep("Could not authenticate user '{$username}': User email in LDAP not set!"); return false; } + define("SYSTEM_MODE", true); if(!$benutzer->create_user($username, "" . $user->get("firstName") . " " . $user->get("lastName"), $user->get("mail"), hash('sha256', $user->get("sid"). $user->get("upn")), 0)){ Exceptions::error_rep("Could not authenticate user '{$username}': Could not create user in DB."); return false; } else { - header("Location: http://". Arbeitszeit::get_app_ini()["general"]["base_url"]."/suite/?info=ldapcreated"); + $statusMessages = new StatusMessages; + $uri = $statusMessages->URIBuilder("ldapcreated"); + header("Location: http://". Arbeitszeit::get_app_ini()["general"]["base_url"]."/suite/?" . $uri); + exit; } } return false; diff --git a/api/v1/class/benutzer/benutzer.arbeit.inc.php b/api/v1/class/benutzer/benutzer.arbeit.inc.php index aba0f34..d7f076a 100644 --- a/api/v1/class/benutzer/benutzer.arbeit.inc.php +++ b/api/v1/class/benutzer/benutzer.arbeit.inc.php @@ -33,7 +33,7 @@ public function create_user($username, $name, $email, $password, $isAdmin = 0) $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"); + Exceptions::error_rep("An error occurred while creating a user. See previous message for more information"); return [ "error" => [ "error_code" => 3, @@ -67,7 +67,7 @@ public function delete_user($id) $sql = "DELETE FROM `users` WHERE id = ?;"; $data = $this->db->sendQuery($sql)->execute([$id]); if ($data == false) { - Exceptions::error_rep("An error occured while deleting an user. See previous message for more information"); + Exceptions::error_rep("An error occurred while deleting an user. See previous message for more information"); return [ "error" => [ "error_code" => 4, @@ -254,7 +254,7 @@ public function get_user_html($username) $base_url = $ini = $this->get_app_ini()["general"]["base_url"]; $data = $this->get_user($username); if ($data == false) { - Exceptions::error_rep("An error occured while generating user html. User '$username' is either not logged in or the session expired."); + Exceptions::error_rep("An error occurred while generating user html. User '$username' is either not logged in or the session expired."); return "

{$this->i18n["unknown_error"]}

"; } while ($data) { diff --git a/api/v1/class/db/db.arbeit.inc.php b/api/v1/class/db/db.arbeit.inc.php index e44a8d0..cdef3a3 100644 --- a/api/v1/class/db/db.arbeit.inc.php +++ b/api/v1/class/db/db.arbeit.inc.php @@ -40,7 +40,7 @@ public function sendQuery($sql){ Exceptions::error_rep("[DB] Query prepared successfully."); return $st; } catch (PDOException $e){ - Exceptions::error_rep("[DB] An error occured while performing a query. | Error message: " . $e->getMessage()); + Exceptions::error_rep("[DB] An error occurred while performing a query. | Error message: " . $e->getMessage()); return false; } } @@ -51,7 +51,7 @@ public function simpleQuery($sql){ Exceptions::error_rep("[DB] Query prepared successfully."); return $pdo->query($sql); } catch (PDOException $e){ - Exceptions::error_rep("[DB] An error occured while performing a simple query. | Error message: " . $e->getMessage()); + Exceptions::error_rep("[DB] An error occurred while performing a simple query. | Error message: " . $e->getMessage()); return false; } } diff --git a/api/v1/class/events/worktimes/WorktimeAddedEvent.php b/api/v1/class/events/worktimes/WorktimeAddedEvent.php index b686454..80f9543 100644 --- a/api/v1/class/events/worktimes/WorktimeAddedEvent.php +++ b/api/v1/class/events/worktimes/WorktimeAddedEvent.php @@ -10,9 +10,12 @@ class WorktimeAddedEvent extends Event private string $username; private array $dates; - public function __construct(string $username, array $dates = []){ + private string $Wtype; + + public function __construct(string $username, array $dates = [], int $Wtype){ $this->username = $username; $this->dates = $dates; + $this->Wtype = $Wtype; } public function getUsername(): string @@ -24,4 +27,9 @@ public function getDates(): array { return $this->dates; } + + public function getWtype(): string + { + return $this->Wtype; + } } diff --git a/api/v1/class/exceptions/exceptions.arbeit.inc.php b/api/v1/class/exceptions/exceptions.arbeit.inc.php index db19e5b..dcb19eb 100644 --- a/api/v1/class/exceptions/exceptions.arbeit.inc.php +++ b/api/v1/class/exceptions/exceptions.arbeit.inc.php @@ -1,32 +1,37 @@ error_rep($message); parent::__construct($message, $code, $previous); } - public function __toString(): string{ - return __CLASS__ . ": [($this->code}]: {$this->message}\n"; + public function __toString(): string + { + return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; } - public static function error_rep($message, $method = NULL){ + public static function error_rep($message, $method = NULL) + { $error_file = self::getSpecificLogFilePath(); // file on your fs, e.g. /var/www/html/error.log $version = @file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/VERSION"); //optional value - if($method == NULL){ + if ($method == NULL) { $method = $_SERVER["REQUEST_METHOD"]; } $time = date("[d.m.Y | H:i:s]"); error_log("{$time} \"{$message}\"\nURL: {$_SERVER["HTTP_HOST"]}{$_SERVER["REQUEST_URI"]} \nVersion: {$version} - Server Name: {$_SERVER["SERVER_NAME"]} - Request Method: '{$method}'\nRemote Address: {$_SERVER["REMOTE_ADDR"]} - Remote Port: {$_SERVER["REMOTE_PORT"]} - Script Name: '{$_SERVER["SCRIPT_FILENAME"]}'\n=======================\n", 3, $error_file); - + } - public static function getSpecificLogFilePath($date = null){ - if($date == null){ + public static function getSpecificLogFilePath($date = null) + { + if ($date == null) { $date = date("Y-m-d"); return $_SERVER["DOCUMENT_ROOT"] . "/data/logs/log-{$date}.log"; } else { preg_match("/^\d{4}-\d{2}-\d{2}$/m", $date, $match); - if($match[0] == null){ + if ($match[0] == null) { self::getSpecificLogFilePath(); } Exceptions::error_rep("Trying to get log file for date '$date'"); @@ -34,19 +39,41 @@ public static function getSpecificLogFilePath($date = null){ } } - public static function failure($code, $error, $stack){ - Exceptions::error_rep("[EXCEPTIONS] A critical error occured. | Message: " . $error); + public static function failure($code, $error, $stack) + { + Exceptions::error_rep("[EXCEPTIONS] A critical error occurred. | Message: " . $error); $parms = http_build_query(array("code" => $code, "error" => $error, "stack" => base64_encode($stack))); header("Location: /errors/500.php?$parms"); } - public static function deprecated($function_name, $additional_message){ + public static function deprecated($function_name, $additional_message) + { $message = "The function '{$function_name}' is deprecated. {$additional_message}"; trigger_error($message, E_USER_DEPRECATED); Exceptions::error_rep($message); return $message; } + + public static function getLastLines($filePath, $lines = 100) + { + if (!is_readable($filePath)) + return "Error retrieving log file!"; + + $file = new \SplFileObject($filePath, 'r'); + $file->seek(PHP_INT_MAX); + $lastLine = $file->key(); + + $linesArr = []; + for ($i = max(0, $lastLine - $lines); $i <= $lastLine; $i++) { + $file->seek($i); + $linesArr[] = $file->current(); + } + + return implode("", $linesArr); + } + + } } diff --git a/api/v1/class/exports/modules/CSVExportModule/CSVExportModule.em.arbeit.inc.php b/api/v1/class/exports/modules/CSVExportModule/CSVExportModule.em.arbeit.inc.php index 28d4c4c..c35e40c 100644 --- a/api/v1/class/exports/modules/CSVExportModule/CSVExportModule.em.arbeit.inc.php +++ b/api/v1/class/exports/modules/CSVExportModule/CSVExportModule.em.arbeit.inc.php @@ -24,7 +24,7 @@ public function export($args) { $year = date("Y"); } - $sql = "SELECT id, username, schicht_tag, schicht_anfang, schicht_ende, ort, pause_start, pause_end + $sql = "SELECT id, username, schicht_tag, schicht_anfang, schicht_ende, ort, pause_start, pause_end, Wtype FROM `arbeitszeiten` WHERE YEAR(schicht_tag) = ? AND MONTH(schicht_tag) = ? AND username = ? ORDER BY schicht_tag DESC"; @@ -47,7 +47,7 @@ public function export($args) { $output = fopen('php://output', 'w'); // Set columns - $columns = ["ID", "Username", "Shift Date", "Shift Start", "Shift End", "Location/Notes", "Pause Start", "Pause End"]; + $columns = ["ID", "Username", "Shift Date", "Shift Start", "Shift End", "Location/Notes", "Pause Start", "Pause End", "Type"]; fputcsv($output, $columns, ';'); // Datenzeilen in CSV schreiben @@ -69,7 +69,7 @@ public function saveAsCsv($args) { $year = date("Y"); } - $sql = "SELECT id, username, schicht_tag, schicht_anfang, schicht_ende, ort, pause_start, pause_end + $sql = "SELECT id, username, schicht_tag, schicht_anfang, schicht_ende, ort, pause_start, pause_end, Wtype FROM `arbeitszeiten` WHERE YEAR(schicht_tag) = ? AND MONTH(schicht_tag) = ? AND username = ? ORDER BY schicht_tag DESC"; @@ -91,7 +91,7 @@ public function saveAsCsv($args) { $output = fopen($filename, 'w'); - $columns = ["ID", "Username", "Shift Date", "Shift Start", "Shift End", "Location/Notes", "Pause Start", "Pause End"]; + $columns = ["ID", "Username", "Shift Date", "Shift Start", "Shift End", "Location/Notes", "Pause Start", "Pause End", "Type"]; fputcsv($output, $columns, ';'); foreach ($data as $row) { diff --git a/api/v1/class/exports/modules/PDFExportModule/PDFExportModule.em.arbeit.inc.php b/api/v1/class/exports/modules/PDFExportModule/PDFExportModule.em.arbeit.inc.php index 2d301c1..2fe1675 100644 --- a/api/v1/class/exports/modules/PDFExportModule/PDFExportModule.em.arbeit.inc.php +++ b/api/v1/class/exports/modules/PDFExportModule/PDFExportModule.em.arbeit.inc.php @@ -25,8 +25,8 @@ public function export($args) { $statement = $arbeit->db()->sendQuery($sql); $userdata = $statement->execute([$year, $month, $user]); if($userdata == false){ - Exceptions::error_rep("An error occured while generating worktime pdf. See previous message for more information"); - die("An error occured!"); + Exceptions::error_rep("An error occurred while generating worktime pdf. See previous message for more information"); + die("An error occurred!"); } $user_data = Benutzer::get_user($user); $user_data["name"] ?? $statement->fetch(\PDO::FETCH_ASSOC)[0]["name"]; # Bug 14 Fix -> http://bugzilla.openducks.org/show_bug.cgi?id=14 @@ -51,6 +51,7 @@ public function export($args) { {$i18nn["pbegin"]} {$i18nn["pend"]} {$i18nn["loc"]} + {$i18nn["type"]} DATA; @@ -68,6 +69,7 @@ public function export($args) { $rew = $row["schicht_anfang"]; $rol = $row["schicht_ende"]; $ral = $row["ort"]; + $rtn = $arbeit->type_from_int($row["Wtype"]) ?? "N/A"; $rps = @strftime("%H:%M", strtotime($row["pause_start"])); $rpe = @strftime("%H:%M", strtotime($row["pause_end"])); @@ -88,6 +90,7 @@ public function export($args) { {$rps} {$rpe} {$ral} + {$rtn} diff --git a/api/v1/class/i18n/admin/users/settings/snippets_DE.json b/api/v1/class/i18n/admin/users/settings/snippets_DE.json index 33acbf3..33a5e9e 100644 --- a/api/v1/class/i18n/admin/users/settings/snippets_DE.json +++ b/api/v1/class/i18n/admin/users/settings/snippets_DE.json @@ -7,5 +7,5 @@ "placeholder_base_url": "sub.domain.tld:", "button_text": "Abschicken", "log_title": "Log-Datei", - "log_p1": "Unten siehst du den Inhalt der heutigen Log-Datei..." + "log_p1": "Unten siehst du den Inhalt der heutigen Log-Datei... (letzten 200 Zeilen)" } \ No newline at end of file diff --git a/api/v1/class/i18n/admin/users/settings/snippets_EN.json b/api/v1/class/i18n/admin/users/settings/snippets_EN.json index fa88a29..1cb3c84 100644 --- a/api/v1/class/i18n/admin/users/settings/snippets_EN.json +++ b/api/v1/class/i18n/admin/users/settings/snippets_EN.json @@ -7,5 +7,5 @@ "placeholder_base_url": "sub.domain.tld:", "button_text": "Submit", "log_title": "Log file", - "log_p1": "See the contents of the log file from today below..." + "log_p1": "See the contents of the log file from today below...(last 200 lines)" } \ No newline at end of file diff --git a/api/v1/class/i18n/admin/users/settings/snippets_NL.json b/api/v1/class/i18n/admin/users/settings/snippets_NL.json index 1943119..ecc73bb 100644 --- a/api/v1/class/i18n/admin/users/settings/snippets_NL.json +++ b/api/v1/class/i18n/admin/users/settings/snippets_NL.json @@ -7,5 +7,5 @@ "placeholder_base_url": "sub.domein.tld:", "button_text": "Verzenden", "log_title": "Logbestand", - "log_p1": "Hieronder kunt u de inhoud van het logbestand van vandaag zien..." + "log_p1": "Hieronder kunt u de inhoud van het logbestand van vandaag zien...(laatste 200 regels)" } \ No newline at end of file diff --git a/api/v1/class/i18n/admin/worktime/all/snippets_DE.json b/api/v1/class/i18n/admin/worktime/all/snippets_DE.json index 6db04ff..bbce20a 100644 --- a/api/v1/class/i18n/admin/worktime/all/snippets_DE.json +++ b/api/v1/class/i18n/admin/worktime/all/snippets_DE.json @@ -12,5 +12,6 @@ "send": "Schicht Ende", "pbegin": "Pause Start", "pend": "Pause Ende", - "loc": "Ort" + "loc": "Ort", + "type": "Typ" } \ No newline at end of file diff --git a/api/v1/class/i18n/admin/worktime/all/snippets_EN.json b/api/v1/class/i18n/admin/worktime/all/snippets_EN.json index 1c34cbc..b83e6de 100644 --- a/api/v1/class/i18n/admin/worktime/all/snippets_EN.json +++ b/api/v1/class/i18n/admin/worktime/all/snippets_EN.json @@ -12,5 +12,6 @@ "send": "end of shift", "pbegin": "Break Start", "pend": "end of break", - "loc": "location" + "loc": "location", + "type": "type" } \ No newline at end of file diff --git a/api/v1/class/i18n/admin/worktime/all/snippets_NL.json b/api/v1/class/i18n/admin/worktime/all/snippets_NL.json index d467e58..3b6cc58 100644 --- a/api/v1/class/i18n/admin/worktime/all/snippets_NL.json +++ b/api/v1/class/i18n/admin/worktime/all/snippets_NL.json @@ -12,5 +12,6 @@ "send": "einde dienst", "pbegin": "Breekstart", "pend": "einde einde", - "loc": "locatie" + "loc": "locatie", + "type": "type" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_DE.json b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_DE.json index f707ad2..0a527bf 100644 --- a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_DE.json +++ b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_DE.json @@ -7,5 +7,6 @@ "csv": "(CSV)", "delete_entry": "Eintrag löschen", "no_shifts": "Keine Einträge gefunden.", - "or": "oder" + "or": "oder", + "type": "Typ" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_EN.json b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_EN.json index 821130d..ab76592 100644 --- a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_EN.json +++ b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_EN.json @@ -7,5 +7,6 @@ "csv": "(CSV)", "delete_entry": "Delete entry", "no_shifts": "No entries found.", - "or": "or" + "or": "or", + "type": "Type" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_NL.json b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_NL.json index 71540d8..f0c8a8c 100644 --- a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_NL.json +++ b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_NL.json @@ -7,5 +7,6 @@ "csv": "(CSV)", "delete_entry": "Invoer verwijderen", "no_shifts": "Geen vermeldingen gevonden.", - "or": "of" + "or": "of", + "type": "Type" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/class/pdf/snippets_DE.json b/api/v1/class/i18n/suite/class/pdf/snippets_DE.json index 2400267..78b0fc6 100644 --- a/api/v1/class/i18n/suite/class/pdf/snippets_DE.json +++ b/api/v1/class/i18n/suite/class/pdf/snippets_DE.json @@ -9,5 +9,6 @@ "no_data": "Keine Daten gefunden!", "worktime_all": "Arbeitszeit insgesamt (gerundet)", "worktime_date": "Anfangsdatum der Daten", - "end_date": "Enddatum" + "end_date": "Enddatum", + "type": "Typ" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/class/pdf/snippets_EN.json b/api/v1/class/i18n/suite/class/pdf/snippets_EN.json index 11426df..98acb3a 100644 --- a/api/v1/class/i18n/suite/class/pdf/snippets_EN.json +++ b/api/v1/class/i18n/suite/class/pdf/snippets_EN.json @@ -9,5 +9,6 @@ "no_data": "No data found!", "worktime_all": "Total working time (rounded)", "worktime_date": "Start date of the data", - "end_date": "End date" + "end_date": "End date", + "type": "Type" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/class/pdf/snippets_NL.json b/api/v1/class/i18n/suite/class/pdf/snippets_NL.json index 71d8308..bbbb6ac 100644 --- a/api/v1/class/i18n/suite/class/pdf/snippets_NL.json +++ b/api/v1/class/i18n/suite/class/pdf/snippets_NL.json @@ -9,5 +9,6 @@ "no_data": "Geen gegevens gevonden!", "worktime_all": "Totale werktijd (afgerond)", "worktime_date": "Begindatum van gegevens", - "end_date": "Einddatum" + "end_date": "Einddatum", + "type": "Type" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/mode/easymode/snippets_DE.json b/api/v1/class/i18n/suite/mode/easymode/snippets_DE.json index a9ce40c..c74e562 100644 --- a/api/v1/class/i18n/suite/mode/easymode/snippets_DE.json +++ b/api/v1/class/i18n/suite/mode/easymode/snippets_DE.json @@ -18,5 +18,6 @@ "pend": "Pause Ende", "submit": "Absenden", "h_vacation": "Buche deinen Urlaub", - "h_sickness": "Krankheit eintragen" + "h_sickness": "Krankheit eintragen", + "type": "Typ" } diff --git a/api/v1/class/i18n/suite/mode/easymode/snippets_EN.json b/api/v1/class/i18n/suite/mode/easymode/snippets_EN.json index cac2cb7..18f0588 100644 --- a/api/v1/class/i18n/suite/mode/easymode/snippets_EN.json +++ b/api/v1/class/i18n/suite/mode/easymode/snippets_EN.json @@ -18,5 +18,6 @@ "pend": "end of break", "submit": "Send", "h_vacation": "Request vacation", - "h_sickness": "Report sickness" + "h_sickness": "Report sickness", + "type": "Type" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/mode/easymode/snippets_NL.json b/api/v1/class/i18n/suite/mode/easymode/snippets_NL.json index b61e99b..8832f8e 100644 --- a/api/v1/class/i18n/suite/mode/easymode/snippets_NL.json +++ b/api/v1/class/i18n/suite/mode/easymode/snippets_NL.json @@ -18,5 +18,6 @@ "pend": "einde einde", "submit": "Verzenden", "h_vacation": "Boek uw vakantie", - "h_sickness": "Voer ziekte in" + "h_sickness": "Voer ziekte in", + "type": "Type" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/worktime/all/snippets_DE.json b/api/v1/class/i18n/suite/worktime/all/snippets_DE.json index 43bb2d1..c52053f 100644 --- a/api/v1/class/i18n/suite/worktime/all/snippets_DE.json +++ b/api/v1/class/i18n/suite/worktime/all/snippets_DE.json @@ -6,5 +6,6 @@ "s_end": "Schicht Ende", "s_pstart": "Pause Start", "s_pend": "Pause Ende", - "s_location": "Ort" + "s_location": "Ort", + "type": "Typ" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/worktime/all/snippets_EN.json b/api/v1/class/i18n/suite/worktime/all/snippets_EN.json index 52e4bf7..5c2322a 100644 --- a/api/v1/class/i18n/suite/worktime/all/snippets_EN.json +++ b/api/v1/class/i18n/suite/worktime/all/snippets_EN.json @@ -6,5 +6,6 @@ "s_end": "Shift End", "s_pstart": "Pause Start", "s_pend": "Pause End", - "s_location": "Location" + "s_location": "Location", + "type": "Type" } \ No newline at end of file diff --git a/api/v1/class/i18n/suite/worktime/all/snippets_NL.json b/api/v1/class/i18n/suite/worktime/all/snippets_NL.json index 51b02e0..b45eabf 100644 --- a/api/v1/class/i18n/suite/worktime/all/snippets_NL.json +++ b/api/v1/class/i18n/suite/worktime/all/snippets_NL.json @@ -6,5 +6,6 @@ "s_end": "Einde dienst", "s_pstart": "Start pauzeren", "s_pend": "Einde einde", - "s_location": "Locatie" + "s_location": "Locatie", + "type": "Type" } \ No newline at end of file diff --git a/api/v1/class/mails/Mails.md b/api/v1/class/mails/Mails.md index b2d1f45..7804b3f 100644 --- a/api/v1/class/mails/Mails.md +++ b/api/v1/class/mails/Mails.md @@ -2,7 +2,7 @@ ## Developer: How to send mails -Currently you do have to make sure that your custom provider is loaded before you can send mails. Simply require your custom provider and follow belows code example to send a mail. In the future you should be able to register a provider aswell and autoload it. +Currently you do have to make sure that your custom provider is loaded before you can send mails. Simply require your custom provider and follow the code example below to send a mail. In the future you should be able to register a provider as well and autoload it. ```php diff --git a/api/v1/class/mails/templates/NewUserTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/NewUserTemplate.mails.arbeit.inc.php index 93fe397..ed64559 100644 --- a/api/v1/class/mails/templates/NewUserTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/NewUserTemplate.mails.arbeit.inc.php @@ -23,7 +23,7 @@ public function render(array $data): MailTemplateData { if($count == 1){ $data = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return false; } $subject = "{$loc["subject"]} | {$ii}"; diff --git a/api/v1/class/mails/templates/PasswordChangedTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/PasswordChangedTemplate.mails.arbeit.inc.php index 971057a..71ed7bb 100644 --- a/api/v1/class/mails/templates/PasswordChangedTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/PasswordChangedTemplate.mails.arbeit.inc.php @@ -22,7 +22,7 @@ public function render(array $data): MailTemplateData { if($count == 1){ $data = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, diff --git a/api/v1/class/mails/templates/PasswordResetTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/PasswordResetTemplate.mails.arbeit.inc.php index 67d558e..099a75e 100644 --- a/api/v1/class/mails/templates/PasswordResetTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/PasswordResetTemplate.mails.arbeit.inc.php @@ -23,7 +23,7 @@ public function render(array $data): MailTemplateData { if($count == 1){ $data = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, diff --git a/api/v1/class/mails/templates/PasswordSendTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/PasswordSendTemplate.mails.arbeit.inc.php index 0bdd29f..b53baa7 100644 --- a/api/v1/class/mails/templates/PasswordSendTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/PasswordSendTemplate.mails.arbeit.inc.php @@ -24,7 +24,7 @@ public function render(array $data): MailTemplateData { if($count == 1){ $data = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, diff --git a/api/v1/class/mails/templates/SicknessApprovedTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/SicknessApprovedTemplate.mails.arbeit.inc.php index c9066f8..f528a2d 100644 --- a/api/v1/class/mails/templates/SicknessApprovedTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/SicknessApprovedTemplate.mails.arbeit.inc.php @@ -25,7 +25,7 @@ public function render(array $data): MailTemplateData if ($count == 1) { $data1 = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, @@ -40,7 +40,7 @@ public function render(array $data): MailTemplateData if ($res1) { $worktime_data = $res1->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching sickness data from database for id '{$data["id"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching sickness data from database for id '{$data["id"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 11, diff --git a/api/v1/class/mails/templates/SicknessPendingTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/SicknessPendingTemplate.mails.arbeit.inc.php index e86d364..3715254 100644 --- a/api/v1/class/mails/templates/SicknessPendingTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/SicknessPendingTemplate.mails.arbeit.inc.php @@ -23,7 +23,7 @@ public function render(array $data): MailTemplateData { if ($count == 1) { $data1 = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, @@ -38,7 +38,7 @@ public function render(array $data): MailTemplateData { if ($res1 != false) { $worktime_data = $res1->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching sickness data from database for id '{$data["id"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching sickness data from database for id '{$data["id"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 11, diff --git a/api/v1/class/mails/templates/SicknessRejectedTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/SicknessRejectedTemplate.mails.arbeit.inc.php index 666961f..9eb24b0 100644 --- a/api/v1/class/mails/templates/SicknessRejectedTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/SicknessRejectedTemplate.mails.arbeit.inc.php @@ -23,7 +23,7 @@ public function render(array $data): MailTemplateData { if ($count == 1) { $data1 = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, @@ -38,7 +38,7 @@ public function render(array $data): MailTemplateData { if ($res1 != false) { $worktime_data = $res1->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching sickness data from database for id '{$data["id"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching sickness data from database for id '{$data["id"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 11, diff --git a/api/v1/class/mails/templates/VacationApprovedTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/VacationApprovedTemplate.mails.arbeit.inc.php index 08b45af..a89ff16 100644 --- a/api/v1/class/mails/templates/VacationApprovedTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/VacationApprovedTemplate.mails.arbeit.inc.php @@ -23,7 +23,7 @@ public function render(array $data): MailTemplateData { if ($count == 1) { $data1 = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, @@ -38,7 +38,7 @@ public function render(array $data): MailTemplateData { if ($res1 != false) { $worktime_data = $res1->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching vacation data from database for id '{$data["id"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching vacation data from database for id '{$data["id"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 11, diff --git a/api/v1/class/mails/templates/VacationPendingTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/VacationPendingTemplate.mails.arbeit.inc.php index 8e60a97..6b029e4 100644 --- a/api/v1/class/mails/templates/VacationPendingTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/VacationPendingTemplate.mails.arbeit.inc.php @@ -23,7 +23,7 @@ public function render(array $data): MailTemplateData { if ($count == 1) { $data1 = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, @@ -38,7 +38,7 @@ public function render(array $data): MailTemplateData { if ($res1 != false) { $worktime_data = $res1->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching vacation data from database for id '{$data["id"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching vacation data from database for id '{$data["id"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 11, diff --git a/api/v1/class/mails/templates/VacationRejectedTemplate.mails.arbeit.inc.php b/api/v1/class/mails/templates/VacationRejectedTemplate.mails.arbeit.inc.php index beaf633..c341b2a 100644 --- a/api/v1/class/mails/templates/VacationRejectedTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/VacationRejectedTemplate.mails.arbeit.inc.php @@ -23,7 +23,7 @@ public function render(array $data): MailTemplateData { if ($count == 1) { $data1 = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, @@ -38,7 +38,7 @@ public function render(array $data): MailTemplateData { if ($res1 != false) { $worktime_data = $res1->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching vacation data from database for id '{$data["id"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching vacation data from database for id '{$data["id"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 11, 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 92afc06..64739eb 100644 --- a/api/v1/class/mails/templates/WorktimeUncompliantTemplate.mails.arbeit.inc.php +++ b/api/v1/class/mails/templates/WorktimeUncompliantTemplate.mails.arbeit.inc.php @@ -25,7 +25,7 @@ public function render(array $data): MailTemplateData if ($count == 1) { $data = $res->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching user data from database for user '{$data["username"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 10, @@ -40,7 +40,7 @@ public function render(array $data): MailTemplateData if ($res1 != false) { $worktime_data = $res1->fetch(\PDO::FETCH_ASSOC); } else { - Exceptions::error_rep("An error occured while fetching worktime data from database for id '{$data["worktime"]}'. See previous message for more information."); + Exceptions::error_rep("An error occurred while fetching worktime data from database for id '{$data["worktime"]}'. See previous message for more information."); return [ "error" => [ "error_code" => 11, diff --git a/api/v1/class/mode/mode.arbeit.inc.php b/api/v1/class/mode/mode.arbeit.inc.php index 44d9bed..cd575c7 100644 --- a/api/v1/class/mode/mode.arbeit.inc.php +++ b/api/v1/class/mode/mode.arbeit.inc.php @@ -16,6 +16,14 @@ public static function check($username){ } } + public static function compute_html_worktime_types(){ + $data = ""; + foreach(Arbeitszeit::get_all_types() as $type => $value){ + $data .= ""; + } + return $data; + } + private static function get_normal_mode_html(){ $nodes = new Nodes; @@ -24,6 +32,7 @@ private static function get_normal_mode_html(){ } $i18n = new i18n; $loc = $i18n->loadLanguage(null, "mode/easymode"); + $t = self::compute_html_worktime_types(); $data = <<< DATA
@@ -44,6 +53,11 @@ private static function get_normal_mode_html(){
+ + +
@@ -80,14 +94,14 @@ private static function get_easymode_html(){ self::get_easymode_html(); } $data = <<< DATA -

An error occured while checking for active easymode entries. Either a connection error to the database or you have multiple entries marked as active. If the problem persists, contact the system administrator!

+

An error occurred while checking for active easymode entries. Either a connection error to the database or you have multiple entries marked as active. If the problem persists, contact the system administrator!

Error-Code: DEM-CHK_FAIL_EM_ENY_AC

DATA; goto skip_to_output; } elseif(!$worktime && $active === true){ - Exceptions::error_rep("An error occured while checking for active easymode entries. Please ask your administrator to remove the current active worktime entry! | Active worktime: " . $worktime); + Exceptions::error_rep("An error occurred while checking for active easymode entries. Please ask your administrator to remove the current active worktime entry! | Active worktime: " . $worktime); $data = <<< DATA -

An error occured while checking for active easymode entries. Please ask your administrator to remove the current active worktime entry!

+

An error occurred while checking for active easymode entries. Please ask your administrator to remove the current active worktime entry!

DATA; } elseif($active == -1){ diff --git a/api/v1/class/nodes/nodes.arbeit.inc.php b/api/v1/class/nodes/nodes.arbeit.inc.php index b16db4c..4da3822 100644 --- a/api/v1/class/nodes/nodes.arbeit.inc.php +++ b/api/v1/class/nodes/nodes.arbeit.inc.php @@ -1,66 +1,84 @@ benutzer()->is_admin($this->benutzer()->get_user($_SESSION["username"])) ? 1 : 0; - $nodeSet = $panel - ? $data["nodes"]["panels"][$file] ?? null + + if ($this->isSystemMode()) { + $userIsAdmin = 1; + } else { + $username = $_SESSION["username"] ?? null; + $user = $this->benutzer()->get_user($username); + $userIsAdmin = $this->benutzer()->is_admin($user) ? 1 : 0; + } + + $nodeSet = $panel + ? $data["nodes"]["panels"][$file] ?? null : $data["nodes"]["functions"][$file] ?? null; - + if (!$nodeSet || !is_array($nodeSet)) { Exceptions::error_rep("checkNode: Invalid node set for $file, expected array, got " . gettype($nodeSet)); return false; } - + $baseKey = $function; $permKey = $function . '_P'; - + $allowed = $nodeSet[$baseKey] ?? false; - + if ($allowed === false) { Exceptions::error_rep("checkNode: Access denied for function $function in file $file"); return false; } - + if (!array_key_exists($permKey, $nodeSet)) { Exceptions::error_rep("checkNode: Permission key $permKey not found in node set for $file"); return $allowed === true; } - + $level = $nodeSet[$permKey]; if (!in_array($level, [0, 1, 2], true)) { Exceptions::error_rep("checkNode: Invalid permission level for $function"); return false; } + if ($level === 2) { - //access granted for public Exceptions::error_rep("checkNode: Access granted for function $function in file $file, user level: $userIsAdmin, required level: $level"); return true; } + if ($level === 1 && $userIsAdmin === 1) { - //access granted for admin user Exceptions::error_rep("checkNode: Access granted for function $function in file $file, user level: $userIsAdmin, required level: $level"); return true; } + if ($level === 0 && ($userIsAdmin === 0 || $userIsAdmin === 1)) { - //acces granted for normal user Exceptions::error_rep("checkNode: Access granted for function $function in file $file, user level: $userIsAdmin, required level: $level"); return true; } + Exceptions::error_rep("checkNode: Access denied for function $function in file $file, user level: $userIsAdmin, required level: $level"); return false; } -} \ No newline at end of file +} diff --git a/api/v1/class/notifications/notifications.arbeit.inc.php b/api/v1/class/notifications/notifications.arbeit.inc.php index 504df72..479e9e9 100644 --- a/api/v1/class/notifications/notifications.arbeit.inc.php +++ b/api/v1/class/notifications/notifications.arbeit.inc.php @@ -33,7 +33,7 @@ public function notifications_delete(){ $sql = "DELETE FROM `kalender` WHERE `datum` < NOW();"; $data = $this->db->sendQuery($sql)->execute(); if($data == false){ - Exceptions::error_rep("An error occured while deleting expired notifications entries. See previous message for more information."); + Exceptions::error_rep("An error occurred while deleting expired notifications entries. See previous message for more information."); return [ "error" => [ "error_code" => 1, @@ -163,7 +163,7 @@ public function get_notifications_entry($id){ Exceptions::error_rep("[NOTIFICATIONS] Found notifications entry with ID '$id'."); return $data; } else { - Exceptions::error_rep("An error occured while getting an notifications entry. Entry could not be found for id '$id'."); + Exceptions::error_rep("An error occurred while getting an notifications entry. Entry could not be found for id '$id'."); return [ "error" => [ "error_code" => 5, @@ -188,7 +188,7 @@ public function create_notifications_entry($time, $date, $location, $comment){ $sql = "INSERT INTO `kalender` (`datum`, `uhrzeit`, `ort`, `notiz`) VALUES (?, ?, ?, ?)"; $res = $this->db->sendQuery($sql)->execute([$date, $time, $location, $comment]); if(!$res){ - Exceptions::error_rep("An error occured while creating an notifications entry. See previous message for more information."); + Exceptions::error_rep("An error occurred while creating an notifications entry. See previous message for more information."); return [ "error" => [ "error_code" => 6, @@ -218,7 +218,7 @@ public function edit_notifications_entry($id, $time, $date, $location, $comment) $sql = "UPDATE `kalender` SET `datum` = ?, `uhrzeit` = ?, `ort` = ?, `notiz` = ? WHERE id = ?;"; $res = $this->db->sendQuery($sql)->execute([$date, $time, $location, $comment, $id]); if(!$res){ - Exceptions::error_rep("An error occured while editing an notifications entry. See previous message for more information."); + Exceptions::error_rep("An error occurred while editing an notifications entry. See previous message for more information."); return [ "error" => [ "error_code" => 8, @@ -244,7 +244,7 @@ public function delete_notifications_entry($id){ $sql = "DELETE FROM `kalender` WHERE id = ?;"; $res = $this->db->sendQuery($sql)->execute([$id]); if(!$res){ - Exceptions::error_rep("An error occured while deleting an notifications entry. See previous message for more information."); + Exceptions::error_rep("An error occurred while deleting an notifications entry. See previous message for more information."); return [ "error" => [ "error_code" => 9, diff --git a/api/v1/class/notifications/plugins/autodelete.notifications.arbeit.inc.php b/api/v1/class/notifications/plugins/autodelete.notifications.arbeit.inc.php index e92c876..ef3d23f 100644 --- a/api/v1/class/notifications/plugins/autodelete.notifications.arbeit.inc.php +++ b/api/v1/class/notifications/plugins/autodelete.notifications.arbeit.inc.php @@ -17,7 +17,7 @@ public function autodelete_obsolete_notifications_entries(){ $sql = "DELETE FROM `kalender` WHERE `datum` < NOW();"; $data = $db->sendQuery($sql)->execute(); if($data == false){ - Exceptions::error_rep("An error occured while autodeleting notifications entries. See previous message for more information."); + Exceptions::error_rep("An error occurred while autodeleting notifications entries. See previous message for more information."); return [ "error" => [ "error_code" => 7, diff --git a/api/v1/class/plugins/docs/Plugins.md b/api/v1/class/plugins/docs/Plugins.md index 146d337..f4aae69 100644 --- a/api/v1/class/plugins/docs/Plugins.md +++ b/api/v1/class/plugins/docs/Plugins.md @@ -15,11 +15,11 @@ Plugins can be restricted to be used by administrators only. TimeTrack will read your plugin's `plugin.yml` and renders the `nav_links` to the `Plugins Page`. You could design your plugin this way, that it only has one `nav_links`. This page then contains all other links, this might help keeping the navigation bar clean. -You can also design your plugin the way you want to. Since `PluginBuilder.plugins.arbeit.inc.php` has `Arbeitszeit` as it's parent class, its functions are inherited aswell. So you are able to communicate with the database aswell. In the future, we are planning to resolve the issue with direct access to the instance data to a plugin through the `permissions` attribute within the `plugin.yml`. +You can also design your plugin the way you want to. Since `PluginBuilder.plugins.arbeit.inc.php` has `Arbeitszeit` as it's parent class, its functions are inherited as well. So you are able to communicate with the database as well. In the future, we are planning to resolve the issue with direct access to the instance data to a plugin through the `permissions` attribute within the `plugin.yml`. A user would go this way to access your plugin: (Select from nav bar) "Plugins" > "[PluginName] View Name" > (your php file) -Plugins are able to work without a navigation bar aswell, like dependencies for other plugins. Just leave the `nav_links` directive empty inside the `plugin.yml` +Plugins are able to work without a navigation bar as well, like dependencies for other plugins. Just leave the `nav_links` directive empty inside the `plugin.yml` ### plugin.yml @@ -45,68 +45,6 @@ These are optional values you can add, which might make things easier: - `required`: An array containing relative paths to the required files, they will then get included within the archive (PHAR) - `nav_links`: If your plugin has a front-end, please specify this attribute. It is stored in key-value pairs, e.g. "Send Message": "views/send-message.php" - Views have to be always in the `/views` folder within your plugin folder -### The .tp1 extension (deprecated) - -The `.tp1` extension is used to gurantee the peristence of the plugins once TimeTrack has used them. - -Every plugin, which is enabled is going to be created within one of the `.tp1` files. It's an acronym for `TimeTrack Plugin v1` - no worries, all plugins are at some point backwards compatible, depending on the `api` keywoard specified. -Everytime the system restarts (which in TimeTrack is triggered once a day) or the `PluginBuilder::initialize_plugins()` function gets used, the plugins are re-read by the system and every `.tp1` file gets replaced. -**Please try to NOT use the same name twice**. Plugins with duplicated names will not get disabled automatically, yet. - -When plugins are memorized, you can re-access them with the `PluginBuilder::unmemorize_plugin($name)` function. There is no need to delete the `.tp1` file. After you want to save your class again, run `memorize_plugin($name)` and the file get's overwritten. -Technically, `.tp1` files are just serialized php classes, so you can also just use as a class. - -Plugin data can also handled on your own way, e.g. by saving them into the `data` folder in any format you'd like. `.tp1` Files only contain the current configuration of your plugin. - ### Permissions While TimeTrack handles permissions in two different ways, plugins can only be viewed by administrators. However, everyone with a link is able to view the plugin views (not the Plugin selection screen itself) - -### Hooks / Callbacks (WIP) - -In general you are able to interact with native functions in two ways: - -- Hooks -- Callbacks - -Hooks and Callbacks are initialized by `Hooks:initialize()` which reads one-time when loading a desired class. - -To remove either a hook or a callback, simply run e.g. `Hooks::removeHook('create_user', 'after', [$this, 'internalCallbackFunction'])` - -#### Hooks - -Hooks allow you to either execute code before or after the function has ran. This allows you to inject code within the native function. - -You can register a hook within your plugin this way: - -```php -Hooks::addHook('create_user', 'before', function(callable $originalFunction, $username, $name, $email, $password, $isAdmin){ - echo "Something before creating the user"; -}); -... -Hooks::addHook('create_user', 'after', [$this, 'internalCallbackFunction']); -``` - -Hooks also allow a special type `around`. This allows the callback functionality while still being able to inject your own code but without running `after` hooks. - -This allows you to perform some checks before a user has been created or whatever else. - -#### Callbacks (WIP) - -Callbacks allow you to also execute a custom function after the native code, but it doesn't allows you to inject anything into it as it's just a callback. Callbacks might be also referred to as "simple Hooks". - -You can register a callback like this: - -```php -Hooks::addHook('create_user', 'callback', function($username, $name, $email, $password, $isAdmin){ - echo "User created: $username"; -}); - -... -Hooks::addHook('create_user', 'callback', [$this, 'internalCallbackFunction']); -``` - -You could likely use this to redirect your user to a custom page of your plugin or to proceed your own data. Callbacks can only be registered by one. If a plugin has registered the callback address, no other plugin would be able to overwrite it or to register it on its own. - -(`internalCallbackFunction` would be the name of a function you have defined within your class.) -You might have to open a plugin the first time to get the hook or callback registered. diff --git a/api/v1/class/plugins/plugins/codeclock/plugin.yml b/api/v1/class/plugins/plugins/codeclock/plugin.yml index 4e3cfaa..402ff57 100644 --- a/api/v1/class/plugins/plugins/codeclock/plugin.yml +++ b/api/v1/class/plugins/plugins/codeclock/plugin.yml @@ -4,7 +4,7 @@ main: Main namespace: CodeClock author: Ente description: Clock in to work with a PIN -version: "1.0" +version: "1.1.1" api: 0.1 permissions: none enabled: false diff --git a/api/v1/class/plugins/plugins/codeclock/src/Setup.php b/api/v1/class/plugins/plugins/codeclock/src/Setup.php index bec2291..9109e3f 100644 --- a/api/v1/class/plugins/plugins/codeclock/src/Setup.php +++ b/api/v1/class/plugins/plugins/codeclock/src/Setup.php @@ -27,9 +27,14 @@ public static function done(): bool{ // create PIN 4-numeric file for each user $users = $arbeit->benutzer()->get_all_users(); + $existingPins = []; foreach($users as $user){ Exceptions::error_rep("Creating PIN file for user {$user["username"]}"); $pinString = (string)random_int(1000, 9999); + do { + $pinString = (string) random_int(1000, 9999); + } while (in_array($pinString, $existingPins)); + $existingPins[] = $pinString; file_put_contents(dirname(__DIR__) . "/data/pin/{$user["username"]}_c", $pinString); $pin = password_hash($pinString, PASSWORD_DEFAULT); if(!file_put_contents(dirname(__DIR__) . "/data/pin/{$user["username"]}", $pin)){ diff --git a/api/v1/class/plugins/plugins/codeclock/views/admin.php b/api/v1/class/plugins/plugins/codeclock/views/admin.php index 1498074..d0ae161 100644 --- a/api/v1/class/plugins/plugins/codeclock/views/admin.php +++ b/api/v1/class/plugins/plugins/codeclock/views/admin.php @@ -16,6 +16,9 @@ $code = new Code; $setup = new Setup; $message = "Ready to reset all PINs?"; + +$arbeit->blockIfNotAdmin(); + if(isset($_GET["reset"])) { //remove token file unlink(dirname(__DIR__, 1) . "/data/token"); diff --git a/api/v1/class/plugins/plugins/nfclogin/src/routes/nfcclogin.ep.toil.arbeit.inc.php b/api/v1/class/plugins/plugins/nfclogin/src/routes/nfcclogin.ep.toil.arbeit.inc.php index 15fa528..0831043 100644 --- a/api/v1/class/plugins/plugins/nfclogin/src/routes/nfcclogin.ep.toil.arbeit.inc.php +++ b/api/v1/class/plugins/plugins/nfclogin/src/routes/nfcclogin.ep.toil.arbeit.inc.php @@ -7,6 +7,7 @@ use Toil\EP; use Arbeitszeit\Arbeitszeit; + use Arbeitszeit\StatusMessages; use Arbeitszeit\Benutzer; use Arbeitszeit\Auth; use NFClogin\NFClogin; @@ -37,12 +38,13 @@ public function get() $block = $nfc->readBlock4(); $uid = $nfc->readCard()["uid"] ?? null; $val = $block["value"]; + $statusMessages = new StatusMessages; $getMapUser = $nfc->getUser($uid); $dbUser = Benutzer::get_user_from_id($val)["username"] ?? null; if($getMapUser == $dbUser){ Auth::login($dbUser, "", ["nfclogin" => true]); } else { - header("Location: /suite/login.php?error=wrongdata&uid={$uid}&block={$val}"); + header("Location: /suite/login.php?" . $statusMessages->URIBuilder("wrongdata") . "&uid={$uid}&block={$val}"); exit; } diff --git a/api/v1/class/plugins/plugins/pluginmanager/views/edit.php b/api/v1/class/plugins/plugins/pluginmanager/views/edit.php index 189ea76..94391e0 100644 --- a/api/v1/class/plugins/plugins/pluginmanager/views/edit.php +++ b/api/v1/class/plugins/plugins/pluginmanager/views/edit.php @@ -12,8 +12,8 @@ try { $yaml = $main->getPluginYaml($_GET["edit"]); } catch (TypeError $e){ - Exceptions::error_rep("[pluginmanager] An error occured while trying to retrieve the YAML configuration."); - $yaml = "An error occured while trying to retrieve the YAML configuration. Does this plugin exist?"; + Exceptions::error_rep("[pluginmanager] An error occurred while trying to retrieve the YAML configuration."); + $yaml = "An error occurred while trying to retrieve the YAML configuration. Does this plugin exist?"; $status = ""; $submitButton = ""; $submitEdit = ""; diff --git a/api/v1/class/plugins/plugins/qrclock/views/index.php b/api/v1/class/plugins/plugins/qrclock/views/index.php index 6d683bc..23535e5 100644 --- a/api/v1/class/plugins/plugins/qrclock/views/index.php +++ b/api/v1/class/plugins/plugins/qrclock/views/index.php @@ -14,7 +14,7 @@ try{ $payload = json_decode(base64_decode($_GET["payload"]), true); } catch (\Exception $e){ - return "An error occured while decoding payload."; + return "An error occurred while decoding payload."; } if ($_GET["action"] === "clockin") { if($main->getStatus() === "clockout"){ @@ -25,7 +25,7 @@ if ($arbeit->add_easymode_worktime($payload["username"])) { echo "

Successfully clocked in.

"; } else { - echo "

An error occured while clocking in.

"; + echo "

An error occurred while clocking in.

"; } } else { echo "

Invalid token.

"; @@ -39,7 +39,7 @@ if ($arbeit->end_easymode_worktime($payload["username"], $arbeit->check_easymode_worktime_finished($payload["username"]))) { echo "

Successfully clocked out.

"; } else { - echo "

An error occured while clocking in.

"; + echo "

An error occurred while clocking in.

"; } } else { echo "

Invalid token.

"; diff --git a/api/v1/class/plugins/template/plugin.yml b/api/v1/class/plugins/template/plugin.yml index 644cad1..636445b 100644 --- a/api/v1/class/plugins/template/plugin.yml +++ b/api/v1/class/plugins/template/plugin.yml @@ -5,13 +5,9 @@ namespace: "Example" author: none description: Plugin Skeletton version: "1.0" - api: 0.1 - permissions: none - - enabled: true custom.values: - testing diff --git a/api/v1/class/plugins/template/src/Main.php b/api/v1/class/plugins/template/src/Main.php index d2f8497..e24f394 100644 --- a/api/v1/class/plugins/template/src/Main.php +++ b/api/v1/class/plugins/template/src/Main.php @@ -7,7 +7,7 @@ use Arbeitszeit; use Arbeitszeit\PluginBuilder; -class Main extends PluginBuilder { +class MyPlugin extends PluginBuilder { private string $log_append; diff --git a/api/v1/class/projects/projects.arbeit.inc.php b/api/v1/class/projects/projects.arbeit.inc.php index be79bb8..34499aa 100644 --- a/api/v1/class/projects/projects.arbeit.inc.php +++ b/api/v1/class/projects/projects.arbeit.inc.php @@ -12,7 +12,7 @@ public function addProjectE($name, $description, $note, $users = null){ $sql = "INSERT INTO projects (id, name, users, description, note) VALUES (0, ?, ?, ?, ?);"; $res = $this->db->sendQuery($sql)->execute([$name, $description, $note, $this->computeProjectUserArray("json", $users)]); if(!$res){ - Exceptions::error_rep("[PROJECTS] An error occured while creating an project. See previous message for more information."); + Exceptions::error_rep("[PROJECTS] An error occurred while creating an project. See previous message for more information."); return false; } else { Exceptions::error_rep("[PROJECTS] Successfully created project '{$name}'."); @@ -25,7 +25,7 @@ public function deleteProject($id){ $sql = "DELETE FROM projects WHERE id = ?"; $res = $this->db->sendQuery($sql)->execute([$id]); if(!$res){ - Exceptions::error_rep("[PROJECTS] An error occured while deleting an project. See previous message for more information."); + Exceptions::error_rep("[PROJECTS] An error occurred while deleting an project. See previous message for more information."); return false; } else { Exceptions::error_rep("[PROJECTS] Successfully deleted project '{$id}'."); @@ -60,7 +60,7 @@ public function getProjects(){ $res = $this->db->sendQuery($sql); $res->execute(); if(!$res){ - Exceptions::error_rep("[PROJECTS] An error occured while fetching projects - there just may be none. See previous message for more information."); + Exceptions::error_rep("[PROJECTS] An error occurred while fetching projects - there just may be none. See previous message for more information."); return false; } else { Exceptions::error_rep("[PROJECTS] Successfully found projects."); @@ -74,7 +74,7 @@ public function getProject($id){ $res = $this->db->sendQuery($sql); $res->execute([$id]); if(!$res){ - Exceptions::error_rep("[PROJECTS] An error occured while fetching project '{$id}'. See previous message for more information."); + Exceptions::error_rep("[PROJECTS] An error occurred while fetching project '{$id}'. See previous message for more information."); return false; } else { Exceptions::error_rep("[PROJECTS] Successfully found project '{$id}'."); diff --git a/api/v1/class/sickness/sickness.arbeit.inc.php b/api/v1/class/sickness/sickness.arbeit.inc.php index d5dc521..9b3ad61 100644 --- a/api/v1/class/sickness/sickness.arbeit.inc.php +++ b/api/v1/class/sickness/sickness.arbeit.inc.php @@ -26,13 +26,13 @@ public function add_sickness($start, $stop, $user = null) $dateObj = \DateTime::createFromFormat($format, $dateString); if ($dateObj == false) { - Exceptions::error_rep("[SICK] An error occured while adding an sickness for user '$user'. Could not validate dateFormat! | String: '{$dateString}', expected: d-m-Y"); + Exceptions::error_rep("[SICK] An error occurred while adding an sickness for user '$user'. Could not validate dateFormat! | String: '{$dateString}', expected: d-m-Y"); return false; } $data = $this->db()->sendQuery("INSERT INTO sick (id, username, start, stop, status) VALUES (0, ?, ?, ?, 'pending')")->execute([$user, $start, $stop]); if($data == false){ - Exceptions::error_rep("[SICK] An error occured while adding an sickness for user '$user'. See previous message for more information."); + Exceptions::error_rep("[SICK] An error occurred while adding an sickness for user '$user'. See previous message for more information."); return false; } else { EventDispatcherService::get()->dispatch(new SicknessCreatedEvent($user, $start, $stop), SicknessCreatedEvent::NAME); @@ -48,7 +48,7 @@ public function remove_sickness($id){ # admin function only Exceptions::error_rep("[SICK] Removing sickness with id '{$id}'..."); $data = $this->db()->sendQuery("DELETE * FROM sick WHERE id = ?")->execute([$id]); if($data == false){ - Exceptions::error_rep("[SICK] An error occured while deleting a sickness with id '{$id}'. See previous message for more information."); + Exceptions::error_rep("[SICK] An error occurred while deleting a sickness with id '{$id}'. See previous message for more information."); return false; } EventDispatcherService::get()->dispatch(new SicknessDeletedEvent($_SESSION["username"], $id), SicknessDeletedEvent::NAME); @@ -70,7 +70,7 @@ public function change_status($id, $new_state = 3) # admin function only } $data = $this->db()->sendQuery($sql)->execute([$id]); if($data == false){ - Exceptions::error_rep("[SICK] An error occured while setting status for sickness. id '{$id}', new state: '{$new_state}'. See previous message for more information."); + Exceptions::error_rep("[SICK] An error occurred while setting status for sickness. id '{$id}', new state: '{$new_state}'. See previous message for more information."); return false; } else { EventDispatcherService::get()->dispatch(new SicknessUpdatedEvent($_SESSION["username"], $id, $new_state), SicknessUpdatedEvent::NAME); @@ -142,7 +142,7 @@ public function get_sickness($id, $mode = 1){ $sql = "SELECT * FROM `sick` WHERE id = ?"; $data = $this->db()->sendQuery($sql)->execute([$id]); if($data == false){ - Exceptions::error_rep("[SICK] An error occured while getting sickness. id '{$id}'. See previous message for more information."); + Exceptions::error_rep("[SICK] An error occurred while getting sickness. id '{$id}'. See previous message for more information."); return false; } else { Exceptions::error_rep("[SICK] Successfully found sickness id '{$id}' inside database."); diff --git a/api/v1/class/status/runHook.php b/api/v1/class/status/runHook.php new file mode 100644 index 0000000..b0baedc --- /dev/null +++ b/api/v1/class/status/runHook.php @@ -0,0 +1,6 @@ +statusMessages()->hook(); \ No newline at end of file diff --git a/api/v1/class/status/statusmessages.arbeit.inc.php b/api/v1/class/status/statusmessages.arbeit.inc.php new file mode 100644 index 0000000..eeabb6e --- /dev/null +++ b/api/v1/class/status/statusmessages.arbeit.inc.php @@ -0,0 +1,152 @@ +db = new DB; + + // check if directly wanting to display message + if ($key != null) { + Exceptions::error_rep("StatusMessages constructor called with key: " . $key, 1, "N/A"); + $this->loadStatusMessages($path); + } + } + + /** + * This function loads the status messages based of the existing i18n files. + * This behavior can be changed by setting $file_path in the loadStatusMessages function. Otherwise the standard file will be used. + * It is recommended to place custom files within the /data directory. + * @param string|null $file_path The path to the status messages file relative to the document root. + * @return void + */ + public function loadStatusMessages($file_path = null) + { + if ($file_path == null) { + Exceptions::error_rep("No file path provided, using default status messages file.", 1, "N/A"); + $this->messages = $this->i18n()->loadLanguage(null, "status"); + } else { + try { + $json_data = file_get_contents($_SERVER["DOCUMENT_ROOT"] . $file_path); + $decoded_data = json_decode($json_data, true); + Exceptions::error_rep("Status messages loaded from file: " . $file_path, 1, "N/A"); + if (is_array($decoded_data)) { + Exceptions::error_rep("Status messages decoded successfully.", 1, "N/A"); + $this->messages = $decoded_data; + } + + $this->custom_path = $file_path; + } catch (\Exception $e) { + Exceptions::error_rep($e->getMessage(), 1, "N/A"); + return; + } + } + } + + /** + * Converts the keys of the status messages to base64 so they can be reversed and then used to determine the key. + * + * @return void + */ + public function base64KeysConverter() + { + Exceptions::error_rep("Converting status message keys to base64.", 1, "N/A"); + $this->base64Keys_messages = []; + foreach (array_keys($this->messages) as $key) { + $this->base64Keys_messages[base64_encode($key)] = $key; + } + } + + + /** + * Loads the status message from specified file with the given ID. + * @param mixed $id + * @return string|null + */ + public function message($id) + { + if (isset($this->messages[$id])) { + Exceptions::error_rep("Status message with ID '$id' found.", 1, "N/A"); + return "
×" . $this->messages[$id] . "
"; + } else { + Exceptions::error_rep("Status message with ID '$id' not found.", 1, "N/A"); + return null; + } + } + + public function convertBase64Key($key) + { + if (isset($this->base64Keys_messages[$key])) { + Exceptions::error_rep("Converting base64 key: " . $key, 1, "N/A"); + return $this->base64Keys_messages[$key]; + } else { + Exceptions::error_rep("Base64 key '$key' not found in messages.", 1, "N/A"); + return null; + } + } + + + /** + * Use this function to build the URI for the status message. + * @param mixed $key The key of the status message. + * @param mixed $path Optional path to the status message, if needed. + * @return string Returns the URI string with the status message encoded in base64 ("status=....", without URI begin (?) or separator (&)). + */ + public function URIBuilder($key, $path = null) + { + // This function is used to build the URI for the status message. + $uri = "status=" . base64_encode(json_encode([ + "key" => base64_encode($key), + "path" => $path + ])); + Exceptions::error_rep("URIBuilder called with key: " . $key . " and path: " . $path, 1, "N/A"); + return $uri; + } + + public function hook() + { + // This function is used in the arbeitszeit class to hook it into the system directly, ensuring it will be displayed at the top and exactly as before v7.13. + Exceptions::error_rep("StatusMessages hook called", 1, "N/A"); + if (isset($_GET["status"])) { + Exceptions::error_rep("StatusMessages hook called with status parameter", 1, "N/A"); + $status = base64_decode($_GET["status"]); + $status = json_decode($status, true); + Exceptions::error_rep("Decoded status: " . print_r($status, true), 1, "N/A"); + + if (is_array($status) && isset($status["key"])) { + $this->loadStatusMessages(); + Exceptions::error_rep("StatusMessages loaded successfully", 1, "N/A"); + Exceptions::error_rep("messages: " . print_r($this->messages, true), 1, "N/A"); + $this->base64KeysConverter(); + Exceptions::error_rep("Base64 keys converted successfully", 1, "N/A"); + Exceptions::error_rep("Base64 keys: " . print_r($this->base64Keys_messages, true), 1, "N/A"); + Exceptions::error_rep("Converting base64 key: " . $status["key"], 1, "N/A"); + + $key = $this->convertBase64Key($status["key"]); + Exceptions::error_rep("Converted key: " . $key . "| base64 original: " . $status["key"], 1, "N/A"); + return $this->message($key); + } else { + Exceptions::error_rep("StatusMessages hook called without valid status parameter", 1, "N/A"); + Exceptions::error_rep("Status parameter: " . print_r($status, true), 1, "N/A"); + return ""; + } + } else { + Exceptions::error_rep("StatusMessages hook called without status parameter", 1, "N/A"); + return ""; + } + + } + } +} \ No newline at end of file diff --git a/api/v1/class/vacation/vacation.arbeit.inc.php b/api/v1/class/vacation/vacation.arbeit.inc.php index 79cafb8..b5470f9 100644 --- a/api/v1/class/vacation/vacation.arbeit.inc.php +++ b/api/v1/class/vacation/vacation.arbeit.inc.php @@ -34,13 +34,13 @@ public function add_vacation($start, $stop, $username = null) $dateObj = \DateTime::createFromFormat($format, $dateString); if ($dateObj == false) { - Exceptions::error_rep("[VACATION] An error occured while adding an vacation for user '$user'. Could not validate dateFormat! | String: '{$dateString}', expected: d-m-Y"); + Exceptions::error_rep("[VACATION] An error occurred while adding an vacation for user '$user'. Could not validate dateFormat! | String: '{$dateString}', expected: d-m-Y"); return false; } $sql = "INSERT INTO `vacation` (`id`, `username`, `start`, `stop`, `status`) VALUES ('0', ?, ?, ?, 'pending') "; $data = $this->db->sendQuery($sql)->execute([$user, $start, $stop]); if(!$data){ - Exceptions::error_rep("[VACATION] An error occured while adding an vacation for user '$user'. See previous message for more information."); + Exceptions::error_rep("[VACATION] An error occurred while adding an vacation for user '$user'. See previous message for more information."); return false; } else { EventDispatcherService::get()->dispatch(new VacationCreatedEvent($user, $start, $stop), VacationCreatedEvent::NAME); @@ -57,7 +57,7 @@ public function remove_vacation($id){ # admin function only $sql = "DELETE * FROM `vacation` WHERE id = ?"; $data = $this->db->sendQuery($sql)->execute(array([$id])); if($data == false){ - Exceptions::error_rep("[VACATION] An error occured while deleting a vacation with id '{$id}'. See previous message for more information"); + Exceptions::error_rep("[VACATION] An error occurred while deleting a vacation with id '{$id}'. See previous message for more information"); return false; } EventDispatcherService::get()->dispatch(new VacationDeletedEvent($_SESSION["username"], (int)$id), VacationDeletedEvent::NAME); @@ -80,7 +80,7 @@ public function change_status($id, $new_state = 3) # admin function only } $data = $this->db->sendQuery($sql)->execute([$id]); if($data == false){ - Exceptions::error_rep("[VACATION] An error occured while setting status for vacaction. id '{$id}', new state: '{$new_state}'. See previous message for more information."); + Exceptions::error_rep("[VACATION] An error occurred while setting status for vacaction. id '{$id}', new state: '{$new_state}'. See previous message for more information."); return false; } else { EventDispatcherService::get()->dispatch(new VacationUpdatedEvent($_SESSION["username"], (int)$id, $new_state), VacationUpdatedEvent::NAME); @@ -94,7 +94,7 @@ public function get_vacation($id, $mode = 1){ Exceptions::error_rep("[VACATION] Getting vacation id '{$id}'..."); $data = $this->db->sendQuery("SELECT * FROM `vacation` WHERE id = ?")->execute([$id]); if($data == false){ - Exceptions::error_rep("[VACATION] An error occured while getting vacaction. id '{$id}'. See previous message for more information."); + Exceptions::error_rep("[VACATION] An error occurred while getting vacaction. id '{$id}'. See previous message for more information."); return false; } else { Exceptions::error_rep("[VACATION] Successfully found vacation id '{$id}' inside database."); diff --git a/api/v1/inc/app.json.sample b/api/v1/inc/app.json.sample index 2c80167..629adbb 100644 --- a/api/v1/inc/app.json.sample +++ b/api/v1/inc/app.json.sample @@ -46,5 +46,10 @@ "saf_group": "GROUP", "saf_domain": "DOMAIN", "create_user": "true" + }, + "config": { + "worktime_types": "/api/v1/inc/config/worktime_types.json", + "vacation_types": "/api/v1/inc/config/vacation_types.json", + "sickness_types": "/api/v1/inc/config/sickness_types.json" } } \ No newline at end of file diff --git a/api/v1/inc/arbeit.inc.php b/api/v1/inc/arbeit.inc.php index 010b49c..6117c64 100644 --- a/api/v1/inc/arbeit.inc.php +++ b/api/v1/inc/arbeit.inc.php @@ -20,6 +20,8 @@ require_once dirname(__DIR__, 1) . "/class/mode/mode.arbeit.inc.php"; require_once dirname(__DIR__, 1) . "/class/notifications/plugins/autodelete.notifications.arbeit.inc.php"; require_once dirname(__DIR__, 1) . "/class/exceptions/exceptions.arbeit.inc.php"; +require_once dirname(__DIR__, 1) . "/class/status/statusMessages.arbeit.inc.php"; +require_once dirname(__DIR__, 1) . "/class/status/runHook.php"; require_once dirname(__DIR__, 1) . "/class/vacation/vacation.arbeit.inc.php"; require_once dirname(__DIR__, 1) . "/class/sickness/sickness.arbeit.inc.php"; @@ -32,6 +34,7 @@ require_once dirname(__DIR__, 1) . "/toil/resources/ep.toil.arbeit.inc.php"; require_once dirname(__DIR__, 1) . "/toil/Permissions.routes.toil.arbeit.inc.php"; require_once dirname(__DIR__, 1) . "/toil/CustomRoutes.routes.toil.arbeit.inc.php"; +require_once dirname(__DIR__, 1) . "/toil/Tokens.routes.toil.arbeit.inc.php"; require_once dirname(__DIR__, 1) . "/class/auth/plugins/ldap/ldap.auth.arbeit.inc.php"; diff --git a/api/v1/inc/config/worktime_types.json b/api/v1/inc/config/worktime_types.json new file mode 100644 index 0000000..1ac310a --- /dev/null +++ b/api/v1/inc/config/worktime_types.json @@ -0,0 +1,7 @@ +{ + "0": "Home Office", + "1": "Office", + "2": "Public Holiday", + "3": "Business Trip", + "4": "Mobile / On the Go" +} \ No newline at end of file diff --git a/api/v1/toil/Controller.toil.arbeit.inc.php b/api/v1/toil/Controller.toil.arbeit.inc.php index f8f1481..efed900 100644 --- a/api/v1/toil/Controller.toil.arbeit.inc.php +++ b/api/v1/toil/Controller.toil.arbeit.inc.php @@ -25,7 +25,7 @@ public static function createcustomview($endpoint, $classFile){ $class = "Toil\\$endpoint"; $class = new $class; } catch (\Throwable $e){ - \Arbeitszeit\Exceptions::error_rep("[API] An error occured while loading custom route for endpoint {$endpoint}: " . $e->getMessage()); + \Arbeitszeit\Exceptions::error_rep("[API] An error occurred while loading custom route for endpoint {$endpoint}: " . $e->getMessage()); header("Content-Type: application/json"); echo json_encode(array("error" => "Could not load custom route.")); die(); diff --git a/api/v1/toil/Permissions.routes.toil.arbeit.inc.php b/api/v1/toil/Permissions.routes.toil.arbeit.inc.php index 829efd8..f0f6aaa 100644 --- a/api/v1/toil/Permissions.routes.toil.arbeit.inc.php +++ b/api/v1/toil/Permissions.routes.toil.arbeit.inc.php @@ -62,7 +62,7 @@ public function checkPermissions($user, $endpoint) { return true; } - if ($endpointPermission === 0 && !$userIsAdmin) { + if ($endpointPermission === 0) { return true; } diff --git a/api/v1/toil/README.md b/api/v1/toil/README.md index 3e5acdf..84e6304 100644 --- a/api/v1/toil/README.md +++ b/api/v1/toil/README.md @@ -29,6 +29,10 @@ Most endpoints are using `GET` method to either change, delete, view or add data * `addNotification` - Adds a notification (admin) * `autodeleteNotification` - Automatically deletes a notification after if has been expired (user) +* `createToken` - Creates a new token for the authenticated user (user). +* `deleteToken` - Deletes a token for the authenticated user (user). +* `refreshToken` - Refreshes a token for the authenticated user (user, POST endpoint). + You can access the API via the base URL, e.g. `"https://{domain}.{tld}/api/v1/toil/"`. HTTP Basic is used for authentication. Example: `https://{ADMIN_USERNAME}:{ADMIN_USER_PASSWORD}@{domain}.{tld}/api/v1/toil/getVersion` @@ -85,3 +89,8 @@ You can use the `CustomRoutes::getCustomRoute("myRoute", true)` function to chec If you do not set the second parameter to `true` the function will return the path to the endpoint file. You can also use the `CustomRoutes::getCustomRoutes()` function to get all custom routes and then check if the endpoint has a key in the returned array. + +## Tokens + +You can use the `createToken` endpoint to create a new token for yourself. The token will be returned in the response along with other useful information you may want to note down. The token will be valid by default for 2 hours. You can set the expiration time in the `Tokens.routes.toil.arbeit.inc.php` file. When a token is expired you can use the `refreshToken` enpoint to refresh the token. When a token expires it is not deleted from the database to allow you refreshing it as the refresh token does not expire. To delete a token entirely you can use the `deleteToken` endpoint (revoke). +Tokens cannot be used to login to the web interface, you can only use them to access the API. You have to set the `Authorization` header to `Bearer {token}` to use the token. diff --git a/api/v1/toil/Routes.toil.arbeit.inc.php b/api/v1/toil/Routes.toil.arbeit.inc.php index 82b406e..af109c3 100644 --- a/api/v1/toil/Routes.toil.arbeit.inc.php +++ b/api/v1/toil/Routes.toil.arbeit.inc.php @@ -14,13 +14,14 @@ use Arbeitszeit\Exceptions; use Toil\Controller; use Toil\Permissions; +use Toil\Tokens; class Routes extends Toil { private Arbeitszeit $arbeitszeit; private Benutzer $benutzer; - private string $api_username; + private string $api_username = ""; private $api_password; @@ -33,6 +34,8 @@ class Routes extends Toil public function __construct() { $this->__set("eventHandler", new EventHandler()); + $this->__set("arbeitszeit", new Arbeitszeit()); + $this->__set("benutzer", new Benutzer()); $endpoint = preg_replace('/\?.*/', '', $_SERVER['REQUEST_URI']); $endpointName = $this->getResourceNameFromPath($endpoint); @@ -42,9 +45,31 @@ public function __construct() Exceptions::error_rep("[API] Public access to '$endpointName' allowed – skipping authentication."); return; } - $user = $_SERVER["PHP_AUTH_USER"] ?? null; $pw = $_SERVER["PHP_AUTH_PW"] ?? null; + $tokenAuth = false; + if (isset($_SERVER["HTTP_AUTHORIZATION"])) { + $authHeader = $_SERVER["HTTP_AUTHORIZATION"]; + if (preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) { + $token = $matches[1]; + $tokens = new Tokens(); + if ($tokens->validate_token($token)) { + Exceptions::error_rep("[API] Token is valid."); + $t = $tokens->get_token_from_token($token); + $user = $this->arbeitszeit->benutzer()->get_user_from_id($t["user_id"])["username"]; + $pw = $t["access_token"]; + $tokenAuth = true; + + } else { + Exceptions::error_rep("[API] Token is invalid."); + header("Content-type: application/json"); + header("HTTP/1.1 401 Unauthorized"); + echo json_encode(["error" => "invalid token"]); + die(); + } + } + } + $this->__set("arbeitszeit", new Arbeitszeit()); $this->__set("benutzer", new Benutzer()); @@ -55,7 +80,12 @@ public function __construct() $this->__set("api_username", $user); } - $this->__set("api_password", $pw or $this->authError($user)); + $this->__set("api_password", $pw ?? $this->authError($user)); + + if($tokenAuth == true){ + Exceptions::error_rep("[API] Token authentication successful."); + return; + } if (!$this->login(username: $user, password: $pw)) { $this->authError($user); @@ -97,7 +127,7 @@ public function routing($eventHandler) { Exceptions::error_rep("[API] Start API routing request and registering routes..."); $base = $this->__get("basepath"); - $user = $_SERVER["PHP_AUTH_USER"]; + $user = $this->__get("api_username"); $eventHandler->register(EventHandler::EVENT_ADD_ROUTE, function (EventArgument $event) use ($base) { $route = $event->route; if (!$event->isSubRoute) { @@ -227,6 +257,21 @@ public function routing($eventHandler) Controller::createview("autoremoveNotifications"); }); + Router::get("/api/v1/toil/createToken", function () { + Exceptions::error_rep("[API] User authenticated and accessing 'createToken' endpoint"); + Controller::createview("createToken"); + }); + + Router::get("/api/v1/toil/deleteToken", function () { + Exceptions::error_rep("[API] User authenticated and accessing 'deleteToken' endpoint"); + Controller::createview("deleteToken"); + }); + + Router::post("/api/v1/toil/refreshToken", function () { + Exceptions::error_rep("[API] User authenticated and accessing 'refreshToken' endpoint"); + Controller::createview("refreshToken"); + }); + // Loading all custom routes CustomRoutes::loadCustomRoutes(); diff --git a/api/v1/toil/Tokens.routes.toil.arbeit.inc.php b/api/v1/toil/Tokens.routes.toil.arbeit.inc.php new file mode 100644 index 0000000..6cc2bed --- /dev/null +++ b/api/v1/toil/Tokens.routes.toil.arbeit.inc.php @@ -0,0 +1,174 @@ +arbeit = new \Arbeitszeit\Arbeitszeit(); + } + + public function createToken($username, $expiration = 7200, $isUpdate = false){ + Exceptions::error_rep("[API] Creating token for user '{$username}'..."); + $user = $this->arbeit->benutzer()->get_user($username); + if($user){ + $token = bin2hex(random_bytes(16)); + $refresh_token = bin2hex(random_bytes(16)); + $expires_at = date("Y-m-d H:i:s", time() + $expiration); + $created_at = date("Y-m-d H:i:s"); + $updated_at = date("Y-m-d H:i:s"); + $user_id = $user['id']; + if($isUpdate){ + $this->update_token($user['id'], $token, $refresh_token, $expires_at, $created_at, $updated_at); + } else { + $this->create_token($user['id'], $token, $refresh_token, $expires_at, $created_at, $updated_at); + } + return [ + 'access_token' => $token, + 'refresh_token' => $refresh_token, + 'expires_at' => $expires_at, + 'created_at' => $created_at, + 'updated_at' => $updated_at, + 'user_id' => $user_id, + 'username' => $username, + 'status' => 'success', + 'isUpdate' => $isUpdate + ]; + } else { + Exceptions::error_rep("[API] User '{$username}' not found."); + return false; + } + } + + public function create_token($user_id, $token, $refresh_token, $expires_at, $created_at, $updated_at){ + Exceptions::error_rep("[API] Creating token in database..."); + $query = "INSERT INTO tokens (user_id, access_token, refresh_token, expires_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)"; + $stmt = $this->arbeit->db()->sendQuery($query); + $stmt->execute([$user_id, $token, $refresh_token, $expires_at, $created_at, $updated_at]); + Exceptions::error_rep("[API] Token created successfully."); + return [ + 'status' => 'success', + 'message' => 'Token created successfully.' + ]; + } + + public function update_token($user_id, $token, $refresh_token, $expires_at, $created_at, $updated_at){ + Exceptions::error_rep("[API] Updating token in database..."); + $query = "UPDATE tokens SET access_token = ?, refresh_token = ?, expires_at = ?, created_at = ?, updated_at = ? WHERE user_id = ?"; + $stmt = $this->arbeit->db()->sendQuery($query); + $stmt->execute([$token, $refresh_token, $expires_at, $created_at, $updated_at, $user_id]); + Exceptions::error_rep("[API] Token updated successfully."); + return [ + 'status' => 'success', + 'message' => 'Token updated successfully.' + ]; + } + + public function delete_token($user_id, $token){ + Exceptions::error_rep("[API] Deleting token from database..."); + $query = "DELETE FROM tokens WHERE user_id = ? AND access_token = ?"; + $stmt = $this->arbeit->db()->sendQuery($query); + $stmt->execute([$user_id, $token]); + Exceptions::error_rep("[API] Token deleted successfully."); + return [ + 'status' => 'success', + 'message' => 'Token deleted successfully.' + ]; + } + + public function get_token_from_user_id($user_id){ + Exceptions::error_rep("[API] Getting token from database..."); + $query = "SELECT * FROM tokens WHERE user_id = ?"; + $stmt = $this->arbeit->db()->sendQuery($query); + $stmt->execute([$user_id]); + $token = $stmt->fetch(); + if($token){ + Exceptions::error_rep("[API] Token found."); + return $token; + } else { + Exceptions::error_rep("[API] Token not found."); + return false; + } + } + + public function get_token_from_token($token){ + Exceptions::error_rep("[API] Getting token from database..."); + $query = "SELECT * FROM tokens WHERE access_token = ?"; + $stmt = $this->arbeit->db()->sendQuery($query); + $stmt->execute([$token]); + $tokenData = $stmt->fetch(); + if($tokenData){ + Exceptions::error_rep("[API] Token found."); + return $tokenData; + } else { + Exceptions::error_rep("[API] Token not found."); + return false; + } + } + + public function validate_token($token){ + Exceptions::error_rep("[API] Validating token..."); + $query = "SELECT * FROM tokens WHERE access_token = ?"; + $stmt = $this->arbeit->db()->sendQuery($query); + $stmt->execute([$token]); + $tokenData = $stmt->fetch(); + if($tokenData){ + if(strtotime($tokenData['expires_at']) > time()){ + Exceptions::error_rep("[API] Token is valid."); + return true; + } else { + Exceptions::error_rep("[API] Token is expired."); + return false; + } + } else { + Exceptions::error_rep("[API] Token not found."); + return false; + } + } + + public function refresh_token($refresh_token){ + Exceptions::error_rep("[API] Refreshing token..."); + $query = "SELECT * FROM tokens WHERE refresh_token = ?"; + $stmt = $this->arbeit->db()->sendQuery($query); + $stmt->execute([$refresh_token]); + $tokenData = $stmt->fetch(); + if($tokenData){ + if(strtotime($tokenData['expires_at']) > time()){ + Exceptions::error_rep("[API] Refresh token is still valid."); + $new_access_token = bin2hex(random_bytes(16)); + $new_refresh_token = bin2hex(random_bytes(16)); + $new_expires_at = date("Y-m-d H:i:s", time() + 7200); + $updated_at = date("Y-m-d H:i:s"); + + $this->update_token( + $tokenData['user_id'], + $new_access_token, + $new_refresh_token, + $new_expires_at, + $tokenData['created_at'], + $updated_at + ); + + Exceptions::error_rep("[API] Token refreshed successfully."); + return [ + 'access_token' => $new_access_token, + 'refresh_token' => $new_refresh_token, + 'expires_at' => $new_expires_at, + 'updated_at' => $updated_at, + 'user_id' => $tokenData['user_id'], + 'status' => 'success' + ]; + } else { + Exceptions::error_rep("[API] Refresh token is expired."); + return false; + } + } else { + Exceptions::error_rep("[API] Refresh token not found."); + return false; + } + } +} \ No newline at end of file diff --git a/api/v1/toil/VERSION b/api/v1/toil/VERSION index fa0755c..6eef684 100644 --- a/api/v1/toil/VERSION +++ b/api/v1/toil/VERSION @@ -1 +1 @@ -v1.11 +v1.12 diff --git a/api/v1/toil/permissions.json b/api/v1/toil/permissions.json index f86e493..7c22d9e 100644 --- a/api/v1/toil/permissions.json +++ b/api/v1/toil/permissions.json @@ -22,5 +22,8 @@ "autoremoveNotifications": 0, "removeNotification": 1, "addNotification": 1, - "getNotifications": 0 + "getNotifications": 0, + "createToken": 0, + "deleteToken": 0, + "refreshToken": 0 } \ No newline at end of file diff --git a/api/v1/toil/resources/addOwnVacation.ep.toil.arbeit.inc.php b/api/v1/toil/resources/addOwnVacation.ep.toil.arbeit.inc.php index 29fe282..6496ed2 100644 --- a/api/v1/toil/resources/addOwnVacation.ep.toil.arbeit.inc.php +++ b/api/v1/toil/resources/addOwnVacation.ep.toil.arbeit.inc.php @@ -40,7 +40,7 @@ public function get() if ($vac->add_vacation($start, $stop, $username)) { echo json_encode(["note" => "Successfully saved vacation record"]); } else { - echo json_encode(["error" => "An error occured while saving vacation"]); + echo json_encode(["error" => "An error occurred while saving vacation"]); } } diff --git a/api/v1/toil/resources/addOwnWorktime.ep.toil.arbeit.inc.php b/api/v1/toil/resources/addOwnWorktime.ep.toil.arbeit.inc.php index 5e57745..4cee095 100644 --- a/api/v1/toil/resources/addOwnWorktime.ep.toil.arbeit.inc.php +++ b/api/v1/toil/resources/addOwnWorktime.ep.toil.arbeit.inc.php @@ -45,10 +45,10 @@ public function get() ]; - if ($arbeit->add_worktime($data["start"], $data["end"], $data["location"], $data["date"], $data["username"], $data["type"], $data["pause"], $data["meta"])) { + if ($arbeit->add_worktime($data["start"], $data["end"], $data["location"], $data["date"], $data["username"], $data["type"], 0, $data["pause"], $data["meta"])) { echo json_encode(["note" => "Successfully saved worktime record"]); } else { - echo json_encode(["error" => "An error occured while saving worktime"]); + echo json_encode(["error" => "An error occurred while saving worktime"]); } } diff --git a/api/v1/toil/resources/addProject.ep.toil.arbeit.inc.php b/api/v1/toil/resources/addProject.ep.toil.arbeit.inc.php index 7900763..735ad65 100644 --- a/api/v1/toil/resources/addProject.ep.toil.arbeit.inc.php +++ b/api/v1/toil/resources/addProject.ep.toil.arbeit.inc.php @@ -43,7 +43,7 @@ public function get() if ($projects->addProjectE($data["name"], $data["description"], $data["note"], $data["users"])) { echo json_encode(["note" => "Successfully saved new project"]); } else { - echo json_encode(["error" => "An error occured while saving project"]); + echo json_encode(["error" => "An error occurred while saving project"]); } } else { echo json_encode(["error" => "No permission."]); diff --git a/api/v1/toil/resources/addVacation.ep.toil.arbeit.inc.php b/api/v1/toil/resources/addVacation.ep.toil.arbeit.inc.php index ed9a83d..fe20b17 100644 --- a/api/v1/toil/resources/addVacation.ep.toil.arbeit.inc.php +++ b/api/v1/toil/resources/addVacation.ep.toil.arbeit.inc.php @@ -40,7 +40,7 @@ public function get() if ($vac->add_vacation($start, $stop, $username)) { echo json_encode(["note" => "Successfully saved vacation record"]); } else { - echo json_encode(["error" => "An error occured while saving vacation"]); + echo json_encode(["error" => "An error occurred while saving vacation"]); } } else { echo json_encode(["error" => "No permission."]); diff --git a/api/v1/toil/resources/addWorktime.ep.toil.arbeit.inc.php b/api/v1/toil/resources/addWorktime.ep.toil.arbeit.inc.php index 31bfcd9..ba5c0a7 100644 --- a/api/v1/toil/resources/addWorktime.ep.toil.arbeit.inc.php +++ b/api/v1/toil/resources/addWorktime.ep.toil.arbeit.inc.php @@ -45,10 +45,10 @@ public function get() ]; if ($user->is_admin($user->get_user($_SERVER["PHP_AUTH_USER"])) == true) { - if ($arbeit->add_worktime($data["start"], $data["end"], $data["location"], $data["date"], $data["username"], $data["type"], $data["pause"], $data["meta"])) { + if ($arbeit->add_worktime($data["start"], $data["end"], $data["location"], $data["date"], $data["username"], $data["type"], 0, $data["pause"], $data["meta"])) { echo json_encode(["note" => "Successfully saved worktime record"]); } else { - echo json_encode(["error" => "An error occured while saving worktime"]); + echo json_encode(["error" => "An error occurred while saving worktime"]); } } else { echo json_encode(["error" => "No permission."]); diff --git a/api/v1/toil/resources/createToken.ep.toil.arbeit.inc.php b/api/v1/toil/resources/createToken.ep.toil.arbeit.inc.php new file mode 100644 index 0000000..7a56cb0 --- /dev/null +++ b/api/v1/toil/resources/createToken.ep.toil.arbeit.inc.php @@ -0,0 +1,70 @@ +$name = $value; + } + + public function __get($name) + { + return $this->$name; + } + + public function get() + { + header('Content-Type: application/json'); + $username = $_SERVER["PHP_AUTH_USER"] ?? die(json_encode(["error" => true])); + $expiration = $_GET["expiration"] ?? 7200; + $isUpdate = $_GET["isUpdate"] ?? false; + + if ($username != null) { + $token = new Tokens(); + $result = $token->createToken($username, $expiration, $isUpdate); + echo json_encode($result); + } else { + echo json_encode(["error" => true]); + } + } + + public function post($post = null) + { + /** + * Empty, required by interface + * + */ + } + + public function delete() + { + /** + * Empty, required by interface + * + */ + } + + public function put() + { + /** + * Empty, required by interface + * + */ + } + } +} + + +?> \ No newline at end of file diff --git a/api/v1/toil/resources/deleteToken.ep.toil.arbeit.inc.php b/api/v1/toil/resources/deleteToken.ep.toil.arbeit.inc.php new file mode 100644 index 0000000..51b5d52 --- /dev/null +++ b/api/v1/toil/resources/deleteToken.ep.toil.arbeit.inc.php @@ -0,0 +1,79 @@ +$name = $value; + } + + public function __get($name) + { + return $this->$name; + } + + public function get() + { + header('Content-Type: application/json'); + + $authHeader = $_SERVER["HTTP_AUTHORIZATION"] ?? null; + + if ($authHeader && preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) { + $token = $matches[1]; + $tokens = new Tokens(); + + if ($tokens->validate_token($token)) { + $t = $tokens->get_token_from_token($token); + $userId = $t["user_id"]; + + $result = $tokens->delete_token($userId, $token); + echo json_encode($result); + return; + } + } + + echo json_encode(["error" => true, "reason" => "Invalid or missing token"]); + } + + + public function post($post = null) + { + /** + * Empty, required by interface + * + */ + } + + public function delete() + { + /** + * Empty, required by interface + * + */ + } + + public function put() + { + /** + * Empty, required by interface + * + */ + } + } +} + + +?> \ No newline at end of file diff --git a/api/v1/toil/resources/refreshToken.ep.toil.arbeit.inc.php b/api/v1/toil/resources/refreshToken.ep.toil.arbeit.inc.php new file mode 100644 index 0000000..5b85e96 --- /dev/null +++ b/api/v1/toil/resources/refreshToken.ep.toil.arbeit.inc.php @@ -0,0 +1,95 @@ +$name = $value; + } + + public function __get($name) + { + return $this->$name; + } + + public function get() + { + /** + * Empty, required by interface + * + */ + } + + public function post($post = null) + { + header('Content-Type: application/json'); + + $authHeader = $_SERVER["HTTP_AUTHORIZATION"] ?? null; + if (!$authHeader || !preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) { + echo json_encode(["error" => true, "reason" => "Missing or invalid access token"]); + return; + } + $accessToken = $matches[1]; + $t = file_get_contents("php://input"); + $data = json_decode($t, true); + $refreshToken = $data["refreshToken"]; + + if (!$refreshToken) { + echo json_encode(["error" => true, "reason" => "Missing refresh token"]); + return; + } + + $tokens = new Tokens(); + + if (!$tokens->validate_token($accessToken)) { + echo json_encode(["error" => true, "reason" => "Invalid access token"]); + return; + } + + $tokenData = $tokens->get_token_from_token($accessToken); + if (!$tokenData) { + echo json_encode(["error" => true, "reason" => "Token not found"]); + return; + } + + if ($tokenData["refresh_token"] !== $refreshToken) { + echo json_encode(["error" => true, "reason" => "Refresh token does not match"]); + return; + } + $newTokens = $tokens->refresh_token($refreshToken); + + echo json_encode($newTokens); + } + + public function delete() + { + /** + * Empty, required by interface + * + */ + } + + public function put() + { + /** + * Empty, required by interface + * + */ + } + } +} + + +?> \ No newline at end of file diff --git a/api/v1/toil/toil.arbeit.inc.php b/api/v1/toil/toil.arbeit.inc.php index 588c8a5..dd34494 100644 --- a/api/v1/toil/toil.arbeit.inc.php +++ b/api/v1/toil/toil.arbeit.inc.php @@ -8,7 +8,7 @@ public function __construct(){ $routes = new Routes; $routes->routing($routes->__get("eventHandler")); } catch (\Exception $e){ - Exceptions::error_rep("An error occured while accessing the API | Code: " . $e->getCode() . " | Message: " . $e->getMessage() . " | Trace: " . $e->getTraceAsString()); + Exceptions::error_rep("An error occurred while accessing the API | Code: " . $e->getCode() . " | Message: " . $e->getMessage() . " | Trace: " . $e->getTraceAsString()); header("HTTP/1.1 500 Internal Server Error"); header("Content-type: application/json"); echo json_encode(array("code" => 500, "message" => "Internal Server error. View log fore more details.")); diff --git a/assets/css/index.css b/assets/css/index.css index a369643..118b15a 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -193,4 +193,47 @@ hr { .text-red { color: red; font-weight: bold;} .text-green { color: green; font-weight: bold;} .text-yellow { color: yellow; font-weight: bold;} -.text-blue { color: blue; font-weight: bold;} \ No newline at end of file +.text-blue { color: blue; font-weight: bold;} + +.status-message { + font-family: "Rubik", sans-serif; + font-size: 16px; + padding: 15px 20px; + margin: 20px auto; + border-radius: 10px; + max-width: 700px; + border: 2px solid rgba(255, 255, 255, 0.3); + box-shadow: 0 0 10px rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.05); + backdrop-filter: blur(6px); + color: white; + text-align: center; + position: relative; + transition: all 0.3s ease-in-out; + cursor: default; +} + +.status-message .dismiss-button { + position: absolute; + top: 8px; + right: 12px; + font-size: 20px; + color: white; + cursor: pointer; + transition: color 0.3s; +} + +.status-message .dismiss-button:hover { + color: red; +} + +.status-message.dismissed { + opacity: 0; + transform: scale(0.95); + height: 0; + margin: 0; + padding: 0; + border: 0; + pointer-events: none; +} + diff --git a/composer.json b/composer.json index fa8421a..28c4e10 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "TimeTrack is a PHP-written time recording tool for small businesses", "type": "software", "license": "GNU GPL", - "version": "7.12", + "version": "7.13.1", "authors": [ { "name": "Ente", diff --git a/errors/403.html b/errors/403.html index 5ad6e63..19662c5 100644 --- a/errors/403.html +++ b/errors/403.html @@ -13,7 +13,7 @@

403 | This request has been denied

Your request to access this ressource has been denied.

-

Either you do not have the permissions to access the ressource or it simply does not exist (anymore).

+

Either you do not have the permissions to access the resource or it simply does not exist (anymore).

Please check back later, thank you!

diff --git a/errors/500.php b/errors/500.php index 7179978..1ff6035 100644 --- a/errors/500.php +++ b/errors/500.php @@ -21,7 +21,7 @@

500 | Internal Server Error

-

An critical error occured - Code:

+

A critical error occurred - Code:

Message:

Trace (if available):

                 
diff --git a/errors/503.html b/errors/503.html
index 6f9fac5..42d4488 100644
--- a/errors/503.html
+++ b/errors/503.html
@@ -12,8 +12,8 @@
         

503 | Service not available

-

Maintenance or an System failure

-

This service is currently not available either to an maintenance or an system failure

+

Maintenance or a System failure

+

This service is currently not available either to a maintenance or a system failure

Please check back later, thank you!

diff --git a/migrations/migrations/20250510162046_init_token_scheme.php b/migrations/migrations/20250510162046_init_token_scheme.php new file mode 100644 index 0000000..4600f78 --- /dev/null +++ b/migrations/migrations/20250510162046_init_token_scheme.php @@ -0,0 +1,26 @@ +hasTable("tokens")) { + $table = $this->table('tokens', ['id' => false, 'primary_key' => 'id']); + $table->addColumn('id', 'integer', ['identity' => true]) + ->addColumn('user_id', 'integer') + ->addColumn('access_token', 'string', ['limit' => 255]) + ->addColumn('refresh_token', 'string', ['limit' => 255]) + ->addColumn('expires_at', 'datetime') + ->addColumn('created_at', 'datetime') + ->addColumn('updated_at', 'datetime') + ->create(); + } else { + echo "Table tokens already exists\n"; + } + } +} diff --git a/migrations/migrations/20250514181409_add_worktime_type_row.php b/migrations/migrations/20250514181409_add_worktime_type_row.php new file mode 100644 index 0000000..7556c18 --- /dev/null +++ b/migrations/migrations/20250514181409_add_worktime_type_row.php @@ -0,0 +1,27 @@ +table('arbeitszeiten')->hasColumn('Wtype')) { + $this->table('arbeitszeiten') + ->addColumn('Wtype', 'enum', [ + 'values' => ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], + 'default' => '0', + 'null' => false, + 'after' => 'pause_end' + ]) + ->update(); + $this->execute('UPDATE arbeitszeiten SET Wtype = "0" WHERE type IS NULL'); + echo "Added Wtype column to arbeitszeiten table\n"; + } else { + echo "Wtype column already exists in arbeitszeiten table\n"; + } + } +} diff --git a/suite/actions/auth/logout.php b/suite/actions/auth/logout.php index 0cd9486..95c8578 100644 --- a/suite/actions/auth/logout.php +++ b/suite/actions/auth/logout.php @@ -7,9 +7,9 @@ $base_url = $ini = $arbeit->get_app_ini()["general"]["base_url"]; $arbeit->auth()->login_validation(); if($arbeit->auth()->logout() == true){ - header("Location: http://{$base_url}/suite/?info=logged_out"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("logged_out")); } else { - header("Location: http://{$base_url}/suite/?info=logged_out_e"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("error")); } ?> \ No newline at end of file diff --git a/suite/actions/auth/reset.php b/suite/actions/auth/reset.php index ba4a836..81c3677 100644 --- a/suite/actions/auth/reset.php +++ b/suite/actions/auth/reset.php @@ -15,7 +15,7 @@ if(isset($_POST["email"])){ $arbeitszeit->mails()->init(new PHPMailerMailsProvider($arbeitszeit, $arbeitszeit->benutzer()->get_username_from_email($_POST["email"]))); $arbeitszeit->mails()->sendMail("PasswordResetTemplate", ["username" => $arbeitszeit->benutzer()->get_username_from_email($_POST["email"]), "email" => $_POST["email"]]); - header("Location: /suite/login.php?info=password_reset"); + header("Location: /suite/login.php?" . $arbeitszeit->statusMessages()->URIBuilder("password_reset")); } if(isset($_POST["password"]) == true && isset($_POST["auth"]) == true){ @@ -32,10 +32,10 @@ if($res){ $arbeitszeit->mails()->init(new PHPMailerMailsProvider($arbeitszeit, $data["username"])); $arbeitszeit->mails()->sendMail("PasswordChangedTemplate", ["username" => $data["username"], "email" => $_POST["auth"]]); - header("Location: /suite/login.php?info=password_changed"); + header("Location: /suite/login.php?" . $arbeitszeit->statusMessages()->URIBuilder("password_changed")); } else { Exceptions::error_rep("Could not change password as the query failed!. See previous message for more information."); - header("Location: /suite/login.php?info=password_change_failed"); + header("Location: /suite/login.php?". $arbeitszeit->statusMessages()->URIBuilder("password_change_failed")); } } else { Exceptions::error_rep("Could not reset password as the user could not be found! | Email: " . $_POST["auth"]); diff --git a/suite/actions/users/toggle_easymode.php b/suite/actions/users/toggle_easymode.php index 596cad0..4ad8dd5 100644 --- a/suite/actions/users/toggle_easymode.php +++ b/suite/actions/users/toggle_easymode.php @@ -6,7 +6,7 @@ $arbeit->auth()->login_validation(); $t = $arbeit->toggle_easymode($_SESSION["username"]); if($t == true){ - header("Location: http://{$base_url}/suite/?info=easymode_toggled"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("easymode_toggled")); die(); } ?> \ No newline at end of file diff --git a/suite/actions/worktime/add.php b/suite/actions/worktime/add.php index 8268857..0c75392 100644 --- a/suite/actions/worktime/add.php +++ b/suite/actions/worktime/add.php @@ -4,11 +4,11 @@ $worktime = new Arbeitszeit; $base_url = $worktime->get_app_ini()["general"]["base_url"]; $worktime->auth()->login_validation(); -$work = $worktime->add_worktime($_POST["time_start"], $_POST["time_end"], $_POST["ort"], $_POST["date"], $_POST["username"],"worktime", array("start" => $_POST["pause_start"], "end" => $_POST["pause_end"])); +$work = $worktime->add_worktime($_POST["time_start"], $_POST["time_end"], $_POST["ort"], $_POST["date"], $_POST["username"],"worktime", $_POST["Wtype"], array("start" => $_POST["pause_start"], "end" => $_POST["pause_end"])); if(!$work){ - header("Location: http://{$base_url}/suite/?info=error_worktime"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('error_worktime')); } else { - header("Location: http://{$base_url}/suite/?info=worktime_added"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('worktime_added')); } diff --git a/suite/actions/worktime/add_sick.php b/suite/actions/worktime/add_sick.php index 77f1b12..3b83267 100644 --- a/suite/actions/worktime/add_sick.php +++ b/suite/actions/worktime/add_sick.php @@ -6,9 +6,9 @@ $worktime->auth()->login_validation(); $sick = $worktime->sickness()->add_sickness(start: $_POST["date-start"], stop: $_POST["date-end"], user: $_SESSION["username"]); if(!$sick){ - header("Location: http://{$base_url}/suite/?info=error_sickness"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder("error_sickness")); } else { - header("Location: http://{$base_url}/suite/?info=sickness_added"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder("sickness_added")); } diff --git a/suite/actions/worktime/add_vacation.php b/suite/actions/worktime/add_vacation.php index 037067c..e45b69f 100644 --- a/suite/actions/worktime/add_vacation.php +++ b/suite/actions/worktime/add_vacation.php @@ -6,9 +6,9 @@ $worktime->auth()->login_validation(); $vacation = $worktime->vacation()->add_vacation(start: $_POST["date-start"], stop: $_POST["date-end"], username: $_SESSION["username"]); if(!$vacation){ - header("Location: http://{$base_url}/suite/?info=error_vacation"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('error_vacation')); } else { - header("Location: http://{$base_url}/suite/?info=vacation_added"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('vacation_added')); } diff --git a/suite/actions/worktime/easymode.php b/suite/actions/worktime/easymode.php index ecdba95..c7489b2 100644 --- a/suite/actions/worktime/easymode.php +++ b/suite/actions/worktime/easymode.php @@ -7,23 +7,23 @@ if($_POST["type"] == "start"){ $work = $worktime->add_easymode_worktime($_SESSION["username"]); - header("Location: http://{$base_url}/suite/?info=worktime_easymode_start"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('worktime_easymode_start')); die(); } elseif($_POST["type"] == "stop"){ $work = $worktime->end_easymode_worktime($_SESSION["username"], $_POST["id"]); - header("Location: http://{$base_url}/suite/?info=worktime_easymode_end"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('worktime_easymode_end')); die(); } elseif($_POST["type"] == "pause_start"){ $work = $worktime->start_easymode_pause_worktime($_SESSION["username"], $_POST["id"]); - header("Location: http://{$base_url}/suite/?info=worktime_easymode_pause_start"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('worktime_easymode_pause_start')); die(); } elseif($_POST["type"] == "pause_end"){ $work = $worktime->end_easymode_pause_worktime($_SESSION["username"], $_POST["id"]); - header("Location: http://{$base_url}/suite/?info=worktime_easymode_pause_end"); + header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('worktime_easymode_pause_end')); die(); } -header("Location: http://{$base_url}/suite/?info=error"); +header("Location: http://{$base_url}/suite/?" . $worktime->statusMessages()->URIBuilder('error')); die(); diff --git a/suite/admin/actions/notifications/add.php b/suite/admin/actions/notifications/add.php index 6dabdf5..fc7729b 100644 --- a/suite/admin/actions/notifications/add.php +++ b/suite/admin/actions/notifications/add.php @@ -8,15 +8,15 @@ $arbeit->auth()->login_validation(); if(!isset($_POST["datum"], $_POST["uhrzeit"], $_POST["ort"])){ - header("Location: http://{$base_url}/suite/?info=error"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("error")); } if($arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ if($arbeit->notifications()->create_notifications_entry($_POST["uhrzeit"], $_POST["datum"], $_POST["ort"], $_POST["notiz"]) == true){ - header("Location: http://{$base_url}/suite/?info=notifications_entry_added"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("notifications_entry_added")); } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } diff --git a/suite/admin/actions/notifications/delete.php b/suite/admin/actions/notifications/delete.php index 60fa5c3..5ee2583 100644 --- a/suite/admin/actions/notifications/delete.php +++ b/suite/admin/actions/notifications/delete.php @@ -9,10 +9,10 @@ $arbeit->auth()->login_validation(); if($arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ if($arbeit->notifications()->delete_notifications_entry($id) == true){ - header("Location: http://{$base_url}/suite/?info=notifications_entry_deleted"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("notifications_entry_deleted")); } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } diff --git a/suite/admin/actions/notifications/edit.php b/suite/admin/actions/notifications/edit.php index 916fbd1..e28102d 100644 --- a/suite/admin/actions/notifications/edit.php +++ b/suite/admin/actions/notifications/edit.php @@ -8,10 +8,10 @@ $arbeit->auth()->login_validation(); if($arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ if($arbeit->notifications()->edit_notifications_entry($_GET["id"], $_POST["uhrzeit"], $_POST["datum"], $_POST["ort"], $_POST["notiz"]) == true){ - header("Location: http://{$base_url}/suite/?info=notifications_entry_edited"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("notifications_entry_edited")); } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } diff --git a/suite/admin/actions/users/add_user.php b/suite/admin/actions/users/add_user.php index 79861a9..72156ce 100644 --- a/suite/admin/actions/users/add_user.php +++ b/suite/admin/actions/users/add_user.php @@ -16,11 +16,11 @@ $arbeit->mails()->init($provider); $arbeit->mails()->sendMail("NewUserTemplate", ["username" => $_POST["username"]]); $arbeit->mails()->sendMail("PasswordSendTemplate", ["username" => $_POST["username"], "password" => $_POST["password"]]); - echo ""; + echo ""; } else { - echo ""; + echo ""; } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } ?> \ No newline at end of file diff --git a/suite/admin/actions/users/delete_user.php b/suite/admin/actions/users/delete_user.php index fb40b71..214a759 100644 --- a/suite/admin/actions/users/delete_user.php +++ b/suite/admin/actions/users/delete_user.php @@ -9,13 +9,13 @@ $arbeit->auth()->login_validation(); if($arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ if($arbeit->benutzer()->get_user_from_id($id)["username"] == "api"){ - header("Location: http://{$base_url}/suite/?info=error"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("error")); // can't delete api user once created die(); } if($arbeit->benutzer()->delete_user($id) == true){ - header("Location: http://{$base_url}/suite/?info=user_deleted"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("user_deleted")); } } else { - header("Location http://{$base_url}/suite/?info=noperms"); + header("Location http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } ?> \ No newline at end of file diff --git a/suite/admin/actions/worktime/delete.php b/suite/admin/actions/worktime/delete.php index 99a32dc..4899346 100644 --- a/suite/admin/actions/worktime/delete.php +++ b/suite/admin/actions/worktime/delete.php @@ -9,10 +9,10 @@ $arbeit->auth()->login_validation(); if($arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ if($arbeit->delete_worktime($id) == true){ - header("Location: http://{$base_url}/suite/?info=worktime_deleted"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("worktime_deleted")); } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } diff --git a/suite/admin/actions/worktime/review.php b/suite/admin/actions/worktime/review.php index b067282..36662a4 100644 --- a/suite/admin/actions/worktime/review.php +++ b/suite/admin/actions/worktime/review.php @@ -14,12 +14,12 @@ if($arbeit->mark_for_review($id) == true){ $arbeit->mails()->init(new PHPMailerMailsProvider($arbeit, $_SESSION["username"], true)); $arbeit->mails()->sendMail("WorktimeUncompliantTemplate", ["username" => $_GET["u"], "worktime" => $id, "type" => 1]); - header("Location: http://{$base_url}/suite/?info=worktime_review"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("worktime_reviewed")); } else { echo "Error while processing..."; } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } diff --git a/suite/admin/actions/worktime/state_sickness.php b/suite/admin/actions/worktime/state_sickness.php index 23219e2..1b961c4 100644 --- a/suite/admin/actions/worktime/state_sickness.php +++ b/suite/admin/actions/worktime/state_sickness.php @@ -22,7 +22,7 @@ if (isset($_GET["id"], $_GET["new"])) { if (!$vacation->get_sickness($id, 2)) { Exceptions::error_rep("Could not find vacation with ID {$id}."); - header("Location: http://{$base_url}/suite/?info=error"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("error")); die(); } $arbeit->mails()->init(new PHPMailerMailsProvider($arbeit, $_SESSION["username"], true)); @@ -30,47 +30,47 @@ case "approve": if ($vacation->change_status($id, 1)) { $arbeit->mails()->sendMail("SicknessApprovedTemplate", ["username" => $_GET["u"], "id" => $id]); - header("Location: http://{$base_url}/suite/?info=changed_sickness"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("changed_sickness")); die(); } else { - Exceptions::error_rep("An error occured while changing status for sickness with id '{$id}'"); - header("Location: http://{$base_url}/suite/?info=noperms"); + Exceptions::error_rep("An error occurred while changing status for sickness with id '{$id}'"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } break; case "pending": if ($vacation->change_status($id, 3)) { $arbeit->mails()->sendMail("SicknessPendingTemplate", ["username" => $_GET["u"], "id" => $id]); - header("Location: http://{$base_url}/suite/?info=changed_sickness"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("changed_sickness")); die(); } else { - Exceptions::error_rep("An error occured while changing status for sickness with id '{$id}'"); - header("Location: http://{$base_url}/suite/?info=noperms"); + Exceptions::error_rep("An error occurred while changing status for sickness with id '{$id}'"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } break; case "reject": if ($vacation->change_status($id, 2)) { $arbeit->mails()->sendMail("SicknessRejectedTemplate", ["username" => $_GET["u"], "id" => $id]); - header("Location: http://{$base_url}/suite/?info=changed_sickness"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("changed_sickness")); die(); } else { - Exceptions::error_rep("An error occured while changing status for sickness with id '{$id}'"); - header("Location: http://{$base_url}/suite/?info=noperms"); + Exceptions::error_rep("An error occurred while changing status for sickness with id '{$id}'"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } break; default: if ($vacation->change_status($id, 3)) { $arbeit->mails()->sendMail("SicknessApprovedTemplate", ["username" => $_GET["u"], "id" => $id]); - header("Location: http://{$base_url}/suite/?info=changed_sickness"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("changed_sickness")); die(); } else { - Exceptions::error_rep("An error occured while chaning status for sickness with id '{$id}'"); - header("Location: http://{$base_url}/suite/?info=noperms"); + Exceptions::error_rep("An error occurred while changing status for sickness with id '{$id}'"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } break; } } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } diff --git a/suite/admin/actions/worktime/state_vacation.php b/suite/admin/actions/worktime/state_vacation.php index 4afbe1d..79b91be 100644 --- a/suite/admin/actions/worktime/state_vacation.php +++ b/suite/admin/actions/worktime/state_vacation.php @@ -15,55 +15,55 @@ if (isset($_GET["id"], $_GET["new"])) { if (!$vacation->get_vacation($id, 2)) { Exceptions::error_rep("Could not find vacation with ID {$id}."); - header("Location: http://{$base_url}/suite/?info=error"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("error")); die(); } - $arbeit->mails()->init(new PHPMailerMailsProvider($arbeit, $_SESSION["username"], true)); - switch ($_GET["new"]) { - case "approve": - if ($vacation->change_status($id, 1)) { - $arbeit->mails()->sendMail("VacationApprovedTemplate", ["username" => $_GET["u"], "id" => $id]); - header("Location: http://{$base_url}/suite/?info=changed_vacation"); - die(); - } else { - Exceptions::error_rep("An error occured while changing status for vacation with id '{$id}'"); - header("Location: http://{$base_url}/suite/?info=noperms"); - } - break; - case "pending": - if ($vacation->change_status($id, 3)) { - $arbeit->mails()->sendMail("VacationPendingTemplate", ["username" => $_GET["u"], "id" => $id]); - header("Location: http://{$base_url}/suite/?info=changed_vacation"); - die(); - } else { - Exceptions::error_rep("An error occured while changing status for vacation with id '{$id}'"); - header("Location: http://{$base_url}/suite/?info=noperms"); - } - break; - case "reject": - if ($vacation->change_status($id, 2)) { - $arbeit->mails()->sendMail("VacationRejectedTemplate", ["username" => $_GET["u"], "id" => $id]); - header("Location: http://{$base_url}/suite/?info=changed_vacation"); - die(); - } else { - Exceptions::error_rep("An error occured while chaning status for vacation with id '{$id}'"); - header("Location: http://{$base_url}/suite/?info=noperms"); - } - break; - default: - if ($vacation->change_status($id, 3)) { - $arbeit->mails()->sendMail("VacationApprovedTemplate", ["username" => $_GET["u"], "id" => $id]); - header("Location: http://{$base_url}/suite/?info=changed_vacation"); - die(); - } else { - Exceptions::error_rep("An error occured while chaning status for vacation with id '{$id}'"); - header("Location: http://{$base_url}/suite/?info=noperms"); - } - break; - } + } + $arbeit->mails()->init(new PHPMailerMailsProvider($arbeit, $_SESSION["username"], true)); + switch ($_GET["new"]) { + case "approve": + if ($vacation->change_status($id, 1)) { + $arbeit->mails()->sendMail("VacationApprovedTemplate", ["username" => $_GET["u"], "id" => $id]); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("changed_vacation")); + die(); + } else { + Exceptions::error_rep("An error occurred while changing status for vacation with id '{$id}'"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); + } + break; + case "pending": + if ($vacation->change_status($id, 3)) { + $arbeit->mails()->sendMail("VacationPendingTemplate", ["username" => $_GET["u"], "id" => $id]); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("changed_vacation")); + die(); + } else { + Exceptions::error_rep("An error occurred while changing status for vacation with id '{$id}'"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); + } + break; + case "reject": + if ($vacation->change_status($id, 2)) { + $arbeit->mails()->sendMail("VacationRejectedTemplate", ["username" => $_GET["u"], "id" => $id]); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("changed_vacation")); + die(); + } else { + Exceptions::error_rep("An error occurred while changing status for vacation with id '{$id}'"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); + } + break; + default: + if ($vacation->change_status($id, 3)) { + $arbeit->mails()->sendMail("VacationApprovedTemplate", ["username" => $_GET["u"], "id" => $id]); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("changed_vacation")); + die(); + } else { + Exceptions::error_rep("An error occurred while changing status for vacation with id '{$id}'"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); + } + break; } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } diff --git a/suite/admin/actions/worktime/unlock.php b/suite/admin/actions/worktime/unlock.php index 65dd7ca..b10cb05 100644 --- a/suite/admin/actions/worktime/unlock.php +++ b/suite/admin/actions/worktime/unlock.php @@ -14,12 +14,12 @@ if($arbeit->unlock_for_review($id) == true){ $arbeit->mails()->init(new PHPMailerMailsProvider($arbeit, $_SESSION["username"], true)); $arbeit->mails()->sendMail("WorktimeUncompliantTemplate", ["username" => $_GET["u"], "worktime" => $id, "type" => 0]); - header("Location: http://{$base_url}/suite/?info=worktime_review_unlock"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("worktime_review_unlocked")); } else { echo "Error while processing..."; } } else { - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } diff --git a/suite/admin/notifications/edit.php b/suite/admin/notifications/edit.php index 31b2d7e..1a5ff74 100644 --- a/suite/admin/notifications/edit.php +++ b/suite/admin/notifications/edit.php @@ -10,7 +10,7 @@ $arbeit->auth()->login_validation(); if(!@$arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($username))){ - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } $id = htmlspecialchars($_GET["id"]); $data = $arbeit->notifications()->get_notifications_entry($id); diff --git a/suite/admin/users/edit.php b/suite/admin/users/edit.php index 5bfafc6..1778004 100644 --- a/suite/admin/users/edit.php +++ b/suite/admin/users/edit.php @@ -10,7 +10,7 @@ $arbeit->auth()->login_validation(); if(!$arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } $add_style = ""; diff --git a/suite/admin/users/settings.php b/suite/admin/users/settings.php index 60e5f62..a4a7d42 100644 --- a/suite/admin/users/settings.php +++ b/suite/admin/users/settings.php @@ -13,7 +13,7 @@

{$language["log_title"]}

diff --git a/suite/admin/worktime/all.php b/suite/admin/worktime/all.php index 8252d16..746bb75 100644 --- a/suite/admin/worktime/all.php +++ b/suite/admin/worktime/all.php @@ -8,7 +8,7 @@ $loc = $arbeit->i18n()->loadLanguage(null, "worktime/all", "admin"); $arbeit->auth()->login_validation(); if(!$arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } if(!is_string(@$_POST["jahr"]) || !is_string(@$_POST["monat"])){ $date_year = date("Y"); @@ -48,6 +48,7 @@ + get_specific_worktime_html($date_month, $date_year) ?> diff --git a/suite/admin/worktime/sick/all.php b/suite/admin/worktime/sick/all.php index 38cba35..f5a558a 100644 --- a/suite/admin/worktime/sick/all.php +++ b/suite/admin/worktime/sick/all.php @@ -9,7 +9,7 @@ $arbeit->auth()->login_validation(); if(!$arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } if(!is_string(@$_POST["jahr"]) || !is_string(@$_POST["monat"])){ $date_year = date("Y"); diff --git a/suite/admin/worktime/vacation/all.php b/suite/admin/worktime/vacation/all.php index 18d5248..082a8f6 100644 --- a/suite/admin/worktime/vacation/all.php +++ b/suite/admin/worktime/vacation/all.php @@ -8,7 +8,7 @@ $loc = $arbeit->i18n()->loadLanguage(null, "worktime/vacation/all", "admin"); $arbeit->auth()->login_validation(); if(!$arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } if(!is_string(@$_POST["jahr"]) || !is_string(@$_POST["monat"])){ $date_year = date("Y"); diff --git a/suite/forgot_password.php b/suite/forgot_password.php index db5688d..0f34e7b 100644 --- a/suite/forgot_password.php +++ b/suite/forgot_password.php @@ -4,7 +4,6 @@ $arbeitszeit = new Arbeitszeit; $ini = $arbeitszeit->get_app_ini(); $loc = $arbeitszeit->i18n()->loadLanguage(NULL, "reset"); -echo $arbeitszeit->check_status_code($_SERVER["REQUEST_URI"]); ?> diff --git a/suite/index.php b/suite/index.php index 6226b3a..076c1de 100644 --- a/suite/index.php +++ b/suite/index.php @@ -22,7 +22,6 @@ notifications()->get_notifications_html(); ?> - check_status_code($_SERVER["REQUEST_URI"]); ?>

|

@@ -32,4 +31,4 @@

">

- \ No newline at end of file + diff --git a/suite/login.php b/suite/login.php index fea7d45..bddbdab 100644 --- a/suite/login.php +++ b/suite/login.php @@ -8,7 +8,6 @@ use NFClogin\NFClogin; $ini = $arbeit->get_app_ini(); $base_url = $ini["general"]["base_url"]; -echo $arbeit->check_status_code($_SERVER["REQUEST_URI"]); $language = $arbeit->i18n()->loadLanguage(NULL, "login"); ?> diff --git a/suite/notifications/all.php b/suite/notifications/all.php index c9abad4..324d08c 100644 --- a/suite/notifications/all.php +++ b/suite/notifications/all.php @@ -8,7 +8,7 @@ $loc = $arbeit->i18n()->loadLanguage(null, "notifications/all"); $arbeit->auth()->login_validation(); if(!$arbeit->benutzer()->is_admin($arbeit->benutzer()->get_user($_SESSION["username"]))){ - header("Location: http://{$base_url}/suite/?info=noperms"); + header("Location: http://{$base_url}/suite/?" . $arbeit->statusMessages()->URIBuilder("noperms")); } ?> diff --git a/suite/notifications/view.php b/suite/notifications/view.php index 4b19e51..c0c82e4 100644 --- a/suite/notifications/view.php +++ b/suite/notifications/view.php @@ -8,7 +8,7 @@ $data = $arbeit->notifications()->get_notifications_entry($id); if(isset($data["error"])){ - header("Location: /suite/?info=notification_not_found"); + header("Location: /suite/?" . $arbeit->statusMessages()->URIBuilder("notification_not_found")); exit; } diff --git a/suite/plugins/index.php b/suite/plugins/index.php index 02e1467..6ea1d40 100644 --- a/suite/plugins/index.php +++ b/suite/plugins/index.php @@ -28,7 +28,7 @@ $f = $pl->load_plugin_view($_GET["pn"], $_GET["p_view"]); if($f == false){ - echo "

An error occured while getting the requested view! Probably it simply does not exist or could not be imported. More information can be found within the log.

"; + echo "

An error occurred while getting the requested view! Probably it simply does not exist or could not be imported. More information can be found within the log.

"; print_r($_GET); } diff --git a/suite/users/changes.php b/suite/users/changes.php index 7a0c414..6e22a80 100644 --- a/suite/users/changes.php +++ b/suite/users/changes.php @@ -22,7 +22,6 @@ notifications()->get_notifications_html() ?> - check_status_code($_SERVER["REQUEST_URI"]); ?>

|

diff --git a/suite/worktime/all.php b/suite/worktime/all.php index de36743..ee0c431 100644 --- a/suite/worktime/all.php +++ b/suite/worktime/all.php @@ -36,6 +36,7 @@ + get_employee_worktime_html($_SESSION["username"]); ?>