Skip to content

Commit

Permalink
Merge branch 'master' into laravel11Update
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelfolaron committed Mar 7, 2025
2 parents c9eac66 + e78c6f0 commit 7fed990
Show file tree
Hide file tree
Showing 16 changed files with 267 additions and 128 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# Version: 3.4.3

* [af62ea4](https://github.com/Leantime/leantime/commit/af62ea4e6e467240a180a0fa073c127ef6c212b4): task: clean up ticket service
* [deffc4e](https://github.com/Leantime/leantime/commit/deffc4e855682ba28eeaa846d92d7d84708a6c2e): fix: not sending notifications when project id is not set correctly
* [e5384f2](https://github.com/Leantime/leantime/commit/e5384f22a9dd0b3ccc3a05f233d754b25fcd7a2e): fix: console kernel to point to the correct binary
* [12ad2e5](https://github.com/Leantime/leantime/commit/12ad2e501945ecc27a96cab5d1df31dd511fcc9b): fix api endpoint to get open user tickets
* [7b3539d](https://github.com/Leantime/leantime/commit/7b3539d94071df590cf20e5724a81d2f2aa06ee3): task: update version
* [2759494](https://github.com/Leantime/leantime/commit/2759494d2cd55a4c727abb8718ee866ceffb8637): task: Improve dailyIngestion report management
* [8a3ee6a](https://github.com/Leantime/leantime/commit/8a3ee6a1cfd918f49c5ed396584043f5a4c46ce0): fix: add missing field
* [1d0b242](https://github.com/Leantime/leantime/commit/1d0b242917b9dcd0ee92cc0bdc2ccc2fef5fcaa4): fix: broken datetime management on sprints creating various bugs throughout the system
* [86b40bd](https://github.com/Leantime/leantime/commit/86b40bdbbf87c814204c24dd974b77d1767e2ef1): task: Cleanup and improve datetime helper class
* [3dd728b](https://github.com/Leantime/leantime/commit/3dd728b5770dad0bdf5e97e3fec92efd671d5bed): Fix link


# Version: 3.4.2

* [14e71ad](https://github.com/Leantime/leantime/commit/14e71adada985e1c1f759cebde46d68da4301eb7): fix: typo in provider name
Expand Down
58 changes: 45 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,52 @@ Built with ADHD, dyslexia and autism in mind. 🧠<br />
* PHP 8.2+
* MySQL 8.0+ or MariaDB 10.6+
* Apache or Nginx (IIS works with some modifications)
* PHP Extensions:
* mysql
* pdo_mysql
* mbstring
* PHP Extensions:
* BC Math (bcmath)
* Ctype
* cURL
* DOM
* Exif
* Fileinfo
* Filter
* GD
* exif
* pcntl
* bcmath
* opcache
* ldap
* zip
* openssl
* phar
* Hash
* LDAP
* Multibyte String (mbstring)
* MySQL
* OPcache
* OpenSSL
* PCNTL
* PCRE
* PDO
* Phar
* Session
* Tokenizer
* Zip
* SimpleXML
<br /><br />


Ctype PHP Extension
cURL PHP Extension
DOM PHP Extension
Fileinfo PHP Extension
Filter PHP Extension
Hash PHP Extension
Mbstring PHP Extension
OpenSSL PHP Extension
PCRE PHP Extension
PDO PHP Extension
Session PHP Extension
Tokenizer PHP Extension
XML PHP Extension







### ️⚡️ Installation (Production) ###

There are two main ways to install LeanTime for production. The first of which is to install all needed pieces of the system locally. The second is to use the officially supported Docker image.
Expand Down Expand Up @@ -120,7 +152,7 @@ We maintain an official <a href="https://hub.docker.com/r/leantime/leantime">Doc
To run the image enter your MySQL credentials and execute. You can pass in all the configuration variables from .env

```
docker run -d --restart unless-stopped -p 80:80 --network leantime-net \
docker run -d --restart unless-stopped -p 8080:8080 --network leantime-net \
-e LEAN_DB_HOST=mysql_leantime \
-e LEAN_DB_USER=admin \
-e LEAN_DB_PASSWORD=321.qwerty \
Expand Down
2 changes: 1 addition & 1 deletion app/Core/Configuration/AppSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
class AppSettings
{
public string $appVersion = '3.4.2';
public string $appVersion = '3.4.3';

public string $dbVersion = '3.4.0';
}
11 changes: 11 additions & 0 deletions app/Core/Console/ConsoleKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Illuminate\Console\Command;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Console\Kernel;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
Expand Down Expand Up @@ -33,6 +35,15 @@ class ConsoleKernel extends Kernel implements ConsoleKernelContract
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];

public function __construct(Application $app, Dispatcher $events)
{
if (! defined('ARTISAN_BINARY')) {
define('ARTISAN_BINARY', 'bin/leantime');
}

parent::__construct($app, $events);
}

/**
* Handle an incoming console command.
*
Expand Down
8 changes: 7 additions & 1 deletion app/Core/Support/DateTimeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,14 @@ public function parseUserDateTime(string $userDate, string $userTime = ''): Carb
$standardFormats = [
DateTime::ATOM,
DateTime::ISO8601,
DateTime::ISO8601_EXPANDED,
DateTime::W3C,
];

// Added in PHP 8.2
if (defined('DateTime::ISO8601_EXPANDED')) {
$standardFormats[] = DateTime::ISO8601_EXPANDED;
}

// Try standard formats first
foreach ($standardFormats as $format) {
try {
Expand Down Expand Up @@ -130,6 +134,8 @@ public function parseUserDateTime(string $userDate, string $userTime = ''): Carb
*
* @param string $dbDate The date string in the database format to parse.
* @return CarbonImmutable The parsed CarbonImmutable instance in db timezone (UTC)
*
* @throws InvalidDateException
*/
public function parseDbDateTime(string $dbDate): CarbonImmutable
{
Expand Down
8 changes: 7 additions & 1 deletion app/Domain/Ideas/Controllers/IdeaDialog.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,15 @@ public function post($params)
}
}

$id = $_GET['id'] ?? null;

// changeItem is set for new or edited item changes.
if (isset($params['changeItem'])) {
if (isset($params['itemId']) && $params['itemId'] != '') {
if (isset($params['description']) === true) {
$currentCanvasId = (int) session('currentIdeaCanvas');

$id = $params['itemId'];
$canvasItem = [
'box' => $params['box'],
'author' => session('userdata.id'),
Expand Down Expand Up @@ -255,10 +258,13 @@ public function post($params)
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
}
}

}

$canvasItem = $this->ideaRepo->getSingleCanvasItem($id);

$this->tpl->assign('canvasTypes', $this->ideaRepo->canvasTypes);
$this->tpl->assign('canvasItem', $this->ideaRepo->getSingleCanvasItem($_GET['id']));
$this->tpl->assign('canvasItem', $canvasItem);

return $this->tpl->displayPartial('ideas.ideaDialog');
}
Expand Down
74 changes: 56 additions & 18 deletions app/Domain/Reports/Services/Reports.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use GuzzleHttp\Client;
use GuzzleHttp\Promise\PromiseInterface;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Leantime\Core\Configuration\AppSettings as AppSettingCore;
use Leantime\Core\Configuration\Environment as EnvironmentCore;
use Leantime\Core\Events\DispatchesEvents;
Expand Down Expand Up @@ -90,47 +92,55 @@ public function __construct(
*/
public function dailyIngestion(): void
{
$this->runIngestionForProject(session('currentProject'));
}

protected function runIngestionForProject(int $projectId): void
{

if (Cache::has('dailyReports-'.$projectId) === false || Cache::get('dailyReports-'.$projectId) < dtHelper()->dbNow()->endOfDay()) {

if (
session()->exists('currentProject')
&&
(
! session()->exists('reportCompleted.'.session('currentProject'))
|| session('reportCompleted.'.session('currentProject')) != 1
)
) {
// Check if the dailyingestion cycle was executed already. There should be one entry for backlog and one entry for current sprint (unless there is no current sprint
// Get current Sprint Id, if no sprint available, dont run the sprint burndown

$lastEntries = $this->reportRepository->checkLastReportEntries(session('currentProject'));
$lastEntries = $this->reportRepository->checkLastReportEntries($projectId);

// If we receive 2 entries we have a report already. If we have one entry then we ran the backlog one and that means there was no current sprint.

if (count($lastEntries) == 0) {
$currentSprint = $this->sprintRepository->getCurrentSprint(session('currentProject'));
$currentSprint = $this->sprintRepository->getCurrentSprint($projectId);

if ($currentSprint !== false) {
$sprintReport = $this->reportRepository->runTicketReport(session('currentProject'), $currentSprint->id);
$sprintReport = $this->reportRepository->runTicketReport($projectId, $currentSprint->id);
if ($sprintReport !== false) {
$this->reportRepository->addReport($sprintReport);
}
}

$backlogReport = $this->reportRepository->runTicketReport(session('currentProject'), '');
$backlogReport = $this->reportRepository->runTicketReport($projectId, '');

if ($backlogReport !== false) {

$this->reportRepository->addReport($backlogReport);

if (! session()->exists('reportCompleted') || is_array(session('reportCompleted')) === false) {
session(['reportCompleted' => []]);
}
Cache::put('dailyReports-'.$projectId, dtHelper()->dbNow()->endOfDay(), 14400); // 4hours

session(['reportCompleted.'.session('currentProject') => 1]);
}
}

}
}

public function cronDailyIngestion(): void
{
$projects = $this->projectRepository->getAll();

foreach ($projects as $project) {
$this->runIngestionForProject($project['id']);
}

}

/**
* @api
*/
Expand Down Expand Up @@ -261,7 +271,7 @@ public function getAnonymousTelemetry(

'serverSoftware' => $_SERVER['SERVER_SOFTWARE'] ?? 'unknown',
'phpUname' => php_uname(),
'isDocker' => is_file('/.dockerenv'),
'isDocker' => $this->isRunningInDocker(),
'phpSapiName' => php_sapi_name(),
'phpOs' => PHP_OS ?? 'unknown',

Expand Down Expand Up @@ -312,7 +322,7 @@ public function sendAnonymousTelemetry(): bool|PromiseInterface
return $promise;

} catch (\Exception $e) {
report($e);
Log::error($e);

return false;
}
Expand Down Expand Up @@ -473,6 +483,34 @@ public function generateTicketReactionsReport()

return $reactions;
}

/**
* Checks if Leantime is running in a Docker environment
* Uses multiple detection methods and handles errors gracefully
*/
private function isRunningInDocker(): bool
{
// Method 1: Check for /.dockerenv file
try {
if (is_file('/.dockerenv')) {
return true;
}
} catch (\Exception $e) {
// Silently fail if file access is restricted
}

// Method 2: Check for Docker-specific environment variables
if (getenv('DOCKER_CONTAINER') !== false || getenv('IS_DOCKER') !== false) {
return true;
}

// Method 3: Check cgroup info (works on Linux hosts)
try {
return strpos(file_get_contents('/proc/1/cgroup'), 'docker') !== false;
} catch (\Exception $e) {
return false; // Return false if all detection methods fail
}
}
}

}
2 changes: 1 addition & 1 deletion app/Domain/Reports/register.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@

})->name('reports:telemetry')->everyMinute();

$scheduler->call(fn () => $reportService->dailyIngestion())->name('reports:dailyIngestion')->everyMinute();
$scheduler->call(fn () => $reportService->cronDailyIngestion())->name('reports:dailyIngestion')->everyMinute();
});
9 changes: 5 additions & 4 deletions app/Domain/Tickets/Controllers/ShowTicket.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,14 @@ public function post($params): Response

// Log time
if (isset($params['saveTimes']) === true) {
$result = $this->timesheetService->logTime($id, $params);

if ($result === true) {
try {
$result = $this->timesheetService->logTime($id, $params);
$this->tpl->setNotification($this->language->__('notifications.time_logged_success'), 'success');
} else {
$this->tpl->setNotification($this->language->__($result['msg']), 'error');
} catch (\Exception $e) {
$this->tpl->setNotification($e->getMessage(), 'error');
}

}

// Save Ticket
Expand Down
2 changes: 2 additions & 0 deletions app/Domain/Tickets/Models/Tickets.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class Tickets

public mixed $bookedHours = null;

public ?array $children = null;

/**
* @param false $values
*/
Expand Down
5 changes: 1 addition & 4 deletions app/Domain/Tickets/Repositories/Tickets.php
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ public function simpleTicketQuery(?int $userId, ?int $projectId): array|false
LEFT JOIN zp_projects ON zp_tickets.projectId = zp_projects.id
LEFT JOIN zp_user AS requestor ON requestor.id = :requestorId
WHERE (
zp_tickets.projectId IN (SELECT projectId FROM zp_relationuserproject WHERE zp_relationuserproject.userId = :userId)
zp_tickets.projectId IN (SELECT projectId FROM zp_relationuserproject WHERE zp_relationuserproject.userId = :requestorId)
OR zp_projects.psettings = 'all'
OR (requestor.role >= 40)
)
Expand All @@ -619,11 +619,8 @@ public function simpleTicketQuery(?int $userId, ?int $projectId): array|false
$stmn->bindValue(':projectId', $projectId, PDO::PARAM_INT);
}

// NOTE: This should not be removed as it is used for authorization
if (isset($userId) && $userId != '') {
$stmn->bindValue(':userId', $userId, PDO::PARAM_INT);
} else {
$stmn->bindValue(':userId', session('userdata.id') ?? '-1', PDO::PARAM_INT);
}

// Current client is only used for authorization as it represents the current client Id assigned to a user.
Expand Down
Loading

0 comments on commit 7fed990

Please sign in to comment.