Skip to content

Commit

Permalink
Merge pull request #2274 from Leantime/imageFixes
Browse files Browse the repository at this point in the history
Image fixes
  • Loading branch information
marcelfolaron committed Feb 3, 2024
2 parents e9bb4d4 + d7232ba commit 8848663
Show file tree
Hide file tree
Showing 34 changed files with 1,083 additions and 279 deletions.
2 changes: 1 addition & 1 deletion app/Core/Bootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ private function handleRequest(): void
private function setErrorHandler(int $debug): void
{
$incomingRequest = app(IncomingRequest::class);
app()->bind(\Illuminate\Contracts\Debug\ExceptionHandler::class, \Leantime\Core\ExceptionHandler::class);

if (
$debug == 0
Expand All @@ -333,7 +334,6 @@ private function setErrorHandler(int $debug): void

Debug::enable();

app()->bind(\Illuminate\Contracts\Debug\ExceptionHandler::class, \Leantime\Core\ExceptionHandler::class);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions app/Core/Mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ public function __construct(Environment $config, Language $language)
$this->mailAgent->isMail();
}

$this->logo = $_SESSION["companysettings.logoPath"] ?? "/dist/images/logo.png";
$this->companyColor = $_SESSION["companysettings.primarycolor"] ?? "#1b75bb";
$this->logo = empty($_SESSION["companysettings.logoPath"]) ? "/dist/images/logo_blue.png" : $_SESSION["companysettings.logoPath"];
$this->companyColor = empty($_SESSION["companysettings.primarycolor"]) ? "#006c9e" : $_SESSION["companysettings.primarycolor"];

$this->language = $language;
}
Expand Down
17 changes: 10 additions & 7 deletions app/Core/Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,10 @@ public function setupBlade(
//@TODO: Check on callstack to make sure autoload loads before sessions
if (!is_a($plugin, '__PHP_Incomplete_Class')) {
if ($domainPaths->has($basename = strtolower($plugin->foldername))) {
throw new Exception("Plugin $basename conflicts with domain");
error_log("Plugin $basename conflicts with domain");
//Clear cache, something is up
unset($_SESSION['enabledPlugins']);
return [];
}

if ($plugin->format == "phar") {
Expand Down Expand Up @@ -944,14 +947,14 @@ public function convertRelativePaths(?string $text): ?string

// Replace links
$text = preg_replace(
'/<a([^>]*) href="([^http|ftp|https|mailto|#][^"]*)"/',
'/<a([^>]*) href="((?!(http|ftp|https|mailto|#))[^"]*)"/',
"<a\${1} href=\"$base\${2}\"",
$text
);

// Replace images
$text = preg_replace(
'/<img([^>]*) src="([^http|ftp|https][^"]*)"/',
'/<img([^>]*) src="((?!(http|ftp|https))[^"]*)"/',
"<img\${1} src=\"$base\${2}\"",
$text
);
Expand Down Expand Up @@ -980,22 +983,22 @@ public function getModulePicture(): string
}

/**
* patchDownloadUrlToFilenameOrAwsUrl - Replace all local download.php references in <img src=""> tags
* patchDownloadUrlToFilenameOrAwsUrl - Replace all local files/get references in <img src=""> tags
* by either local filenames or AWS URLs that can be accesse without being authenticated
*
* Note: This patch is required by the PDF generating engine as it retrieves URL data without being
* authenticated
*
* @access public
* @param string $textHtml HTML text, potentially containing <img srv="https://local.domain/download.php?xxxx"> tags
* @return string HTML text with the https://local.domain/download.php?xxxx replaced by either full qualified
* @param string $textHtml HTML text, potentially containing <img srv="https://local.domain/files/get?xxxx"> tags
* @return string HTML text with the https://local.domain/files/get?xxxx replaced by either full qualified
* local filenames or AWS URLs
*/
public function patchDownloadUrlToFilenameOrAwsUrl(string $textHtml): string
{
$patchedTextHtml = $this->convertRelativePaths($textHtml);

// TO DO: Replace local download.php
// TO DO: Replace local files/get
$patchedTextHtml = $patchedTextHtml;

return $patchedTextHtml;
Expand Down
6 changes: 3 additions & 3 deletions app/Domain/Clients/Templates/showClient.tpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@
</a>
<ul class="dropdown-menu">
<li class="nav-header"><?php echo $tpl->__("subtitles.file"); ?></li>
<li><a href="<?=BASE_URL ?>/download.php?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php echo $file['extension'] ?>&realName=<?php echo $file['realName'] ?>"><?php echo $tpl->__("links.download"); ?></a></li>
<li><a href="<?=BASE_URL ?>/files/get?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php echo $file['extension'] ?>&realName=<?php echo $file['realName'] ?>"><?php echo $tpl->__("links.download"); ?></a></li>

<?php
if ($login::userIsAtLeast($roles::$admin)) { ?>
Expand All @@ -234,9 +234,9 @@

</ul>
</div>
<a class="cboxElement" href="<?=BASE_URL ?>/download.php?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php $tpl->e($file['extension']); ?>&realName=<?php $tpl->e($file['realName']); ?>">
<a class="cboxElement" href="<?=BASE_URL ?>/files/get?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php $tpl->e($file['extension']); ?>&realName=<?php $tpl->e($file['realName']); ?>">
<?php if (in_array(strtolower($file['extension']), $tpl->get('imgExtensions'))) : ?>
<img style='max-height: 50px; max-width: 70px;' src="<?=BASE_URL ?>/download.php?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php $tpl->e($file['extension']); ?>&realName=<?php $tpl->e($file['realName']); ?>" alt="" />
<img style='max-height: 50px; max-width: 70px;' src="<?=BASE_URL ?>/files/get?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php $tpl->e($file['extension']); ?>&realName=<?php $tpl->e($file['realName']); ?>" alt="" />
<?php else : ?>
<img style='max-height: 50px; max-width: 70px;' src='<?=BASE_URL ?>/dist/images/thumbs/doc.png' />
<?php endif; ?>
Expand Down
15 changes: 11 additions & 4 deletions app/Domain/Cron/Controllers/Run.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ public function init(Environment $config)
public function run(): Response
{
if (! $this->config->poorMansCron) {
error_log("Poor mans cron off");
return new Response();
}

error_log("Adding Event Listener to request_terminated");
Events::add_event_listener('leantime.core.httpkernel.terminate.request_terminated', function () {
ignore_user_abort(true);

Expand All @@ -45,17 +47,22 @@ public function run(): Response

$output = new \Symfony\Component\Console\Output\BufferedOutput();

if ($this->config->debug) {

register_shutdown_function(function () use ($output) {
error_log("Command Output: " . $output->fetch());
error_log("Cron run finished");
if ($this->config->debug) {
error_log("Command Output: " . $output->fetch());
error_log("Cron run finished");
}
});

error_log("Cron run started");
}


error_log("Before calling schedule:run");
/** @return never **/
(new \Leantime\Core\ConsoleKernel())->call('schedule:run', [], $output);
error_log("After calling schedule:run");

});

return tap(new Response(), function ($response) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
<div class="btn-group pull-left" style="margin-right:5px;">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown"><?=$tpl->__("links.new_with_icon") ?> <span class="caret"></span></button>
<ul class="dropdown-menu">

<li><a href="#/tickets/newTicket">Add Todo</a></li>
<li><a href="#/tickets/editMilestone">Add Milestone</a></li>
<li><a href="#/sprints/editSprint">Add List</a></li>

</ul>
</div>
Expand Down
182 changes: 182 additions & 0 deletions app/Domain/Files/Controllers/Get.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<?php

namespace Leantime\Domain\Files\Controllers;

use Aws\S3\S3Client;
use Leantime\Core\Controller;
use Leantime\Core\Environment;
use Leantime\Core\Frontcontroller;
use Leantime\Domain\Files\Repositories\Files as FileRepository;
use Leantime\Domain\Files\Services\Files as FileService;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
*
*/
class Get extends Controller
{
private FileService $filesService;
private FileRepository $filesRepo;

private Environment $config;

/**
* @param FileRepository $filesRepo
* @param FileService $filesService
* @return void
*/
public function init(
FileRepository $filesRepo,
FileService $filesService,
Environment $config
): void {
$this->filesRepo = $filesRepo;
$this->filesService = $filesService;
$this->config = $config;
}

/**
* @return Response
* @throws \Exception
*/
public function get(): Response
{

$encName = preg_replace("/[^a-zA-Z0-9]+/", "", $_GET['encName']);
$realName = $_GET['realName'];
$ext = preg_replace("/[^a-zA-Z0-9]+/", "", $_GET['ext']);
$module = preg_replace("/[^a-zA-Z0-9]+/", "", $_GET['module'] ?? '');

if ($this->config->useS3) {
return $this->getFileFromS3($encName, $ext, $module, $realName);
} else {
return $this->getFileLocally($encName, $ext, $module, $realName);
}

}

/**
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
private function getFileLocally($encName, $ext, $module, $realName): Response
{

$mimes = array(
'jpg' => 'image/jpg',
'jpeg' => 'image/jpg',
'gif' => 'image/gif',
'png' => 'image/png',
);

//TODO: Replace with ROOT
$path = realpath(APP_ROOT . "/" . $this->config->userFilePath . "/");

$fullPath = $path . "/" . $encName . '.' . $ext;

if (file_exists(realpath($fullPath))) {
if ($fd = fopen(realpath($fullPath), 'rb')) {
$path_parts = pathinfo($fullPath);

if ($ext == 'pdf') {
header('Content-type: application/pdf');
header("Content-Disposition: inline; filename=\"" . $realName . "." . $ext . "\"");
} elseif ($ext == 'jpg' || $ext == 'jpeg' || $ext == 'gif' || $ext == 'png') {
header('Content-type: ' . $mimes[$ext]);
header('Content-disposition: inline; filename="' . $realName . "." . $ext . '";');
} elseif ($ext == 'svg') {
header('Content-type: image/svg+xml');
header('Content-disposition: attachment; filename="' . $realName . "." . $ext . '";');
} else {
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"" . $realName . "." . $ext . "\"");
}


$sLastModified = filemtime($fullPath);
$sEtag = md5_file($fullPath);

$sFileSize = filesize($fullPath);


$oStreamResponse = new StreamedResponse();
$oStreamResponse->headers->set("Content-Type", $mimes[$ext] );
$oStreamResponse->headers->set("Content-Length", $sFileSize);
$oStreamResponse->headers->set("ETag", $sEtag);

if(app()->make(Environment::class)->debug == false) {
$oStreamResponse->headers->set("Pragma", 'public');
$oStreamResponse->headers->set("Cache-Control", 'max-age=86400');
$oStreamResponse->headers->set("Last-Modified", gmdate("D, d M Y H:i:s", $sLastModified)." GMT");
}
$oStreamResponse->setCallback(function() use ($fullPath) {readfile($fullPath);});

return $oStreamResponse;

}
} else {
http_response_code(404);
die();
}
}

/**
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
private function getFileFromS3($encName, $ext, $module, $realName): Response
{

$mimes = array(
'jpg' => 'image/jpg',
'jpeg' => 'image/jpg',
'gif' => 'image/gif',
'png' => 'image/png',
);

// Instantiate the client.

$s3Client = new S3Client([
'version' => 'latest',
'region' => $this->config->s3Region,
'endpoint' => $this->config->s3EndPoint == "" ? null : $this->config->s3EndPoint,
'use_path_style_endpoint' => !($this->config->s3UsePathStyleEndpoint == "false"),
'credentials' => [
'key' => $this->config->s3Key,
'secret' => $this->config->s3Secret,
],
]);

try {
// implode all non-empty elements to allow s3FolderName to be empty.
// otherwise you will get an error as the key starts with a slash
$fileName = implode('/', array_filter(array($this->config->s3FolderName, $encName . "." . $ext)));
$result = $s3Client->getObject([
'Bucket' => $this->config->s3Bucket,
'Key' => $fileName,
'Body' => 'this is the body!',
]);

$response = new Response($result->get('Body')->getContents());

if ($ext == 'pdf') {
$response->headers->set('Content-type', 'application/pdf');
} elseif ($ext == 'jpg' || $ext == 'jpeg' || $ext == 'gif' || $ext == 'png') {
$response->headers->set('Content-type', $result['ContentType']);
} elseif ($ext == 'svg') {
$response->headers->set('Content-type', 'image/svg+xml');

} else {
header('Content-disposition: attachment; filename="' . $realName . "." . $ext . '";');
}

$response->headers->set('Content-Disposition', "inline; filename=\"" . $realName . "." . $ext . "\"");

return $response;
} catch (Aws\S3\Exception\S3Exception $e) {
return new Response($e->getMessage());
}
}
}
1 change: 1 addition & 0 deletions app/Domain/Files/Services/Files.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public function deleteFile($fileId): bool
{
return $this->fileRepository->deleteFile($fileId);
}

}

}
12 changes: 6 additions & 6 deletions app/Domain/Files/Templates/browse.tpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
</a>
<ul class="dropdown-menu">
<li class="nav-header"><?php echo $tpl->__("subtitles.file"); ?></li>
<li><a target="_blank" href="<?=BASE_URL ?>/download.php?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php echo $file['extension'] ?>&realName=<?php echo $file['realName'] ?>"><?php echo $tpl->__("links.download"); ?></a></li>
<li><a target="_blank" href="<?=BASE_URL ?>/files/get?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php echo $file['extension'] ?>&realName=<?php echo $file['realName'] ?>"><?php echo $tpl->__("links.download"); ?></a></li>

<?php

Expand All @@ -85,9 +85,9 @@

</ul>
</div>
<a class="imageLink" data-ext="<?php echo $file['extension'] ?>" href="<?=BASE_URL?>/download.php?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php echo $file['extension'] ?>&realName=<?php echo $file['realName'] ?>">
<a class="imageLink" data-ext="<?php echo $file['extension'] ?>" href="<?=BASE_URL?>/files/get?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php echo $file['extension'] ?>&realName=<?php echo $file['realName'] ?>">
<?php if (in_array(strtolower($file['extension']), $tpl->get('imgExtensions'))) : ?>
<img style='max-height: 50px; max-width: 70px;' src="<?=BASE_URL ?>/download.php?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php echo $file['extension'] ?>&realName=<?php echo $file['realName'] ?>" alt="" />
<img style='max-height: 50px; max-width: 70px;' src="<?=BASE_URL ?>/files/get?module=<?php echo $file['module'] ?>&encName=<?php echo $file['encName'] ?>&ext=<?php echo $file['extension'] ?>&realName=<?php echo $file['realName'] ?>" alt="" />
<?php else : ?>
<img style='max-height: 50px; max-width: 70px;' src='<?=BASE_URL ?>/dist/images/doc.png' />
<?php endif; ?>
Expand Down Expand Up @@ -267,15 +267,15 @@
'</a>' +
'<ul class="dropdown-menu">' +
'<li class="nav-header"><?php echo $tpl->__("subtitles.file"); ?></li>' +
'<li><a target="_blank" href="<?=BASE_URL ?>/download.php?module='+ response.module +'&encName='+ response.encName +'&ext='+ response.extension +'&realName='+ response.realName +'"><?php echo str_replace("'", '"', $tpl->__("links.download")); ?></a></li>'+
'<li><a target="_blank" href="<?=BASE_URL ?>/files/get?module='+ response.module +'&encName='+ response.encName +'&ext='+ response.extension +'&realName='+ response.realName +'"><?php echo str_replace("'", '"', $tpl->__("links.download")); ?></a></li>'+
<?php
if ($login::userIsAtLeast($roles::$editor)) { ?>
'<li><a href="<?=BASE_URL ?>/files/showAll?delFile='+ response.fileId +'" class="delete deleteFile"><i class="fa fa-trash"></i> <?php echo str_replace("'", '"', $tpl->__("links.delete")); ?></a></li>'+
<?php } ?>
'</ul>'+
'</div>'+
'<a class="imageLink" href="<?=BASE_URL?>/download.php?module='+ response.module +'&encName='+ response.encName +'&ext='+ response.extension +'&realName='+ response.realName +'">'+
'<img style="max-height: 50px; max-width: 70px;" src="<?=BASE_URL ?>/download.php?module='+ response.module +'&encName='+ response.encName +'&ext='+ response.extension +'&realName='+ response.realName +'" alt="" />'+
'<a class="imageLink" href="<?=BASE_URL?>/files/get?module='+ response.module +'&encName='+ response.encName +'&ext='+ response.extension +'&realName='+ response.realName +'">'+
'<img style="max-height: 50px; max-width: 70px;" src="<?=BASE_URL ?>/files/get?module='+ response.module +'&encName='+ response.encName +'&ext='+ response.extension +'&realName='+ response.realName +'" alt="" />'+

'<span class="filename">'+response.realName+'.</span>'+
'</a>'+
Expand Down

0 comments on commit 8848663

Please sign in to comment.