diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45d0842..4d52d45 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,33 @@
# CHANGELOG
+## v8.2
+
+* Users are now able to propose corrections to worktimes when they have been marked as for "in review".
+
+## v8.1
+
+* Toil API release `1.13`:
+ * Added `editUser` endpoint
+ * Added `getOwnUser` endpoint
+ * `healthcheck` endpoint now includes the server time (ISO-8601) and API version
+* Enhanced `app.json` with the `mobile` section (enable/disable app, enable/disable API token generation within Settings, per-Client rate limit, enable/disable QR-code mobile pairing) - for future mobile app release
+* Fixed translations for German and English
+* Added example values to `LDAP` section within `app.json`
+* The ID of the worktime is now being displayed within `Worktime records` and `All worktime records`.
+* Updated `README.md`
+* **Update requires DB migration** (see `README.md` section `Database`)
+* Added projects (read more within the `README.md`)
+* Upgrading composer dependencies, please run `composer update`:
+ * `simple-router`: `4.3.7.2` to `5.0.0.3`
+* Added function to check if current user is admin
+* Added function to redirect directly to the suite page if an error occurs
+* Fixed an error causing infinite redirects to the 500 HTTP Page when the database is not available.
+
+## v8.0.2
+
+* Small fixes to the web UI
+* Updated `README.md`
+
## v8.0.1
* Readded the `nfclogin` button to the login page
diff --git a/README.md b/README.md
index aa8b019..61220cf 100644
--- a/README.md
+++ b/README.md
@@ -21,8 +21,8 @@ TimeTrack aims to be an easy-to-use time recording software for small enterprise
### Requirements
-- at least PHP 8.2 (`curl|gd|gmp|intl|mbstring|mysqli|openssl|xsl|gettext|dom|ldap`)
-- composer (to install dependencies; phpmailer: for sending emails via smtp, parsedown: markdown parser for the `CHANGELOG.md`, simple-router: does the API routing, yaml: for reading plugin yaml files, ldaptools: for LDAP authentication, dompdf: for PDF generation, phinx: for database migrations)
+- PHP 8.2 (`curl|gd|gmp|intl|mbstring|mysqli|openssl|xsl|gettext|dom|ldap`) - tested with PHP 8.2.26
+- composer (to install dependencies; phpmailer: for sending emails via smtp, parsedown: markdown parser for the `CHANGELOG.md`, simple-router: does the API routing, yaml: for reading plugin yaml files, ldaptools: for LDAP authentication, dompdf: for PDF generation, phinx: for database migrations, event-dispatcher: for handling events, contracts: for defining events and interfaces)
- Apache2.4 with enabled rewrite mod (optional)
This software has been tested on Debian 11/12, XAMPP, PHP internal server (e.g. `php -S 0.0.0.0:80`).
@@ -40,6 +40,9 @@ Simply install the software by following these steps:
- Start webserver e.g. `service apache2 stop && php -S 0.0.0.0:80` or using apache2 (then you have to configure the `sites-available` conf yourself)
- You can then access TimeTrack in your browser at `http://localhost`, default login is `admin` with password `admin`. Create yourself a new admin account, login and delete the default account afterwards.
+To save log files, please create the subfolder `data/logs` and make it writeable to the web server (e.g. `chown www-data:www-data data/logs && chmod 775 data/logs`).
+Please also make sure that the `/data` directory is writable by the webserver, aswell as the plugins directory (default: `api/v1/class/plugins/plugins`).
+
### Configure app.json
In step 2, you need to configure the `app.json.sample` within the `api/v1/inc` folder:
@@ -176,6 +179,13 @@ You can access the plugin by navigating to `Plugins` -> `[codeclock] View PIN`.
To login with the PIN navigate to http://BASE_URL/api/v1/toil/code and enter your PIN.
+## Projects
+
+Administrators can now create Projects within the `Projects management` tab.
+Users can access their projects within the `Projects` tab.
+
+You can create items for their projects and map worktimes to it. The feature will be reworked in the future.
+
## Updates
TimeTrack has to be updated in two ways: database and application.
@@ -184,6 +194,7 @@ TimeTrack has to be updated in two ways: database and application.
If downloaded from GitHub you can simply pull the latest release e.g. `git pull`
If downloaded any other way, just make sure to copy and paste the new files into TimeTrack's root directory.
+**Check the changelogs**: They usually tell you if you need to update composer dependencies (`composer update`) or if a database migration is required.
### Database
diff --git a/VERSION b/VERSION
index 5210382..8d1eec6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.0.1
\ No newline at end of file
+8.1
\ No newline at end of file
diff --git a/api/v1/class/arbeitszeit.inc.php b/api/v1/class/arbeitszeit.inc.php
index 2338780..d83c29d 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\Projects;
use Arbeitszeit\StatusMessages;
use Arbeitszeit\Events\EventDispatcherService;
use Arbeitszeit\Events\EasymodeWorktimeAddedEvent; // "EasymodeWorktimeSTARTED" Event, actually.
@@ -47,11 +48,13 @@ class Arbeitszeit
private $nodes;
private $statusMessages;
+ private $projects;
+
public function __construct()
{
$this->db = new DB();
$this->init_lang() ?? null;
- if(isset($this->get_app_ini()["general"]["timezone"])){
+ if (isset($this->get_app_ini()["general"]["timezone"])) {
try {
date_default_timezone_set($this->get_app_ini()["general"]["timezone"]);
} catch (\Exception $e) {
@@ -84,7 +87,7 @@ public function init_lang()
*/
public function delete_worktime($id)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "delete_worktime")){
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "delete_worktime")) {
return false;
}
$data = $this->db->sendQuery("DELETE FROM arbeitszeiten WHERE id = ?")->execute([$id]);
@@ -97,7 +100,7 @@ public function delete_worktime($id)
]
];
} else {
- EventDispatcherService::get()->dispatch(new WorktimeDeletedEvent($_SESSION["username"], (int)$id), WorktimeDeletedEvent::NAME);
+ EventDispatcherService::get()->dispatch(new WorktimeDeletedEvent($_SESSION["username"], (int) $id), WorktimeDeletedEvent::NAME);
Exceptions::error_rep("Worktime entry with ID '{$id}' deleted successfully.");
return 1;
}
@@ -107,7 +110,7 @@ public function delete_worktime($id)
public static function add_easymode_worktime($username)
{
$nodes = new Nodes;
- if(!$nodes->checkNode("arbeitszeit.inc", "add_easymode_worktime")) {
+ if (!$nodes->checkNode("arbeitszeit.inc", "add_easymode_worktime")) {
return false;
}
Exceptions::error_rep("Creating easymode worktime entry for user '{$username}'...");
@@ -138,7 +141,7 @@ public static function add_easymode_worktime($username)
public static function end_easymode_worktime($username, $id)
{
$nodes = new Nodes;
- if(!$nodes->checkNode("arbeitszeit.inc", "end_easymode_worktime")) {
+ if (!$nodes->checkNode("arbeitszeit.inc", "end_easymode_worktime")) {
return false;
}
Exceptions::error_rep("Ending easymode worktime for user '{$username}'...");
@@ -157,7 +160,7 @@ public static function end_easymode_worktime($username, $id)
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);
+ EventDispatcherService::get()->dispatch(new EasymodeWorktimeEndedEvent($username, (int) $id), EasymodeWorktimeEndedEvent::NAME);
Exceptions::error_rep("Easymode worktime ended for user '{$username}'");
return true;
}
@@ -166,7 +169,7 @@ public static function end_easymode_worktime($username, $id)
public function start_easymode_pause_worktime($username, $id)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "start_easymode_pause_worktime")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "start_easymode_pause_worktime")) {
return false;
}
Exceptions::error_rep("Starting easymode pause for user '{$username}'...");
@@ -184,7 +187,7 @@ public function start_easymode_pause_worktime($username, $id)
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);
+ EventDispatcherService::get()->dispatch(new EasymodeWorktimePauseStartEvent($username, (int) $id), EasymodeWorktimePauseStartEvent::NAME);
Exceptions::error_rep("Easymode pause started for user '{$username}'");
return true;
}
@@ -192,7 +195,7 @@ public function start_easymode_pause_worktime($username, $id)
}
public function end_easymode_pause_worktime($username, $id)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "end_easymode_pause_worktime")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "end_easymode_pause_worktime")) {
return false;
}
Exceptions::error_rep("Ending easymode pause for user '{$username}'...");
@@ -210,7 +213,7 @@ public function end_easymode_pause_worktime($username, $id)
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);
+ EventDispatcherService::get()->dispatch(new EasymodeWorktimePauseEndEvent($username, (int) $id), EasymodeWorktimePauseEndEvent::NAME);
Exceptions::error_rep("Easymode pause ended for user '{$username}'");
return true;
}
@@ -219,7 +222,7 @@ public function end_easymode_pause_worktime($username, $id)
public function toggle_easymode($username)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "toggle_easymode")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "toggle_easymode")) {
return false;
}
Exceptions::error_rep("Toggling easymode for user '{$username}'...");
@@ -254,7 +257,7 @@ public function toggle_easymode($username)
public function get_easymode_status($username, $mode = 0)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_easymode_status")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "get_easymode_status")) {
return false;
}
Exceptions::error_rep("Getting easymode status for user '{$username}'...");
@@ -294,7 +297,7 @@ public function get_easymode_status($username, $mode = 0)
public static function check_easymode_worktime_finished($username)
{
$nodes = new Nodes;
- if(!$nodes->checkNode("arbeitszeit.inc", "check_easymode_worktime_finished")) {
+ if (!$nodes->checkNode("arbeitszeit.inc", "check_easymode_worktime_finished")) {
return false;
}
Exceptions::error_rep("Checking easymode worktime for user '{$username}'...");
@@ -332,7 +335,7 @@ public static function check_easymode_worktime_finished($username)
*/
public function add_worktime($start, $end, $location, $date, $username, $type, $Wtype = 0, $pause = null, $meta = null)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "add_worktime")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "add_worktime")) {
return false;
}
$user = new Benutzer;
@@ -346,7 +349,7 @@ public function add_worktime($start, $end, $location, $date, $username, $type, $
return false;
} else {
- if($this->type_from_int($Wtype) == false){
+ 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;
}
@@ -354,7 +357,7 @@ public function add_worktime($start, $end, $location, $date, $username, $type, $
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`, `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, $Wtype ,$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 occurred while creating an worktime entry. See previous message for more information.");
return false;
@@ -366,7 +369,57 @@ public function add_worktime($start, $end, $location, $date, $username, $type, $
}
}
- public static function type_from_int($int){
+ public function update_worktime($id, $array)
+ {
+ if(!$this->check_if_for_review($id)){
+ return false;
+ }
+ $allowed = [
+ "schicht_anfang",
+ "schicht_ende",
+ "ort"
+ ];
+
+ $fields = [];
+ $values = [];
+
+ foreach ($allowed as $col) {
+ if (isset($array[$col]) && $array[$col] !== "" && $array[$col] !== null && $array[$col] !== false) {
+ $fields[] = "$col = ?";
+ $values[] = $array[$col];
+ }
+ }
+
+ if (empty($fields)) {
+ return false;
+ }
+
+ $sql = "UPDATE arbeitszeiten SET " . implode(", ", $fields) . " WHERE id = ?";
+ $values[] = $id;
+
+ try {
+ $stmt = $this->db->sendQuery($sql);
+ $ok = $stmt->execute($values);
+
+ if (!$ok) {
+ return false;
+ }
+
+ if ($stmt->rowCount() === 0) {
+ return false;
+ }
+
+ return true;
+ } catch (\PDOException $e) {
+ Exceptions::error_rep("[WORKTIME] Update failed: " . $e->getMessage());
+ return false;
+ }
+ }
+
+
+
+ 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];
@@ -377,7 +430,8 @@ public static function type_from_int($int){
}
- public static function get_all_types(){
+ 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;
@@ -439,7 +493,7 @@ private static function sanitizeOutput($data)
public function get_all_worktime()
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_all_worktime")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "get_all_worktime")) {
return false;
}
Exceptions::error_rep("Getting all worktimes...");
@@ -459,7 +513,7 @@ public function get_all_worktime()
public function get_all_user_worktime($username)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_all_user_worktime")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "get_all_user_worktime")) {
return false;
}
Exceptions::error_rep("Getting all worktimes for user '{$username}'...");
@@ -479,7 +533,7 @@ public function get_all_user_worktime($username)
public function get_specific_worktime_html(int $month, int $year)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_specific_worktime_html")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "get_specific_worktime_html")) {
return false;
}
Exceptions::error_rep("Getting worktimes rendered for month '{$month}' and year '{$year}'...");
@@ -537,6 +591,7 @@ public function get_specific_worktime_html(int $month, int $year)
$rpe
$rum $rno
$rtn
+
$rqw
@@ -554,7 +609,7 @@ public function get_specific_worktime_html(int $month, int $year)
}
public function get_employee_worktime_html($username)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "get_employee_worktime_html")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "get_employee_worktime_html")) {
return false;
}
Exceptions::error_rep("Getting worktimes rendered for user '{$username}'...");
@@ -575,7 +630,7 @@ public function get_employee_worktime_html($username)
$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"]));
+ $rpe = @strftime("%H:%M", strtotime($row["pause_end"]));
if ($rps == "01:00" || $rps == null) {
$rps = "-";
@@ -584,12 +639,14 @@ public function get_employee_worktime_html($username)
$rpe = "-";
}
- if ($row["review"] != 0) {
+ if ($row["review"] == 1 || $row["review"] == '1') {
$rmn = "style='color: red;'" ?? null;
$rno = "⚠ {$this->i18n["to_review"]} ⚠" ?? null;
+ $rrr = "| {$this->i18n["propose_correction"]}" ?? null;
} else {
$rmn = null;
$rno = null;
+ $rrr = null;
}
$data = <<$rol
$rps
$rpe
-
$rum $rno
+
$rum $rno $rrr
$rtn
+
$rqw
@@ -618,7 +676,7 @@ public function get_employee_worktime_html($username)
public function mark_for_review($id)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "mark_for_review")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "mark_for_review")) {
return false;
}
Exceptions::error_rep("Marking worktime with ID '{$id}' for review...");
@@ -628,14 +686,31 @@ public function mark_for_review($id)
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);
+ EventDispatcherService::get()->dispatch(new WorktimeMarkedForReviewEvent($_SESSION["username"], (int) $id), WorktimeMarkedForReviewEvent::NAME);
return true;
}
}
+ public function check_if_for_review($id)
+ {
+ Exceptions::error_rep("Checking if worktime with ID '{$id}' is for review...");
+ $sql = "SELECT * FROM `arbeitszeiten` WHERE id = ? AND review = ?";
+ $res = $this->db->sendQuery($sql);
+ $res->execute([$id, 1]);
+
+ if (!$res) {
+ Exceptions::error_rep("An error occured while checking if worktime is for review. See previous message for more information.");
+ return false;
+ } else {
+ if ($res->rowCount() == 1) {
+ return true;
+ }
+ }
+ }
+
public function unlock_for_review($id)
{
- if(!$this->nodes()->checkNode("arbeitszeit.inc", "unlock_for_review")) {
+ if (!$this->nodes()->checkNode("arbeitszeit.inc", "unlock_for_review")) {
return false;
}
Exceptions::error_rep("Unlocking worktime from review with ID '{$id}'...");
@@ -645,7 +720,7 @@ public function unlock_for_review($id)
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);
+ EventDispatcherService::get()->dispatch(new WorktimeUnlockedFromReviewEvent($_SESSION["username"], (int) $id), WorktimeUnlockedFromReviewEvent::NAME);
return true;
}
}
@@ -721,22 +796,26 @@ public static function fix_easymode_worktime($username)
}
- public function blockIfNotAdmin(){
- if(!Benutzer::is_admin(Benutzer::get_user($_SESSION["username"]))){
- header("Location: /");
+ public function blockIfNotAdmin()
+ {
+ if (!Benutzer::is_admin(Benutzer::get_user($_SESSION["username"]))) {
+ $this->statusMessages()->redirect("error");
}
return false;
}
- public function global_dispatcher(): \Symfony\Component\EventDispatcher\EventDispatcher {
+ public function global_dispatcher(): \Symfony\Component\EventDispatcher\EventDispatcher
+ {
return \Arbeitszeit\Events\EventDispatcherService::get();
}
- public function getTimeTrackVersion(){
+ public function getTimeTrackVersion()
+ {
return file_get_contents(dirname(__DIR__, 3) . "/VERSION");
}
- public function getToilVersion(){
+ public function getToilVersion()
+ {
return file_get_contents(dirname(__DIR__, 1) . "/toil/VERSION");
}
@@ -830,6 +909,13 @@ public function statusMessages(): StatusMessages
$this->statusMessages = new StatusMessages;
return $this->statusMessages;
}
+
+ public function projects(): Projects
+ {
+ if (!$this->projects)
+ $this->projects = new Projects;
+ return $this->projects;
+ }
}
}
diff --git a/api/v1/class/benutzer/benutzer.arbeit.inc.php b/api/v1/class/benutzer/benutzer.arbeit.inc.php
index d7f076a..e5c410f 100644
--- a/api/v1/class/benutzer/benutzer.arbeit.inc.php
+++ b/api/v1/class/benutzer/benutzer.arbeit.inc.php
@@ -297,6 +297,22 @@ public static function is_admin($user)
}
}
+ public static function current_user_is_admin(){
+ if(self::get_current_user()["isAdmin"] == true){
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static function get_current_user(){
+ return self::get_user($_SESSION["username"]);
+ }
+
+ public static function get_name_from_id($id){
+ return self::get_user_from_id($id)["name"];
+ }
+
public function editUserProperties(mixed $username_or_id, string $name, mixed $value): bool
{
if($this->nodes()->checkNode("benutzer.inc", "editUserProperties") == false){
diff --git a/api/v1/class/i18n/admin/notifications/edit/snippets_DE.json b/api/v1/class/i18n/admin/notifications/edit/snippets_DE.json
index 824e062..9226b42 100644
--- a/api/v1/class/i18n/admin/notifications/edit/snippets_DE.json
+++ b/api/v1/class/i18n/admin/notifications/edit/snippets_DE.json
@@ -1,5 +1,5 @@
{
- "title": "Benachrichtigungeneintrag bearbeiten",
+ "title": "Benachrichtigungseintrag bearbeiten",
"label_date": "Datum",
"label_time": "Uhrzeit",
"label_location": "Ort",
diff --git a/api/v1/class/i18n/admin/projects/admin/snippets_DE.json b/api/v1/class/i18n/admin/projects/admin/snippets_DE.json
new file mode 100644
index 0000000..36fb66f
--- /dev/null
+++ b/api/v1/class/i18n/admin/projects/admin/snippets_DE.json
@@ -0,0 +1,19 @@
+{
+ "title": "Projekte - Admin",
+ "existing_projects": "Meine Projekte",
+ "th_id": "ID",
+ "th_name": "Name",
+ "th_deadline": "Deadline",
+ "th_owner": "Besitzer",
+ "th_actions": "Aktionen",
+ "btn_edit": "Bearbeiten",
+ "btn_delete": "Löschen",
+ "no_projects": "Keine Projekte gefunden...",
+ "add_project": "Füge ein neues Projekt hinzu",
+ "label_name": "Name",
+ "label_description": "Beschreibung",
+ "label_deadline": "Deadline",
+ "label_owner": "Besitzer",
+ "btn_add": "Projekt hinzufügen",
+ "delete_confirm": "Bist du sicher, dass du das Projekt löschen möchtest?"
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/admin/projects/admin/snippets_EN.json b/api/v1/class/i18n/admin/projects/admin/snippets_EN.json
new file mode 100644
index 0000000..23b3ad0
--- /dev/null
+++ b/api/v1/class/i18n/admin/projects/admin/snippets_EN.json
@@ -0,0 +1,19 @@
+{
+ "title": "Projects - Admin",
+ "existing_projects": "Below is a list for which projects you are a member in.",
+ "th_id": "ID",
+ "th_name": "Name",
+ "th_deadline": "Deadline",
+ "th_owner": "Owner",
+ "th_actions": "Actions",
+ "btn_edit": "Edit",
+ "btn_delete": "Delete",
+ "no_projects": "No projects found...",
+ "add_project": "Add a new project",
+ "label_name": "Name",
+ "label_description": "Description",
+ "label_assoc": "Project short name",
+ "label_owner": "Owner",
+ "btn_add": "Add project",
+ "delete_confirm": "Are you sure that you want to delete this project?"
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/admin/projects/edit/snippets_DE.json b/api/v1/class/i18n/admin/projects/edit/snippets_DE.json
new file mode 100644
index 0000000..9bd8742
--- /dev/null
+++ b/api/v1/class/i18n/admin/projects/edit/snippets_DE.json
@@ -0,0 +1,9 @@
+{
+ "title": "Projekt bearbeiten",
+ "label_name": "Projektname",
+ "label_description": "Projektbeschreibung",
+ "label_deadline": "Fällig bis",
+ "label_owner": "Projecteigentümer",
+ "btn_save": "Projekt bearbeiten.",
+ "btn_cancel": "Zurück."
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/admin/projects/edit/snippets_EN.json b/api/v1/class/i18n/admin/projects/edit/snippets_EN.json
new file mode 100644
index 0000000..f587de3
--- /dev/null
+++ b/api/v1/class/i18n/admin/projects/edit/snippets_EN.json
@@ -0,0 +1,9 @@
+{
+ "title": "Edit Project",
+ "label_name": "Project name",
+ "label_description": "Project description",
+ "label_deadline": "Project deadline",
+ "label_owner": "Project Owner",
+ "btn_save": "Edit Project.",
+ "btn_cancel": "Cancel."
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/admin/users/edit/snippets_DE.json b/api/v1/class/i18n/admin/users/edit/snippets_DE.json
index 052a917..73dca01 100644
--- a/api/v1/class/i18n/admin/users/edit/snippets_DE.json
+++ b/api/v1/class/i18n/admin/users/edit/snippets_DE.json
@@ -1,7 +1,7 @@
{
"title": "Benutzer bearbeiten",
"add_user": "Benutzer hinzufügen",
- "p1": "Hier kannst du alle Nutzer bearbeiten oder neue hinzufügen oder löschen",
+ "p1": "Hier kannst du alle Benutzer bearbeiten oder Neue hinzufügen oder löschen",
"th1": "Aktion",
"th2": "Name",
"th3": "Benutzername",
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 bbce20a..d287f5e 100644
--- a/api/v1/class/i18n/admin/worktime/all/snippets_DE.json
+++ b/api/v1/class/i18n/admin/worktime/all/snippets_DE.json
@@ -13,5 +13,6 @@
"pbegin": "Pause Start",
"pend": "Pause Ende",
"loc": "Ort",
- "type": "Typ"
+ "type": "Typ",
+ "id": "ID"
}
\ 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 b83e6de..864bf82 100644
--- a/api/v1/class/i18n/admin/worktime/all/snippets_EN.json
+++ b/api/v1/class/i18n/admin/worktime/all/snippets_EN.json
@@ -13,5 +13,6 @@
"pbegin": "Break Start",
"pend": "end of break",
"loc": "location",
- "type": "type"
+ "type": "type",
+ "id": "ID"
}
\ 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 3b6cc58..1949da5 100644
--- a/api/v1/class/i18n/admin/worktime/all/snippets_NL.json
+++ b/api/v1/class/i18n/admin/worktime/all/snippets_NL.json
@@ -13,5 +13,6 @@
"pbegin": "Breekstart",
"pend": "einde einde",
"loc": "locatie",
- "type": "type"
+ "type": "type",
+ "id": "ID"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/admin/worktime/sick/all/snippets_DE.json b/api/v1/class/i18n/admin/worktime/sick/all/snippets_DE.json
index 571eb99..950aec6 100644
--- a/api/v1/class/i18n/admin/worktime/sick/all/snippets_DE.json
+++ b/api/v1/class/i18n/admin/worktime/sick/all/snippets_DE.json
@@ -1,6 +1,6 @@
{
"title": "Alle Krankheiten",
- "note1": "Unten siehst du eine Liste aller Krankheiten deiner Mitarbeiter.",
+ "note1": "Unten siehst du eine Liste aller Krankmeldungen deiner Mitarbeiter.",
"note2": "Geordnet: Neu zu alt, die letzten 100 Einträge",
"t1": "Mitarbeiter",
"t2": "Krankheit Beginn",
diff --git a/api/v1/class/i18n/admin/worktime/vacation/all/snippets_DE.json b/api/v1/class/i18n/admin/worktime/vacation/all/snippets_DE.json
index 1960294..6dc12f8 100644
--- a/api/v1/class/i18n/admin/worktime/vacation/all/snippets_DE.json
+++ b/api/v1/class/i18n/admin/worktime/vacation/all/snippets_DE.json
@@ -1,6 +1,6 @@
{
"title": "Alle Urlaube",
- "note1": "Unten siehst du eine Liste aller Urlaube deiner Mitarbeiter.",
+ "note1": "Unten siehst du eine Liste aller Urlaubsanträge deiner Mitarbeiter.",
"note2": "Geordnet: Neu zu alt, die letzten 100 Einträge",
"t1": "Mitarbeiter",
"t2": "Urlaub Beginn",
diff --git a/api/v1/class/i18n/admin/worktime/vacation/all/snippets_EN.json b/api/v1/class/i18n/admin/worktime/vacation/all/snippets_EN.json
index f20e450..71706ce 100644
--- a/api/v1/class/i18n/admin/worktime/vacation/all/snippets_EN.json
+++ b/api/v1/class/i18n/admin/worktime/vacation/all/snippets_EN.json
@@ -11,6 +11,7 @@
"pending": "Pending",
"or": "or",
"approved": "Approved",
- "rejected": "Rejected"
+ "rejected": "Rejected",
+ "not_found": "No vacation reports found."
}
}
\ 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 0a527bf..4369750 100644
--- a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_DE.json
+++ b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_DE.json
@@ -3,6 +3,7 @@
"easymode_disabled": "Easymode deaktiviert.",
"to_review": "Zur Prüfung",
"remove_review": "Prüfung aufheben",
+ "propose_correction": "Änderung vorschlagen",
"print": "(Drucken)",
"csv": "(CSV)",
"delete_entry": "Eintrag löschen",
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 ab76592..f83beb8 100644
--- a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_EN.json
+++ b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_EN.json
@@ -3,6 +3,7 @@
"easymode_disabled": "Easymode disabled.",
"to_review": "For review",
"remove_review": "Remove review",
+ "propose_correction": "Propose correction",
"print": "(Print)",
"csv": "(CSV)",
"delete_entry": "Delete entry",
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 f0c8a8c..93574a0 100644
--- a/api/v1/class/i18n/suite/class/arbeitszeit/snippets_NL.json
+++ b/api/v1/class/i18n/suite/class/arbeitszeit/snippets_NL.json
@@ -3,6 +3,7 @@
"easymode_disabled": "Easymode uitgeschakeld.",
"to_review": "Ter beoordeling",
"remove_review": "Review verwijderen",
+ "propose_correction": "Propose Correction",
"print": "(Afdrukken)",
"csv": "(CSV)",
"delete_entry": "Invoer verwijderen",
diff --git a/api/v1/class/i18n/suite/nav/snippets_DE.json b/api/v1/class/i18n/suite/nav/snippets_DE.json
index 7ca2082..2a61043 100644
--- a/api/v1/class/i18n/suite/nav/snippets_DE.json
+++ b/api/v1/class/i18n/suite/nav/snippets_DE.json
@@ -3,10 +3,12 @@
"settings": "Einstellungen",
"own_worktime": "Eigene Arbeitszeiten",
"notifications": "Benachrichtigungen",
+ "projects": "Projekte",
"logout": "Abmelden",
"a_allworktime": "Alle Arbeitszeiten",
"a_useredit": "Benutzer bearbeiten",
"a_sickness": "Alle Krankheiten",
"a_vacation": "Alle Urlaube",
- "a_plugins": "Plugins"
+ "a_plugins": "Plugins",
+ "a_projects": "Projektmanagement"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/nav/snippets_EN.json b/api/v1/class/i18n/suite/nav/snippets_EN.json
index 97a5875..9f0fc29 100644
--- a/api/v1/class/i18n/suite/nav/snippets_EN.json
+++ b/api/v1/class/i18n/suite/nav/snippets_EN.json
@@ -4,9 +4,11 @@
"own_worktime": "Worktime records",
"notifications": "Calendar",
"logout": "Logout",
+ "projects": "Projects",
"a_allworktime": "All worktime records",
"a_useredit": "Edit users",
"a_sickness": "Sickness reports",
"a_vacation": "Vacation reports",
- "a_plugins": "Plugins"
+ "a_plugins": "Plugins",
+ "a_projects": "Projects management"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/nav/snippets_NL.json b/api/v1/class/i18n/suite/nav/snippets_NL.json
index 4671610..ef441e6 100644
--- a/api/v1/class/i18n/suite/nav/snippets_NL.json
+++ b/api/v1/class/i18n/suite/nav/snippets_NL.json
@@ -4,9 +4,11 @@
"own_worktime": "Eigen werktijden",
"notifications": "Benachrichtigungen",
"logout": "Uitloggen",
+ "projects": "Projects",
"a_allworktime": "Alle werktijden",
"a_useredit": "Gebruiker bewerken",
"a_sickness": "Alle ziekten",
"a_vacation": "Alle vakanties",
- "a_plugins": "Plug-ins"
+ "a_plugins": "Plug-ins",
+ "a_projects": "Projects management"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/projects/addUser/snippets_EN.json b/api/v1/class/i18n/suite/projects/addUser/snippets_EN.json
new file mode 100644
index 0000000..aad17cb
--- /dev/null
+++ b/api/v1/class/i18n/suite/projects/addUser/snippets_EN.json
@@ -0,0 +1,10 @@
+{
+ "title": "Add User to Project",
+ "title2": "Add User to Project",
+ "label_userid": "User ID",
+ "label_role": "Role",
+ "label_permissions": "Permissions",
+ "tooltip_permissions": "0 = Member, 1 = Project Admin",
+ "btn_add": "Add.",
+ "btn_cancel": "Cancel."
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/projects/createItem/snippets_EN.json b/api/v1/class/i18n/suite/projects/createItem/snippets_EN.json
new file mode 100644
index 0000000..0f1ceb7
--- /dev/null
+++ b/api/v1/class/i18n/suite/projects/createItem/snippets_EN.json
@@ -0,0 +1,7 @@
+{
+ "title": "Create Project Item",
+ "description": "Description",
+ "assignee": "Assignee (UserID)",
+ "btn_save": "Save.",
+ "btn_cancel": "Cancel."
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/projects/item/snippets_EN.json b/api/v1/class/i18n/suite/projects/item/snippets_EN.json
new file mode 100644
index 0000000..89fabea
--- /dev/null
+++ b/api/v1/class/i18n/suite/projects/item/snippets_EN.json
@@ -0,0 +1,15 @@
+{
+ "title": "View Item",
+ "no_description": "No description...",
+ "project": "Project",
+ "assignee": "Assignee",
+ "status": "Status",
+ "worktimes": "Worktimes",
+ "th_user": "User",
+ "th_hours": "Hours",
+ "th_date": "Date",
+ "no_worktimes": "No worktimes...",
+ "btn_edit": "Edit",
+ "btn_delete": "Delete",
+ "id": "ID"
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/projects/overview/snippets_EN.json b/api/v1/class/i18n/suite/projects/overview/snippets_EN.json
new file mode 100644
index 0000000..b1db03c
--- /dev/null
+++ b/api/v1/class/i18n/suite/projects/overview/snippets_EN.json
@@ -0,0 +1,15 @@
+{
+ "title": "Overview - Projects",
+ "intro": "This is the overview of the projects tab.",
+ "your_projects": "Your projects",
+ "link_view_project": "View project",
+ "no_projects": "No projects...",
+ "th_item_title": "Item title",
+ "th_project": "Project",
+ "th_status": "Status",
+ "th_actions": "Actions",
+ "your_items": "Your items",
+ "btn_view_item": "View item",
+ "no_items": "No items...",
+ "id": "ID"
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/projects/view/snippets_EN.json b/api/v1/class/i18n/suite/projects/view/snippets_EN.json
new file mode 100644
index 0000000..e0542f6
--- /dev/null
+++ b/api/v1/class/i18n/suite/projects/view/snippets_EN.json
@@ -0,0 +1,23 @@
+{
+ "title": "View Project",
+ "no_description": "No description...",
+ "description": "Description",
+ "deadline": "Deadline",
+ "tab_items": "Items",
+ "tab_members": "Members",
+ "tab_worktimes": "Worktimes",
+ "items": "Items",
+ "th_item": "Item",
+ "th_assignee": "Assignee",
+ "th_status": "Status",
+ "th_actions": "Actions",
+ "btn_view": "View",
+ "no_items": "No items...",
+ "members": "Members",
+ "no_members": "No members...",
+ "worktimes": "Worktimes",
+ "th_user": "User",
+ "th_hours": "Hours",
+ "no_worktimes": "No worktimes...",
+ "id": "ID"
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/status/snippets_DE.json b/api/v1/class/i18n/suite/status/snippets_DE.json
index 14ec08d..293e834 100644
--- a/api/v1/class/i18n/suite/status/snippets_DE.json
+++ b/api/v1/class/i18n/suite/status/snippets_DE.json
@@ -26,5 +26,17 @@
"statemismatch": "Fehler: Sicherheitsfehler",
"ldapauth": "Fehler: Die Authentifizierung über LDAP ist fehlgeschlagen.",
"ldapcreated": "Hinweis: Bitte melde dich erneut an. Dein Benutzerkonto wurde nun erstellt. (LDAP allowed self-login)",
- "notification_not_found": "Fehler: Benachrichtigung nicht gefunden."
+ "notification_not_found": "Fehler: Benachrichtigung nicht gefunden.",
+ "project_deleted": "Hinweis: Das Projekt wurde erfolgreich gelöscht.",
+ "project_deleted_failed": "Fehler: Das Projekt konnte nicht gelöscht werden. Bitte wende dich an deinen Administrator.",
+ "project_edited": "Hinweis: Das Projekt wurde erfolgreich bearbeitet.",
+ "project_edited_error": "Fehler: Das Projekt konnte nicht gelöscht werden. Bitte wende dich an deinen Administrator.",
+ "project_added": "Hinweis: Das Projekt wurde erfolgreich erstellt.",
+ "project_added_failed": "Fehler: Das Projekt konnte nicht erstellt werden. Bitte wende dich an deinen Administrator.",
+ "project_userAdded": "Hinweis: Der Benutzer hat nun Zugriff auf das freigegebene Projekt.",
+ "project_userAddeed_failed": "Fehler: Dem Benutzer konnten die Berechtigungen nicht zugewiesen werden. Bitte wende dich an deinen Administrator.",
+ "projects_item_added": "Hinweis: Projektaufgabe hinzugefügt.",
+ "projects_item_failed": "Fehler: Die Projektaufgabe konnte nicht hinzugefügt werden. Bitte wende dich an deinen Administrator.",
+ "mapWorktimeToItem_success": "Hinweis: Arbeitszeit erfolgreich mit der Projektaufgabe verbunden.",
+ "mapWorktimeToItem_failed": "Fehler: Die Arbeitszeit konnte nicht mit der Projektaufgabe verbunden werden. Bitte wende dich an deinen Administrator."
}
diff --git a/api/v1/class/i18n/suite/status/snippets_EN.json b/api/v1/class/i18n/suite/status/snippets_EN.json
index baf04cf..d89396f 100644
--- a/api/v1/class/i18n/suite/status/snippets_EN.json
+++ b/api/v1/class/i18n/suite/status/snippets_EN.json
@@ -26,5 +26,17 @@
"statemismatch": "Error: Security error",
"ldapauth": "Error: LDAP authentication failed.",
"ldapcreated": "Note: Please log in again. Your user account has now been created. (LDAP allowed self-login)",
- "notification_not_found": "Error: Notification not found."
+ "notification_not_found": "Error: Notification not found.",
+ "project_deleted": "Note: Project successfully deleted.",
+ "project_deleted_failed": "Error: The project could not be deleted. Please contact your administrator.",
+ "project_edited": "Note: The changes to the project have been applied.",
+ "project_edited_error": "Error: An error occurred while applying the changes to the project. Please contact your administrator.",
+ "project_added": "Note: The project has been succesfully added.",
+ "project_added_error": "Error: An error occurred while creating the new project. Please contact your administrator.",
+ "project_userAdded": "Note: The user has been successfully added to the project.",
+ "project_userAdded_failed": "Error: An error occurred while assigning the permissions to the user. Please contact your administrator.",
+ "projects_item_added": "Note: The item has been added to the project.",
+ "projects_item_failed": "Error: An error occurred while creating the item. Please contact your administrator.",
+ "mapWorktimeToItem_success": "Note: Worktime successfully mapped to project item.",
+ "mapWorktimeToItem_failed": "Error: An error occurred while mapping the worktime to the project itme. Please contact your administrator."
}
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 c52053f..c2aaf41 100644
--- a/api/v1/class/i18n/suite/worktime/all/snippets_DE.json
+++ b/api/v1/class/i18n/suite/worktime/all/snippets_DE.json
@@ -7,5 +7,6 @@
"s_pstart": "Pause Start",
"s_pend": "Pause Ende",
"s_location": "Ort",
- "type": "Typ"
+ "type": "Typ",
+ "id": "ID"
}
\ 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 5c2322a..c13a4bc 100644
--- a/api/v1/class/i18n/suite/worktime/all/snippets_EN.json
+++ b/api/v1/class/i18n/suite/worktime/all/snippets_EN.json
@@ -7,5 +7,6 @@
"s_pstart": "Pause Start",
"s_pend": "Pause End",
"s_location": "Location",
- "type": "Type"
+ "type": "Type",
+ "id": "ID"
}
\ 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 b45eabf..f54406a 100644
--- a/api/v1/class/i18n/suite/worktime/all/snippets_NL.json
+++ b/api/v1/class/i18n/suite/worktime/all/snippets_NL.json
@@ -7,5 +7,6 @@
"s_pstart": "Start pauzeren",
"s_pend": "Einde einde",
"s_location": "Locatie",
- "type": "Type"
+ "type": "Type",
+ "id": "ID"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/worktime/correction/snippets_DE.json b/api/v1/class/i18n/suite/worktime/correction/snippets_DE.json
new file mode 100644
index 0000000..4252bb2
--- /dev/null
+++ b/api/v1/class/i18n/suite/worktime/correction/snippets_DE.json
@@ -0,0 +1,15 @@
+{
+ "title": "Änderung an einer Arbeitszeit vorschlagen",
+ "h2": "Derzeitige Daten",
+ "start": "Start",
+ "end": "Ende",
+ "comment": "Kommentar",
+ "suggest_change": "Änderung vorschlagen",
+ "new_start": "Neue Startzeit",
+ "new_end": "Neues Ende",
+ "new_comment": "Neuer Kommentar",
+ "reason": "Begründung",
+ "tooltip_reason": "Bitte gebe eine Begründung an, warum du nun die Daten änderst. Diese wird dem Kommentarfeld hinzugefügt.",
+ "btn_submit": "Abschicken",
+ "date": "Datum"
+}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/worktime/correction/snippets_EN.json b/api/v1/class/i18n/suite/worktime/correction/snippets_EN.json
new file mode 100644
index 0000000..3d5afdd
--- /dev/null
+++ b/api/v1/class/i18n/suite/worktime/correction/snippets_EN.json
@@ -0,0 +1,15 @@
+{
+ "title": "Propose a correction for a worktime",
+ "h2": "Current data",
+ "start": "Start",
+ "end": "End",
+ "comment": "Comment",
+ "suggest_change": "Propose Correction",
+ "new_start": "New start time",
+ "new_end": "New end time",
+ "new_comment": "New comment",
+ "reason": "Reason",
+ "tooltip_reason": "Please provide a reason why you are proposing a correction. The reason will be attached to the comment.",
+ "btn_submit": "Submit",
+ "date": "Date"
+}
\ No newline at end of file
diff --git a/api/v1/class/plugins/PluginBuilder.plugins.arbeit.inc.php b/api/v1/class/plugins/PluginBuilder.plugins.arbeit.inc.php
index a0c219d..8a264c2 100644
--- a/api/v1/class/plugins/PluginBuilder.plugins.arbeit.inc.php
+++ b/api/v1/class/plugins/PluginBuilder.plugins.arbeit.inc.php
@@ -129,7 +129,7 @@ final public function load_class($class, $name): void{
*
* @return bool|void If everything went ok, void. If an error occurs false bool.
*/
- final public function initialize_plugins(): ?bool {
+ final public function initialize_plugins(): bool {
if ($this->testing == true) {
$plugins = $this->get_plugins();
if ($plugins == false) {
@@ -154,10 +154,10 @@ final public function initialize_plugins(): ?bool {
return true;
} elseif ($this->testing == false) {
- // Keine Aktion notwendig
} else {
return false;
}
+ return false;
}
@@ -195,7 +195,7 @@ final public function read_plugin_configuration($name, $raw = false): array|stri
return (array)$yaml;
} else {
Exceptions::error_rep("{$la} Could not read plugin configuration for plugin '{$name}' - Path: " . $_SERVER["DOCUMENT_ROOT"] . "" . $this->basepath . "/" . $name . "/plugin.yml");
- return null;
+ return false;
}
}
@@ -205,7 +205,7 @@ final public function read_plugin_configuration($name, $raw = false): array|stri
*
* @return array|void Returns and array on success. Nothing otherwise
*/
- final public function get_plugins(): array{
+ final public function get_plugins(): array|bool{
$this->logger("{$this->la} Getting all plugins...");
$dir = array_diff(scandir($_SERVER["DOCUMENT_ROOT"]. "" . $this->get_basepath()), array(".", "..", "data"));
if($dir == false){
@@ -224,6 +224,7 @@ final public function get_plugins(): array{
return $data;
}
}
+ return false;
}
@@ -299,7 +300,7 @@ final public function memorize_plugin($name, $additional_payload = null): bool{
* @param string $name Class name of the plugin
* @return object|bool|Exception Returns the class on success and either false or an Exception on failure
*/
- final public function unmemorize_plugin($name): object{
+ final public function unmemorize_plugin($name): object|bool{
Exceptions::deprecated(__FUNCTION__, "This function is not supported anymore.");
$this->logger("{$this->la} Unmemorizing plugin '{$name}'...");
$plugin = $this->read_plugin_configuration($name);
diff --git a/api/v1/class/plugins/plugins/userdetail/plugin.yml b/api/v1/class/plugins/plugins/userdetail/plugin.yml
index b860420..2a4f72a 100644
--- a/api/v1/class/plugins/plugins/userdetail/plugin.yml
+++ b/api/v1/class/plugins/plugins/userdetail/plugin.yml
@@ -4,7 +4,7 @@ main: Main
namespace: userdetail
author: Ente
description: Save more information about your employees
-version: "1.2"
+version: "1.2.1"
api: 0.1
permissions: none
enabled: true
diff --git a/api/v1/class/plugins/plugins/userdetail/src/Main.php b/api/v1/class/plugins/plugins/userdetail/src/Main.php
index 4cd7940..15c5e16 100644
--- a/api/v1/class/plugins/plugins/userdetail/src/Main.php
+++ b/api/v1/class/plugins/plugins/userdetail/src/Main.php
@@ -8,7 +8,6 @@
use Arbeitszeit\Benutzer;
use Arbeitszeit\Exceptions;
use Arbeitszeit\PluginBuilder;
-use Arbeitszeit\Hooks;
use Arbeitszeit\PluginInterface;
class Userdetail extends PluginBuilder implements PluginInterface {
@@ -49,7 +48,6 @@ public function get_plugin_configuration(): array{
public function __construct(){
$this->set_log_append();
$this->set_plugin_configuration();
- #Hooks::addHook("create_user", "callback", function(){echo "hi";}, "userdetail");
$this->check_folder();
}
@@ -102,10 +100,6 @@ public function compute_user_nav(){
return $html;
}
- public function add($name, $value, $user){
-
- }
-
public function save_employee_data($payload){
$this->logger("[userdetail] Saving employee data...");
$handle = fopen(dirname(__DIR__, 1) . "/data/" . $payload["username"] . ".json", "w+");
@@ -140,10 +134,6 @@ public function check_employee_file($username){
}
}
- public function create_user_callback($username, $name, $email, $password, $isAdmin){
- echo "Successfully created user account...";
- }
-
public function check_folder(){
if(!file_exists(dirname(__DIR__, 1) . "/data/")){
mkdir(dirname(__DIR__, 1) . "/data/");
diff --git a/api/v1/class/plugins/plugins/utility/plugin.yml b/api/v1/class/plugins/plugins/utility/plugin.yml
new file mode 100644
index 0000000..19c0fa4
--- /dev/null
+++ b/api/v1/class/plugins/plugins/utility/plugin.yml
@@ -0,0 +1,15 @@
+name: utility
+src: /src
+main: Main
+namespace: utility
+author: Ente
+description: 'Export all data from an user and more.'
+version: '1.0'
+api: 0.1
+permissions: none
+enabled: true
+custom.values:
+ license: LIC
+nav_links:
+ 'Open Utility Plugin': views/overview.php
+path: 'C:\Users\diege\Documents\GitHub\timetrack/api/v1/class/plugins/plugins/utility/plugin.yml'
diff --git a/api/v1/class/plugins/plugins/utility/src/Main.php b/api/v1/class/plugins/plugins/utility/src/Main.php
new file mode 100644
index 0000000..2463bb6
--- /dev/null
+++ b/api/v1/class/plugins/plugins/utility/src/Main.php
@@ -0,0 +1,172 @@
+read_plugin_configuration("utility")["version"];
+ $this->log_append = "[Utility] v{$v}";
+ }
+
+ public function get_log_append(): string
+ {
+ return $this->log_append;
+ }
+
+ public function set_plugin_configuration(): void
+ {
+ $this->plugin_configuration = $this->read_plugin_configuration("userdetail");
+ }
+
+ public function get_plugin_configuration(): array
+ {
+ return $this->plugin_configuration;
+ }
+
+ public function __construct()
+ {
+ $this->set_log_append();
+ $this->set_plugin_configuration();
+ $this->db = new DB();
+ }
+
+ public function onLoad(): void
+ {
+ $lga = $this->get_log_append();
+ $this->logger($lga . " Loading utility plugin...");
+ }
+
+ public function onEnable(): void
+ {
+
+ }
+
+ public function onDisable(): void
+ {
+
+ }
+
+ public function set_userData($data): void {
+ $this->userData = $data;
+ }
+
+ public function checkIfuserDataisSet(){
+ try{
+ if(isset($this->userData)){
+ return true;
+ } else {
+ return false;
+ }
+ } catch(\Exception $e){
+ StatusMessages::redirect("error");
+ }
+ }
+
+ public function exportAll($username): self{
+ $userData = Benutzer::get_user($username);
+ $this->userData = $userData;
+ $arr = [];
+ $arr["worktimes"] = $this->exportUserWorktimes();
+ $arr["sickness"] = $this->exportUserSickness();
+ $arr["vacations"] = $this->exportUserVacations();
+
+ $this->data = json_encode($arr, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
+ $this->filename = "export_user_" . $userData["username"] . "_" . date("Y-m-d_H-i-s") . ".json";
+
+ return $this;
+ }
+
+ public function download(): void{
+ if(!isset($this->data)){
+ StatusMessages::redirect("error");
+ }
+
+ header('Content-Type: application/json; charset=utf-8');
+ header('Content-Disposition: attachment; filename="' . $this->filename . '"');
+ header('Content-Length: ' . strlen($this->data));
+
+ echo $this->data;
+ exit;
+ }
+
+ public function getString(): string{
+ return $this->data;
+ }
+
+ public function exportUserWorktimes(): array|bool{
+ $this->checkIfuserDataisSet();
+ $sql = "SELECT * FROM `arbeitszeiten` WHERE name = ?";
+ $res = $this->db->sendQuery($sql);
+ $res->execute([$this->userData["username"]]);
+
+ $worktimes = [];
+ if($res->rowCount() >= 1){
+ while($data = $res->fetch(\PDO::FETCH_ASSOC)){
+ array_push($worktimes, $data);
+ }
+ return $worktimes;
+ } else {
+ return false;
+ }
+ }
+
+ public function exportUserSickness(): array|bool{
+ $this->checkIfuserDataisSet();
+ $sql = "SELECT * FROM `sick` WHERE username = ?";
+ $res = $this->db->sendQuery($sql);
+ $res->execute([$this->userData["username"]]);
+
+ $sickness = [];
+
+ if($res->rowCount() >= 1){
+ while($data = $res->fetch(\PDO::FETCH_ASSOC)){
+ array_push($sickness, $data);
+ }
+ return $sickness;
+ } else {
+ return false;
+ }
+ }
+
+ public function exportUserVacations(): array|bool{
+ $this->checkIfuserDataisSet();
+ $sql = "SELECT * FROM `vacation` WHERE username = ?";
+ $res = $this->db->sendQuery($sql);
+ $res->execute([$this->userData["username"]]);
+
+ $vacation = [];
+
+ if($res->rowCount() >= 1){
+ while($data = $res->fetch(\PDO::FETCH_ASSOC)){
+ array_push($vacation, $data);
+ }
+ return $vacation;
+ } else {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/api/v1/class/plugins/plugins/utility/views/download.php b/api/v1/class/plugins/plugins/utility/views/download.php
new file mode 100644
index 0000000..96482d7
--- /dev/null
+++ b/api/v1/class/plugins/plugins/utility/views/download.php
@@ -0,0 +1,23 @@
+auth()->login_validation();
+$a->benutzer()->current_user_is_admin();
+
+if(!isset($_POST["username"])){
+ $main->logger("[utility] Username not found. Aborting export...");
+ $a->statusMessages()->redirect("error");
+}
+
+$main->exportAll($_POST["username"])->download();
\ No newline at end of file
diff --git a/api/v1/class/plugins/plugins/utility/views/overview.php b/api/v1/class/plugins/plugins/utility/views/overview.php
new file mode 100644
index 0000000..c63bac0
--- /dev/null
+++ b/api/v1/class/plugins/plugins/utility/views/overview.php
@@ -0,0 +1,28 @@
+auth()->login_validation();
+$a->benutzer()->current_user_is_admin();
+?>
+
+
User Export
+
+
Please input the username of the user you want to export all data of below.