diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..63370ee
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,9 @@
+.git
+.gitignore
+vendor
+node_modules
+Dockerfile
+docker-compose.yml
+*.md
+*.log
+tests
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 268af8e..c335c57 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,45 @@
# CHANGELOG
+## v8.3.1
+
+* Removed deprecated `app` attribute from `general` section within `app.json`
+* Fixed issue stating that the account got disabled when logging out or when accessing the web UI
+
+## v8.3
+
+**This update requires DB migration** - see `README.md` section `Database`
+
+* Fixed an issue causing the plugin system to be always enabled
+* Sickness and vacation entries can now have a type like worktime entries
+* Fixed dispatching of `WorktimeAddedEvent` event with incorrect parameters
+* Added Database migration instructions when using Docker within the `README.md` `Database` section
+* Removed the `setup` folder entirely (deprecated)
+* Fixed DB migrations for Docker setups
+
+## v8.2.3
+
+**This update requires DB migration** - see `README.md` section `Database`
+
+* Fixed missing `status` i18n entries
+* Fixed incorrect theme loading within error pages
+* Removed outdated `index.css` file
+* Added missing footer to project management pages
+* Admins can now enable or disable users within the `userdetail` plugin
+
+## v8.2.2
+
+* Fixed deprecation warning for `WorktimeAddedEvent` event
+* Dockerized TimeTrack:
+ * Added `Dockerfile` and `docker-compose.yml` to run TimeTrack within Docker
+ * Added `entrypoint.sh` to handle database migrations and start Apache
+ * Updated `README.md` with Docker instructions
+
+## v8.2.1
+
+* Added events for worktime correction proposals: `WorktimeCorrectionProposed`
+* Sanitized outputs to prevent XSS attacks
+* Added a link to the documentation within the settings page
+
## v8.2
* Users are now able to propose corrections to worktimes when they have been marked as for "in review".
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..4afdbc7
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+github@openducks.org.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..f2a9e3e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,86 @@
+# ---- Base image ------------------------------------------------------------
+FROM php:8.2-apache
+
+# ---- System packages & PHP extensions -------------------------------------
+RUN apt-get update && apt-get install -y \
+ libcurl4-openssl-dev \
+ libgd-dev \
+ libgmp-dev \
+ libicu-dev \
+ libonig-dev \
+ libxml2-dev \
+ libxslt1-dev \
+ libldap2-dev \
+ libzip-dev \
+ zip \
+ unzip \
+ git \
+ locales \
+ && docker-php-ext-configure intl \
+ && docker-php-ext-configure gd --with-jpeg --with-freetype \
+ && docker-php-ext-install \
+ curl \
+ gd \
+ gmp \
+ intl \
+ mbstring \
+ mysqli \
+ pdo \
+ pdo_mysql \
+ xsl \
+ gettext \
+ dom \
+ ldap \
+ zip \
+ && a2enmod rewrite
+
+RUN apt-get update && apt-get install -y default-mysql-client
+
+
+ # ---- Add Python and smartcard support --------------------------------------
+RUN apt-get update && apt-get install -y \
+ python3 \
+ python3-pip \
+ pcscd \
+ pcsc-tools \
+ libpcsclite-dev \
+ && pip3 install pyscard \
+ && systemctl enable pcscd || true \
+&& rm -rf /var/lib/apt/lists/*
+
+
+RUN { \
+ echo "display_errors = Off"; \
+ echo "display_startup_errors = Off"; \
+ echo "log_errors = On"; \
+ echo "error_reporting = E_ALL & ~E_DEPRECATED & ~E_WARNING & ~E_NOTICE"; \
+ echo "error_log = /var/log/php_errors.log"; \
+ } > /usr/local/etc/php/conf.d/99-production.ini
+
+
+# ---- Locale configuration --------------------------------------------------
+RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
+
+# ---- Working directory -----------------------------------------------------
+WORKDIR /var/www/html
+
+# ---- Copy application files ------------------------------------------------
+COPY . /var/www/html
+
+# ---- Permissions -----------------------------------------------------------
+RUN chown -R www-data:www-data /var/www/html \
+ && chmod -R 755 /var/www/html
+
+# ---- Apache vHost ----------------------------------------------------------
+COPY docker/apache2.conf /etc/apache2/sites-available/000-default.conf
+
+# ---- Composer --------------------------------------------------------------
+COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
+RUN composer install --no-dev --no-interaction --optimize-autoloader
+
+# ---- Entry point -----------------------------------------------------------
+COPY docker/entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+EXPOSE 80
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/README.md b/README.md
index 4ee7138..0456851 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,23 @@ TimeTrack aims to be an easy-to-use time recording software for small enterprise
## Installation
+### Quick Install with Docker
+
+You can quickly get started with TimeTrack using Docker. Follow these steps:
+
+* Ensure you have Docker and Docker Compose installed on your system.
+* Clone the TimeTrack repository: `git clone https://github.com/Ente/timetrack.git` & `cd timetrack`
+* Build the Docker image: `docker build -t openducks/timetrack .`
+* Create a `app.json` configuration file based on the provided sample below: `cp api/v1/inc/app.json.sample api/v1/inc/app.json` and edit it to fit your needs.
+ * Adjust the database settings if needed (at least `db_password`)
+ * Change the base_url to match your setup (e.g. `localhost:8080`)
+ * **Change the DB password before using in production within the `docker-compose.yml` and `app.json` file!**
+* Start the services using Docker Compose: `docker-compose up -d`
+* Access TimeTrack in your web browser at `http://localhost:8080`
+* Login with username `admin` and password `admin`
+
+Certain features, like the NFC login may require additional setup for parsing the USB device.
+
### Requirements
- PHP 8.2 (`curl|gd|gmp|intl|mbstring|mysqli|openssl|xsl|gettext|dom|ldap`) - tested with PHP 8.2.26
@@ -101,8 +118,6 @@ LDAP authentication works with OpenLDAP and Active Directory.
If done correctly, you should now be able to access the application via http://BASE_URL/ - redirects to http://BASE_URL/suite/
-**Please delete the whole `/setup/` folder after installation**
-
After configuring, please rename the `app.json.sample` to `app.json` (`mv app.json.sample app.json`)
## Maintenance Mode
@@ -210,6 +225,7 @@ 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.
+If you are using docker, you can just restart the container after updating the files, as the entrypoint will try to do the migration automatically.
## Managed Hosting
diff --git a/VERSION b/VERSION
index 0dc0f32..905c243 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.2
\ No newline at end of file
+8.3.1
\ No newline at end of file
diff --git a/api/v1/class/arbeitszeit.inc.php b/api/v1/class/arbeitszeit.inc.php
index 8810531..8222d10 100644
--- a/api/v1/class/arbeitszeit.inc.php
+++ b/api/v1/class/arbeitszeit.inc.php
@@ -363,7 +363,7 @@ public function add_worktime($start, $end, $location, $date, $username, $type, $
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], $Wtype), WorktimeAddedEvent::NAME);
+ EventDispatcherService::get()->dispatch(new WorktimeAddedEvent($username, $Wtype, ["start" => $start, "end" => $end]), WorktimeAddedEvent::NAME);
Exceptions::error_rep("Worktime entry for user '{$username}' created successfully.");
return true;
}
diff --git a/api/v1/class/auth/auth.arbeit.inc.php b/api/v1/class/auth/auth.arbeit.inc.php
index e56169d..277436c 100644
--- a/api/v1/class/auth/auth.arbeit.inc.php
+++ b/api/v1/class/auth/auth.arbeit.inc.php
@@ -71,9 +71,14 @@ public static function login($username, $password, $option){ # "option"-> array
$_SESSION["username"] = $username;
$_SESSION["time"] = date("d.m.Y H:i:s", $ts);
self::store_state($username);
-
+ # check if user active
+ if(!$data["active"]) {
+ EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "failed"));
+ Exceptions::error_rep("Login failed for username '$username' - User inactive. Redirecting...");
+ die(header("Location: http://{$base_url}/suite/login.php?" . $sM->URIBuilder("userinactive")));
+ }
if(@isset($option["remember"])){
- if($ini["general"]["app"] == "true"){
+ if($ini["mobile"]["allow_app_use"] == "true"){
EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "success"));
Exceptions::error_rep("Successfully authenticated user '" . $username . "' - LDAP Auth");
@ini_set("session.cookie_samesite", "None");
@@ -103,6 +108,12 @@ public static function login($username, $password, $option){ # "option"-> array
nfclogin:
Exceptions::error_rep("Authenticated user via NFC login '" . $username . "'");
}
+ # check if user active
+ if(!$data["active"]) {
+ EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "failed"));
+ Exceptions::error_rep("Login failed for username '$username' - User inactive. Redirecting...");
+ die(header("Location: http://{$base_url}/suite/login.php?" . $sM->URIBuilder("userinactive")));
+ }
Exceptions::error_rep("Successfully authenticated user '" . $username . "'");
$ini = Arbeitszeit::get_app_ini();
$ts = time();
@@ -113,7 +124,7 @@ public static function login($username, $password, $option){ # "option"-> array
self::store_state($username);
if(@isset($option["remember"])){
- if($ini["general"]["app"] == "true"){
+ if($ini["mobile"]["allow_app_use"] == "true"){
EventDispatcherService::get()->dispatch(new LoggedInUserEvent($username, "success"));
Exceptions::error_rep("Successfully authenticated user '" . $username . "'");
@ini_set("session.cookie_samesite", "None");
@@ -141,29 +152,50 @@ public static function login($username, $password, $option){ # "option"-> array
}
}
- public function login_validation(){
+ public function login_validation()
+ {
Exceptions::error_rep("Validating login...");
$ini = Arbeitszeit::get_app_ini();
$baseurl = $ini["general"]["base_url"];
- if($ini["general"]["app"] == "true"){
+
+ if ($ini["mobile"]["allow_app_use"] == "true") {
@ini_set("session.cookie_samesite", "None");
header('P3P: CP="CAO PSA OUR"');
- @session_set_cookie_params(["path" => "/", "domain" => $ini["general"]["base_url"], "secure" => true, "samesite" => "None"]);
+ @session_set_cookie_params([
+ "path" => "/",
+ "domain" => $ini["general"]["base_url"],
+ "secure" => true,
+ "samesite" => "None"
+ ]);
}
+
@session_start();
- if(isset($_SESSION["logged_in"]) == false){
+
+ if (empty($_SESSION["logged_in"]) || empty($_SESSION["username"])) {
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?" . $this->statusMessages()->URIBuilder("notloggedin"));
+ exit;
}
- if($this->get_state($_SESSION["username"]) != $_COOKIE["state"]){
- EventDispatcherService::get()->dispatch(new ValidatedLoginEvent($_SESSION["username"] ?? "N/A", "failed"));
+
+ if ($this->get_state($_SESSION["username"]) !== ($_COOKIE["state"] ?? null)) {
+ EventDispatcherService::get()->dispatch(new ValidatedLoginEvent($_SESSION["username"], "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?" . $this->statusMessages()->URIBuilder("statemismatch"));
+ exit;
+ }
+
+ if (!$this->benutzer()->user_active($_SESSION["username"])) {
+ EventDispatcherService::get()->dispatch(new ValidatedLoginEvent($_SESSION["username"], "failed"));
+ $this->remove_state($_SESSION["username"]);
+ Exceptions::error_rep("User {$_SESSION["username"]} is inactive. Removing state and redirecting...");
+ header("Location: http://{$baseurl}/suite/login.php?" . $this->statusMessages()->URIBuilder("userinactive"));
+ exit;
}
}
+
/**
* logout() - Logs out user
*
@@ -202,13 +234,13 @@ public static function store_state($user){
$db = new DB;
$ini = self::get_app_ini();
$state = bin2hex(random_bytes(12));
- if($ini["general"]["app"] == "true"){
+ if($ini["mobile"]["allow_app_use"] == "true"){
@ini_set("session.cookie_samesite", "None");
@session_set_cookie_params(["path" => "/", "domain" => $ini["general"]["base_url"], "secure" => true, "samesite" => "None"]);
- setcookie("state", $state, null, "/");
+ setcookie("state", $state, time()+(60*60*24*30), "/");
session_regenerate_id(true);
} else {
- setcookie("state", $state, null, "/");
+ setcookie("state", $state, time()+(60*60*24*30), "/");
$_SESSION["state"] = $state;
}
$sql = "UPDATE `users` SET `state` = ? WHERE `username` = ?;";
diff --git a/api/v1/class/benutzer/benutzer.arbeit.inc.php b/api/v1/class/benutzer/benutzer.arbeit.inc.php
index 3a7a05b..36b62eb 100644
--- a/api/v1/class/benutzer/benutzer.arbeit.inc.php
+++ b/api/v1/class/benutzer/benutzer.arbeit.inc.php
@@ -47,6 +47,23 @@ public function create_user($username, $name, $email, $password, $isAdmin = 0)
}
}
+ public function user_active($username){
+ $user = $this->get_user($username);
+ if($user["active"] == true || $user["active"] == 1){
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function activate_user($username){
+ return $this->editUserProperties($username, "active", 1);
+ }
+
+ public function deactivate_user($username){
+ return $this->editUserProperties($username, "active", 0);
+ }
+
/**
* delete_user() - Deletes a user from the database
*
@@ -326,7 +343,7 @@ public function editUserProperties(mixed $username_or_id, string $name, mixed $v
return false;
}
- $allowed_types = ["username", "email", "isAdmin", "name"];
+ $allowed_types = ["username", "email", "isAdmin", "name", "active"];
if (!in_array($name, $allowed_types)) {
Exceptions::error_rep("Could not update user entry – invalid property '{$name}'");
return false;
diff --git a/api/v1/class/events/worktimes/WorktimeAddedEvent.php b/api/v1/class/events/worktimes/WorktimeAddedEvent.php
index 80f9543..4b615f7 100644
--- a/api/v1/class/events/worktimes/WorktimeAddedEvent.php
+++ b/api/v1/class/events/worktimes/WorktimeAddedEvent.php
@@ -12,7 +12,7 @@ class WorktimeAddedEvent extends Event
private string $Wtype;
- public function __construct(string $username, array $dates = [], int $Wtype){
+ public function __construct(string $username, int $Wtype, array $dates = []){
$this->username = $username;
$this->dates = $dates;
$this->Wtype = $Wtype;
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 950aec6..55e7126 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
@@ -6,6 +6,7 @@
"t2": "Krankheit Beginn",
"t3": "Krankheit Ende",
"t4": "Status",
+ "t5": "Krankheitsart",
"status": {
"set_to": "Setzen zu",
"pending": "In Prüfung",
diff --git a/api/v1/class/i18n/admin/worktime/sick/all/snippets_EN.json b/api/v1/class/i18n/admin/worktime/sick/all/snippets_EN.json
index 374819a..288a6aa 100644
--- a/api/v1/class/i18n/admin/worktime/sick/all/snippets_EN.json
+++ b/api/v1/class/i18n/admin/worktime/sick/all/snippets_EN.json
@@ -6,6 +6,7 @@
"t2": "Sickness Start",
"t3": "Sickness End",
"t4": "Status",
+ "t5": "Sickness Type",
"status": {
"set_to": "Set to",
"pending": "Pending",
diff --git a/api/v1/class/i18n/admin/worktime/sick/all/snippets_NL.json b/api/v1/class/i18n/admin/worktime/sick/all/snippets_NL.json
index 26e7a56..638617d 100644
--- a/api/v1/class/i18n/admin/worktime/sick/all/snippets_NL.json
+++ b/api/v1/class/i18n/admin/worktime/sick/all/snippets_NL.json
@@ -6,6 +6,7 @@
"t2": "Begin van de ziekte",
"t3": "Ziekteeinde",
"t4": "Status",
+ "t5": "Soort ziekte",
"status": {
"set_to": "Instellen op",
"pending": "Wordt beoordeeld",
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 6dc12f8..0a54328 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
@@ -6,6 +6,7 @@
"t2": "Urlaub Beginn",
"t3": "Urlaub Ende",
"t4": "Status",
+ "t5": "Urlaubsart",
"status": {
"set_to": "Setzen zu",
"pending": "In Prüfung",
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 71706ce..e815b3c 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
@@ -6,6 +6,7 @@
"t2": "Vacation Start",
"t3": "Vacation End",
"t4": "Status",
+ "t5": "Vacation Type",
"status": {
"set_to": "Set to",
"pending": "Pending",
diff --git a/api/v1/class/i18n/admin/worktime/vacation/all/snippets_NL.json b/api/v1/class/i18n/admin/worktime/vacation/all/snippets_NL.json
index 414e648..d486437 100644
--- a/api/v1/class/i18n/admin/worktime/vacation/all/snippets_NL.json
+++ b/api/v1/class/i18n/admin/worktime/vacation/all/snippets_NL.json
@@ -6,6 +6,7 @@
"t2": "Vakantiebegin",
"t3": "Einde van de vakantie",
"t4": "Status",
+ "t5": "Soort vakantie",
"status": {
"set_to": "Instellen op",
"pending": "Wordt beoordeeld",
diff --git a/api/v1/class/i18n/suite/sickness/snippets_DE.json b/api/v1/class/i18n/suite/sickness/snippets_DE.json
index 55a417d..365c46b 100644
--- a/api/v1/class/i18n/suite/sickness/snippets_DE.json
+++ b/api/v1/class/i18n/suite/sickness/snippets_DE.json
@@ -4,5 +4,6 @@
"note1": "Bitte gib hier das Start-Datum deines Ausfalls an. Solltest du nur einen Tag ausfallen, dann fülle 'End-Datum' bitte [RED]nicht[/RED] aus.",
"note2": "End-Datum deiner Ausfalls*",
"button_submit": "Krankheit einreichen",
- "note3": "Nach dem Absenden wird dein Arbeitgeber die Krankheit überprüfen und sich bei dir, bei Bedarf, melden."
+ "note3": "Nach dem Absenden wird dein Arbeitgeber die Krankheit überprüfen und sich bei dir, bei Bedarf, melden.",
+ "label_type": "Krankheitsart"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/sickness/snippets_EN.json b/api/v1/class/i18n/suite/sickness/snippets_EN.json
index a09a66d..1dd4eef 100644
--- a/api/v1/class/i18n/suite/sickness/snippets_EN.json
+++ b/api/v1/class/i18n/suite/sickness/snippets_EN.json
@@ -4,5 +4,6 @@
"note1": "Please enter the Start-Date of your sickness. If you do not know or you are just leaving for one day. Please do [RED]not[/RED] fill out the 'End-Date field.",
"note2": "End-Date of your sickness*",
"button_submit": "Submit sickness report",
- "note3": "After submitting your report the responsible person will check and if required reach out to you"
+ "note3": "After submitting your report the responsible person will check and if required reach out to you",
+ "label_type": "Sickness Type"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/sickness/snippets_NL.json b/api/v1/class/i18n/suite/sickness/snippets_NL.json
index 3611ffa..eb5500d 100644
--- a/api/v1/class/i18n/suite/sickness/snippets_NL.json
+++ b/api/v1/class/i18n/suite/sickness/snippets_NL.json
@@ -4,5 +4,6 @@
"note1": "Vul hier de startdatum van uw storing in. Bent u maar één dag afwezig, vul dan [RED]niet[/RED] 'Einddatum' in." ,
"note2": "Einddatum van uw mislukking*",
"button_submit": "Ziekte indienen",
- "note3": "Na het indienen zal uw werkgever de ziekte controleren en indien nodig contact met u opnemen."
+ "note3": "Na het indienen zal uw werkgever de ziekte controleren en indien nodig contact met u opnemen.",
+ "label_type": "Soort ziekte"
}
\ 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 293e834..76439ef 100644
--- a/api/v1/class/i18n/suite/status/snippets_DE.json
+++ b/api/v1/class/i18n/suite/status/snippets_DE.json
@@ -15,8 +15,8 @@
"password_reset": "Hinweis: Es wurde eine E-Mail verschickt, damit du dein Passwort zurücksetzen kannst.",
"nodata": "Fehler: Es wurde kein Benutzername oder Passwort eingegeben.",
"wrongdata": "Fehler: Falsche Anmeldedaten.",
- "worktime_review": "Hinweis: Arbeitszeit erfolgreich auf Prüfung gestellt!",
- "worktime_review_unlock": "Hinweis: Prüfung erfolgreich aufgehoben für Arbeitszeit!",
+ "worktime_reviewed": "Hinweis: Arbeitszeit erfolgreich auf Prüfung gestellt!",
+ "worktime_review_unlocked": "Hinweis: Prüfung erfolgreich aufgehoben für Arbeitszeit!",
"worktime_easymode_start": "Hinweis: Deine Schicht wurde erfolgreich gestartet!",
"worktime_easymode_end": "Hinweis: Deine Schicht wurde erfolgreich beendet!",
"worktime_easymode_pause_start": "Hinweis: Deine Pause wurde erfolgreich gestartet!",
@@ -38,5 +38,16 @@
"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."
+ "mapWorktimeToItem_failed": "Fehler: Die Arbeitszeit konnte nicht mit der Projektaufgabe verbunden werden. Bitte wende dich an deinen Administrator.",
+ "notloggedin": "Fehler: Du bist nicht eingeloggt.",
+ "loggedout": "Hinweis: Du wurdest ausgeloggt.",
+ "worktime_updated": "Hinweis: Arbeitszeit erfolgreich aktualisiert!",
+ "error_worktime_update": "Fehler: Deine Arbeitszeit konnte nicht aktualisiert werden. Bitte kontaktiere deinen Administrator!",
+ "notifications_entry_added": "Hinweis: Benachrichtigungeneintrag hinzugefügt!",
+ "notifications_entry_edited": "Hinweis: Benachrichtigungeneintrag aktualisiert!",
+ "user_added": "Hinweis: Neuer Benutzer erfolgreich hinzugefügt!",
+ "changed_sickness": "Hinweis: Die Krankheit wurde erfolgreich aktualisiert!",
+ "changed_vacation": "Hinweis: Der Urlaub wurde erfolgreich aktualisiert!",
+ "userinactive": "Fehler: Dein Konto wurde deaktiviert. Bitte wende dich an deinen Administrator!",
+ "plugins_disabled": "Fehler: Das Plugin-System ist deaktiviert. 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 d89396f..97df70d 100644
--- a/api/v1/class/i18n/suite/status/snippets_EN.json
+++ b/api/v1/class/i18n/suite/status/snippets_EN.json
@@ -38,5 +38,16 @@
"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."
+ "mapWorktimeToItem_failed": "Error: An error occurred while mapping the worktime to the project itme. Please contact your administrator.",
+ "notloggedin": "Error: You are not logged in.",
+ "loggedout": "Note: You have been logged out.",
+ "worktime_updated": "Note: Work time successfully updated!",
+ "error_worktime_update": "Error: Your work time could not be updated. Please contact your administrator!",
+ "notifications_entry_added": "Note: Notification entry added!",
+ "notifications_entry_edited": "Note: Notification entry updated!",
+ "user_added": "Note: New user successfully added!",
+ "changed_sickness": "Note: Sickness successfully updated!",
+ "changed_vacation": "Note: Vacation successfully updated!",
+ "userinactive": "Error: Your account has been disabled. Please contact your administrator!",
+ "plugins_disabled": "Error: The plugin system is disabled. Please contact your administrator!"
}
diff --git a/api/v1/class/i18n/suite/status/snippets_NL.json b/api/v1/class/i18n/suite/status/snippets_NL.json
index 8dee092..36dcb03 100644
--- a/api/v1/class/i18n/suite/status/snippets_NL.json
+++ b/api/v1/class/i18n/suite/status/snippets_NL.json
@@ -26,5 +26,28 @@
"statemismatch": "Fout: Beveiligingsfout",
"ldapauth": "Fout: LDAP-authenticatie mislukt.",
"ldapcreated": "Opmerking: Log opnieuw in. Je account is nu aangemaakt. (LDAP self-login toegestaan)",
- "notification_not_found": "Fout: Notificatie niet gevonden."
+ "notification_not_found": "Fout: Notificatie niet gevonden.",
+ "project_deleted": "Opmerking: Project succesvol verwijderd.",
+ "project_deleted_failed": "Fout: Het project kon niet worden verwijderd. Neem contact op met je beheerder.",
+ "project_edited": "Opmerking: Wijzigingen aan het project zijn doorgevoerd.",
+ "project_edited_error": "Fout: Er is een fout opgetreden bij het doorvoeren van de wijzigingen aan het project. Neem contact op met je beheerder.",
+ "project_added": "Opmerking: Het project is succesvol toegevoegd.",
+ "project_added_error": "Fout: Er is een fout opgetreden bij het aanmaken van het nieuwe project. Neem contact op met je beheerder.",
+ "project_userAdded": "Opmerking: De gebruiker is succesvol toegevoegd aan het project.",
+ "project_userAdded_failed": "Fout: Er is een fout opgetreden bij het toewijzen van de rechten aan de gebruiker. Neem contact op met je beheerder.",
+ "projects_item_added": "Opmerking: Het item is toegevoegd aan het project.",
+ "projects_item_failed": "Fout: Er is een fout opgetreden bij het aanmaken van het item. Neem contact op met je beheerder.",
+ "mapWorktimeToItem_success": "Opmerking: Werkuren succesvol gekoppeld aan projectitem.",
+ "mapWorktimeToItem_failed": "Fout: Er is een fout opgetreden bij het koppelen van de werkuren aan het projectitem. Neem contact op met je beheerder.",
+ "notloggedin": "Fout: Je bent niet ingelogd.",
+ "loggedout": "Opmerking: Je bent uitgelogd.",
+ "worktime_updated": "Opmerking: Werkuren succesvol bijgewerkt!",
+ "error_worktime_update": "Fout: Je werkuren konden niet worden bijgewerkt. Neem contact op met je beheerder!",
+ "notifications_entry_added": "Opmerking: Notificatie-item toegevoegd!",
+ "notifications_entry_edited": "Opmerking: Notificatie-item bijgewerkt!",
+ "user_added": "Opmerking: Nieuwe gebruiker succesvol toegevoegd!",
+ "changed_sickness": "Opmerking: Ziekte succesvol bijgewerkt!",
+ "changed_vacation": "Opmerking: Vakantie succesvol bijgewerkt!",
+ "userinactive": "Fout: Je account is uitgeschakeld. Neem contact op met je beheerder!",
+ "plugins_disabled": "Fout: Het plugin-systeem is uitgeschakeld. Neem contact op met je beheerder!"
}
diff --git a/api/v1/class/i18n/suite/users/settings/snippets_DE.json b/api/v1/class/i18n/suite/users/settings/snippets_DE.json
index ff8b5b1..f3785ba 100644
--- a/api/v1/class/i18n/suite/users/settings/snippets_DE.json
+++ b/api/v1/class/i18n/suite/users/settings/snippets_DE.json
@@ -13,6 +13,7 @@
"note2_b": "hier",
"note2_c": "um die Aktuellsten Änderungen der Webseite zu sehen.",
"github_note": "Hilfe via GitHub",
- "roadmap_note": "TimeTrack Roadmap"
+ "roadmap_note": "TimeTrack Roadmap",
+ "documentation": "TimeTrack Dokumentation"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/users/settings/snippets_EN.json b/api/v1/class/i18n/suite/users/settings/snippets_EN.json
index 020505c..efe163d 100644
--- a/api/v1/class/i18n/suite/users/settings/snippets_EN.json
+++ b/api/v1/class/i18n/suite/users/settings/snippets_EN.json
@@ -13,6 +13,7 @@
"note2_b": "here",
"note2_c": "to see the latest changes to the website.",
"github_note": "Help via GitHub",
- "roadmap_note": "TimeTrack Roadmap"
+ "roadmap_note": "TimeTrack Roadmap",
+ "documentation": "TimeTrack Documentation"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/users/settings/snippets_NL.json b/api/v1/class/i18n/suite/users/settings/snippets_NL.json
index 4ee3f7f..839971e 100644
--- a/api/v1/class/i18n/suite/users/settings/snippets_NL.json
+++ b/api/v1/class/i18n/suite/users/settings/snippets_NL.json
@@ -13,6 +13,7 @@
"note2_b": "hier",
"note2_c": "om de laatste wijzigingen aan de website te zien.",
"github_note": "Hulp via GitHub",
- "roadmap_note": "TimeTrack Roadmap"
+ "roadmap_note": "TimeTrack Roadmap",
+ "documentation": "TimeTrack Documentatie"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/vacation/snippets_DE.json b/api/v1/class/i18n/suite/vacation/snippets_DE.json
index 438c179..7de198d 100644
--- a/api/v1/class/i18n/suite/vacation/snippets_DE.json
+++ b/api/v1/class/i18n/suite/vacation/snippets_DE.json
@@ -4,5 +4,6 @@
"note1": "Bitte gib hier das Start-Datum deines Urlaubs an. Solltest du nur einen Tag Urlaub nehmen wollen, dann fülle das 'End-Datum' bitte [RED]nicht[/RED] aus.",
"note2": "End-Datum deines Urlaubs*",
"button_submit": "Urlaub einreichen.",
- "note3": "Nach dem Absenden wird dein Arbeitgeber deinen Urlaub prüfen und sich bei dir, bei Bedarf, melden."
+ "note3": "Nach dem Absenden wird dein Arbeitgeber deinen Urlaub prüfen und sich bei dir, bei Bedarf, melden.",
+ "label_type": "Urlaubsart"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/vacation/snippets_EN.json b/api/v1/class/i18n/suite/vacation/snippets_EN.json
index fc87581..cfa4191 100644
--- a/api/v1/class/i18n/suite/vacation/snippets_EN.json
+++ b/api/v1/class/i18n/suite/vacation/snippets_EN.json
@@ -4,5 +4,6 @@
"note1": "Please enter the start date of your vacation. If you plan to just use one day, do [RED]not[/RED] fill out the 'End-Date' field.",
"note2": "End-Date of your vacation*",
"button_submit": "Request Vacation.",
- "note3": "After submitting the responsible person will check and if required contact you."
+ "note3": "After submitting the responsible person will check and if required contact you.",
+ "label_type": "Vacation Type"
}
\ No newline at end of file
diff --git a/api/v1/class/i18n/suite/vacation/snippets_NL.json b/api/v1/class/i18n/suite/vacation/snippets_NL.json
index 0882ef6..179740a 100644
--- a/api/v1/class/i18n/suite/vacation/snippets_NL.json
+++ b/api/v1/class/i18n/suite/vacation/snippets_NL.json
@@ -4,5 +4,6 @@
"note1": "Vul hier de startdatum van uw vakantie in. Als u slechts één vakantiedag wilt opnemen, [RED]vul dan niet[/RED] de 'einddatum' in 'uit'",
"note2": "Einddatum van uw vakantie*",
"button_submit": "Vakantie indienen.",
- "note3": "Na verzending zal uw werkgever uw vakantie controleren en indien nodig contact met u opnemen."
+ "note3": "Na verzending zal uw werkgever uw vakantie controleren en indien nodig contact met u opnemen.",
+ "label_type": "Soort vakantie"
}
\ 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 8a264c2..9b19037 100644
--- a/api/v1/class/plugins/PluginBuilder.plugins.arbeit.inc.php
+++ b/api/v1/class/plugins/PluginBuilder.plugins.arbeit.inc.php
@@ -26,7 +26,7 @@ class PluginBuilder{
*
* @var boolean
*/
- private bool $plugins = true;
+ private static bool $plugins = true;
/**
* Path of the plugin directory specified in the app.ini
@@ -58,6 +58,7 @@ class PluginBuilder{
public function __construct(){
$this->set_basepath();
$this->set_testing();
+ $this->config = Arbeitszeit::get_app_ini()["plugins"];
}
/**
@@ -132,7 +133,7 @@ final public function load_class($class, $name): void{
final public function initialize_plugins(): bool {
if ($this->testing == true) {
$plugins = $this->get_plugins();
- if ($plugins == false) {
+ if ($plugins == false || $plugins == "false") {
$this->logger("{$this->la} Could not get plugins. Please verify the plugin path given in the app.ini");
return false;
}
@@ -375,16 +376,20 @@ final public static function logger($message): void{
}
final public static function check_plugins_enabled(){
- if(!isset(self::$plugins)){
- self::$plugins = Arbeitszeit::get_app_ini()["plugins"]["plugins"];
- }
- if(self::$plugins == true){
+ if(Arbeitszeit::get_app_ini()["plugins"]["plugins"] == "true" || Arbeitszeit::get_app_ini()["plugins"]["plugins"] == true){
return true;
} else {
return false;
}
}
+ final public static function redirect_if_disabled(){
+ if(!self::check_plugins_enabled()){
+ StatusMessages::redirect("plugins_disabled");
+ exit();
+ }
+ }
+
final public function get_plugin_nav($name) {
$this->logger("{$this->la} Getting nav links for plugin '{$name}'");
$conf = $this->read_plugin_configuration($name);
diff --git a/api/v1/class/plugins/plugins/userdetail/views/user.php b/api/v1/class/plugins/plugins/userdetail/views/user.php
index 231d449..296aad1 100644
--- a/api/v1/class/plugins/plugins/userdetail/views/user.php
+++ b/api/v1/class/plugins/plugins/userdetail/views/user.php
@@ -26,6 +26,7 @@
$username = $_POST["username"];
$email = $_POST["email"];
$name = $_POST["name"];
+ @$active = $_POST["active"];
if (!empty($username)) {
if ($benutzer->editUserProperties($id, "username", $username)) {
@@ -50,6 +51,16 @@
}
}
+ if (!empty($_POST["active"])) {
+ if ($benutzer->editUserProperties($id, "active", 1)) {
+ echo "Enabled user account for ID {$id}.
";
+ }
+ } elseif(empty($_POST["active"])){
+ if($benutzer->editUserProperties($id, "active", 0)) {
+ echo "Disabled user account for ID {$id}.
";
+ }
+ }
+
$payload = [
"id" => $id,
"username" => $username,
@@ -83,6 +94,7 @@
" placeholder="box@mail.com">
+ >
" hidden>