diff --git a/composer.lock b/composer.lock index 8fe8a3a2..c7122d97 100644 --- a/composer.lock +++ b/composer.lock @@ -1869,24 +1869,24 @@ }, { "name": "erusev/parsedown", - "version": "1.7.4", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/erusev/parsedown.git", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" + "reference": "96baaad00f71ba04d76e45b4620f54d3beabd6f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/96baaad00f71ba04d76e45b4620f54d3beabd6f7", + "reference": "96baaad00f71ba04d76e45b4620f54d3beabd6f7", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=5.3.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35" + "phpunit/phpunit": "^7.5|^8.5|^9.6" }, "type": "library", "autoload": { @@ -1913,9 +1913,15 @@ ], "support": { "issues": "https://github.com/erusev/parsedown/issues", - "source": "https://github.com/erusev/parsedown/tree/1.7.x" + "source": "https://github.com/erusev/parsedown/tree/1.8.0" }, - "time": "2019-12-30T22:54:17+00:00" + "funding": [ + { + "url": "https://github.com/erusev", + "type": "github" + } + ], + "time": "2026-02-16T11:41:01+00:00" }, { "name": "friendsofsymfony/rest-bundle", @@ -11437,16 +11443,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.94.0", + "version": "v3.94.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "883b20fb38c7866de9844ab6d0a205c423bde2d4" + "reference": "d1a3634e29916367b885250e1fc4dfd5ffe3b091" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/883b20fb38c7866de9844ab6d0a205c423bde2d4", - "reference": "883b20fb38c7866de9844ab6d0a205c423bde2d4", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/d1a3634e29916367b885250e1fc4dfd5ffe3b091", + "reference": "d1a3634e29916367b885250e1fc4dfd5ffe3b091", "shasum": "" }, "require": { @@ -11529,7 +11535,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.94.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.94.1" }, "funding": [ { @@ -11537,7 +11543,7 @@ "type": "github" } ], - "time": "2026-02-11T16:44:33+00:00" + "time": "2026-02-18T12:24:42+00:00" }, { "name": "masterminds/html5", @@ -11829,16 +11835,16 @@ }, { "name": "phpstan/phpstan-doctrine", - "version": "2.0.16", + "version": "2.0.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "f4ff6084a26d91174b3f0b047589af293a893104" + "reference": "734ef36c2709b51943f04aacadddb76f239562d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/f4ff6084a26d91174b3f0b047589af293a893104", - "reference": "f4ff6084a26d91174b3f0b047589af293a893104", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/734ef36c2709b51943f04aacadddb76f239562d3", + "reference": "734ef36c2709b51943f04aacadddb76f239562d3", "shasum": "" }, "require": { @@ -11899,9 +11905,9 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.16" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.17" }, - "time": "2026-02-11T08:54:45+00:00" + "time": "2026-02-18T10:21:23+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/phpstan.neon b/phpstan.neon index 78bcdf60..0e8903d2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3800,11 +3800,6 @@ parameters: count: 1 path: src/Utils/ParsedownExtension.php - - - message: "#^Property App\\\\Utils\\\\ParsedownExtension\\:\\:\\$safeLinksWhitelist has no type specified\\.$#" - count: 1 - path: src/Utils/ParsedownExtension.php - - message: "#^Parameter \\#1 \\$profile of method App\\\\Utils\\\\ProfileManager\\:\\:getProfile\\(\\) expects string, mixed given\\.$#" count: 1 diff --git a/src/Constants.php b/src/Constants.php index 6001eef4..ed8ad68f 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -17,11 +17,11 @@ final class Constants /** * The current release version */ - public const VERSION = '2.49.0'; + public const VERSION = '2.50.0'; /** * The current release: major * 10000 + minor * 100 + patch */ - public const VERSION_ID = 24900; + public const VERSION_ID = 25000; /** * The software name */ diff --git a/src/Form/Type/QuickEntryTimesheetType.php b/src/Form/Type/QuickEntryTimesheetType.php index 1b079ecd..f39f35a2 100644 --- a/src/Form/Type/QuickEntryTimesheetType.php +++ b/src/Form/Type/QuickEntryTimesheetType.php @@ -95,6 +95,7 @@ final class QuickEntryTimesheetType extends AbstractType $duration = $data->getDuration(false); try { if (null !== $duration) { + $duration += $data->getBreak(); $end = clone $data->getBegin(); $end->modify('+ ' . abs($duration) . ' seconds'); $data->setEnd($end); diff --git a/src/Utils/Parsedown.php b/src/Utils/Parsedown.php index d261bced..bd32e1b2 100644 --- a/src/Utils/Parsedown.php +++ b/src/Utils/Parsedown.php @@ -21,15 +21,17 @@ class Parsedown extends \Parsedown { $block = parent::blockHeader($Line); - $text = $block['element']['text']; + if (isset($block['element']['handler']['argument'])) { + $text = $block['element']['handler']['argument']; - if (\is_string($text) && $text !== '') { - $id = $this->getIDfromText($text); + if (\is_string($text) && $text !== '') { + $id = $this->getIDfromText($text); - // add id-attribute - $block['element']['attributes'] = [ - 'id' => $id - ]; + // add id-attribute + $block['element']['attributes'] = [ + 'id' => $id + ]; + } } return $block; diff --git a/src/Utils/ParsedownExtension.php b/src/Utils/ParsedownExtension.php index 833b1389..17f7dbec 100644 --- a/src/Utils/ParsedownExtension.php +++ b/src/Utils/ParsedownExtension.php @@ -44,55 +44,17 @@ final class ParsedownExtension extends Parsedown ]; /** - * Overwritten to add support for file:/// - */ - protected $safeLinksWhitelist = [ - 'file:///', - 'http://', - 'https://', - 'ftp://', - 'ftps://', - 'mailto:', - 'data:image/png;base64,', - 'data:image/gif;base64,', - 'data:image/jpeg;base64,', - 'irc:', - 'ircs:', - 'git:', - 'ssh:', - 'news:', - 'steam:', - ]; - - /** - * Overwritten: - * - added support for file:/// - * - open links in new windows + * Overwritten to open links in new windows */ protected function inlineUrl($Excerpt): ?array { - if ($this->urlsLinked !== true or !isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') { - return null; + $block = parent::inlineUrl($Excerpt); + + if (isset($block['element']['attributes']) && \is_array($block['element']['attributes'])) { + $block['element']['attributes']['target'] = '_blank'; } - if (preg_match('/\b(https?:[\/]{2}|file:[\/]{3})[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) { - $url = $matches[0][0]; - - return [ - 'extent' => \strlen($matches[0][0]), - 'position' => $matches[0][1], - 'element' => [ - 'name' => 'a', - 'text' => $url, - 'attributes' => [ - 'href' => $url, - 'target' => '_blank' - ], - ], - ]; - } - - return null; + return $block; } protected function blockTable($Line, ?array $Block = null) // @phpstan-ignore missingType.return,missingType.iterableValue,missingType.parameter diff --git a/templates/activity/details.html.twig b/templates/activity/details.html.twig index 3033c96a..eb775b45 100644 --- a/templates/activity/details.html.twig +++ b/templates/activity/details.html.twig @@ -8,7 +8,7 @@ {% set currency = activity.project.customer.currency %} {% endif %} - {% embed '@theme/embeds/card.html.twig' %} + {% embed '@theme/embeds/card.html.twig' with {fullsize: true} %} {% import "macros/widgets.html.twig" as widgets %} {% import "customer/actions.html.twig" as customerActions %} {% import "project/actions.html.twig" as projectActions %} @@ -21,7 +21,6 @@ {{ widgets.card_tool_button('edit', {'class': 'modal-ajax-form open-edit', 'title': 'edit', 'url': path('admin_activity_edit', {'id': activity.id})}) }} {% endif %} {% endblock %} - {% block box_body_class %}p-0{% endblock %} {% block box_body %} {% if activity.comment is not empty %}
http://example.com/foo-bar.html
+ file:///home/kimai/images/beautiful-flower.png- file:///home/kimai/images/beautiful-flower.png
sdfsdf asdfasdf asdfasdf
# test
aasdfasdf
diff --git a/tests/Utils/ParsedownExtensionTest.php b/tests/Utils/ParsedownExtensionTest.php index 8d4d4bbb..52e7dfcc 100644 --- a/tests/Utils/ParsedownExtensionTest.php +++ b/tests/Utils/ParsedownExtensionTest.php @@ -21,7 +21,7 @@ class ParsedownExtensionTest extends TestCase public function testTableContainsCssClasses(): void { $sut = new ParsedownExtension(); - $html = $sut->parse(' + $html = $sut->text(' | Item | Price | |---|---| | Something | $ 472,78 | @@ -34,7 +34,7 @@ class ParsedownExtensionTest extends TestCase public function testHeaderIsNotConverted(): void { $sut = new ParsedownExtension(); - $html = $sut->parse(' + $html = $sut->text(' # Foo '); self::assertEquals('
# Foo
', $html); diff --git a/tests/Utils/ParsedownTest.php b/tests/Utils/ParsedownTest.php index 7ca2677f..ee41fe04 100644 --- a/tests/Utils/ParsedownTest.php +++ b/tests/Utils/ParsedownTest.php @@ -19,7 +19,7 @@ class ParsedownTest extends TestCase public function testTableContainsCssClasses(): void { $sut = new Parsedown(); - $html = $sut->parse(' + $html = $sut->text(' | Item | Price | |---|---| | Something | $ 472,78 | @@ -32,7 +32,7 @@ class ParsedownTest extends TestCase public function testHeaderContainsId(): void { $sut = new Parsedown(); - $html = $sut->parse(' + $html = $sut->text(' # Foo '); self::assertEquals('Foo
', $html); @@ -41,7 +41,7 @@ class ParsedownTest extends TestCase public function testHeaderContainsIdDoesNotDuplicate(): void { $sut = new Parsedown(); - $html = $sut->parse(' + $html = $sut->text(' # Foo # Foo # Foo diff --git a/tests/phpstan.neon b/tests/phpstan.neon index fad2467b..5fd6eadb 100644 --- a/tests/phpstan.neon +++ b/tests/phpstan.neon @@ -1346,26 +1346,11 @@ parameters: count: 1 path: Form/Type/DurationTypeTest.php - - - message: "#^Method App\\\\Tests\\\\Form\\\\Type\\\\QuickEntryTimesheetTypeTest\\:\\:getExtensions\\(\\) has no return type specified\\.$#" - count: 1 - path: Form/Type/QuickEntryTimesheetTypeTest.php - - message: "#^Method App\\\\Tests\\\\Form\\\\Type\\\\QuickEntryTimesheetTypeTest\\:\\:getTestData\\(\\) has no return type specified\\.$#" count: 1 path: Form/Type/QuickEntryTimesheetTypeTest.php - - - message: "#^Method App\\\\Tests\\\\Form\\\\Type\\\\QuickEntryTimesheetTypeTest\\:\\:testSubmitValidData\\(\\) has parameter \\$expectedDuration with no type specified\\.$#" - count: 1 - path: Form/Type/QuickEntryTimesheetTypeTest.php - - - - message: "#^Method App\\\\Tests\\\\Form\\\\Type\\\\QuickEntryTimesheetTypeTest\\:\\:testSubmitValidData\\(\\) has parameter \\$value with no type specified\\.$#" - count: 1 - path: Form/Type/QuickEntryTimesheetTypeTest.php - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\AbstractCalculatorTestCase\\:\\:assertDescription\\(\\) has parameter \\$addActivity with no type specified\\.$#" count: 1