diff --git a/assets/css/app/bs-overrides.css b/assets/css/app/bs-overrides.css index 6043aafe..61a36bc4 100644 --- a/assets/css/app/bs-overrides.css +++ b/assets/css/app/bs-overrides.css @@ -105,4 +105,19 @@ form .col-form-label.required:after, form label.required:after { position: relative; right: -2px; z-index: 700; +} + +/**************************************** + * HTML diff styling + ****************************************/ + +/* Insertations are marked with green background and bold */ +ins { + background-color: #95f095; + font-weight: bold; +} + +del { + background-color: #f09595; + font-weight: bold; } \ No newline at end of file diff --git a/composer.json b/composer.json index 1be31dc2..0f5c6450 100644 --- a/composer.json +++ b/composer.json @@ -4,16 +4,17 @@ "require": { "php": "^7.4 || ^8.0", "ext-ctype": "*", + "ext-dom": "*", "ext-gd": "*", "ext-iconv": "*", "ext-intl": "*", "ext-json": "*", "ext-mbstring": "*", - "ext-dom": "*", "beberlei/doctrineextensions": "^1.2", "brick/math": "^0.8.15", "composer/package-versions-deprecated": "1.11.99.4", "doctrine/annotations": "^1.6", + "doctrine/data-fixtures": "^1.6.6", "doctrine/dbal": "^3.4.6", "doctrine/doctrine-bundle": "^2.0", "doctrine/doctrine-migrations-bundle": "^3.0", @@ -25,6 +26,7 @@ "gregwar/captcha-bundle": "^2.1.0", "hslavich/oneloginsaml-bundle": "^2.10", "jbtronics/2fa-webauthn": "^1.0.0", + "jfcherng/php-diff": "^6.14", "league/csv": "^9.8.0", "league/html-to-markdown": "^5.0.1", "liip/imagine-bundle": "^2.2", @@ -78,9 +80,7 @@ "twig/intl-extra": "^3.0", "twig/markdown-extra": "^3.0", "web-auth/webauthn-symfony-bundle": "^3.3", - "webmozart/assert": "^1.4", - "doctrine/data-fixtures": "^1.6.6" - + "webmozart/assert": "^1.4" }, "require-dev": { "dama/doctrine-test-bundle": "^7.0", diff --git a/composer.lock b/composer.lock index 609e595c..914a5c40 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "112549e8d6425274bb56b648d18c3878", + "content-hash": "51206f9aa7e372b40ef62a105c4a3f13", "packages": [ { "name": "beberlei/assert", @@ -2628,6 +2628,242 @@ }, "time": "2022-10-03T22:29:32+00:00" }, + { + "name": "jfcherng/php-color-output", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/jfcherng/php-color-output.git", + "reference": "6c7bf16686cc6a291647fcb87491640a2d5edd20" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jfcherng/php-color-output/zipball/6c7bf16686cc6a291647fcb87491640a2d5edd20", + "reference": "6c7bf16686cc6a291647fcb87491640a2d5edd20", + "shasum": "" + }, + "require": { + "php": ">=7.1.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.19", + "liip/rmt": "^1.6", + "phan/phan": "^2 || ^3 || ^4", + "phpunit/phpunit": ">=7 <10", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jfcherng\\Utility\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jack Cherng", + "email": "jfcherng@gmail.com" + } + ], + "description": "Make your PHP command-line application colorful.", + "keywords": [ + "ansi-colors", + "color", + "command-line", + "str-color" + ], + "support": { + "issues": "https://github.com/jfcherng/php-color-output/issues", + "source": "https://github.com/jfcherng/php-color-output/tree/3.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.me/jfcherng/5usd", + "type": "custom" + } + ], + "time": "2021-05-27T02:45:54+00:00" + }, + { + "name": "jfcherng/php-diff", + "version": "6.14.2", + "source": { + "type": "git", + "url": "https://github.com/jfcherng/php-diff.git", + "reference": "8b2bd0c987f69835a816642193d62a7c3664ca96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jfcherng/php-diff/zipball/8b2bd0c987f69835a816642193d62a7c3664ca96", + "reference": "8b2bd0c987f69835a816642193d62a7c3664ca96", + "shasum": "" + }, + "require": { + "jfcherng/php-color-output": "^3", + "jfcherng/php-mb-string": "^1.4.6", + "jfcherng/php-sequence-matcher": "^3.2.9", + "php": ">=7.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.8", + "liip/rmt": "^1.6", + "phan/phan": "^5", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jfcherng\\Diff\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jack Cherng", + "email": "jfcherng@gmail.com" + }, + { + "name": "Chris Boulton", + "email": "chris.boulton@interspire.com" + } + ], + "description": "A comprehensive library for generating differences between two strings in multiple formats (unified, side by side HTML etc).", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/jfcherng/php-diff/issues", + "source": "https://github.com/jfcherng/php-diff/tree/6.14.2" + }, + "funding": [ + { + "url": "https://www.paypal.me/jfcherng/5usd", + "type": "custom" + } + ], + "time": "2023-03-15T19:26:28+00:00" + }, + { + "name": "jfcherng/php-mb-string", + "version": "1.4.8", + "source": { + "type": "git", + "url": "https://github.com/jfcherng/php-mb-string.git", + "reference": "ef8926ff849863bfea234e99ee827947bedd86b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jfcherng/php-mb-string/zipball/ef8926ff849863bfea234e99ee827947bedd86b0", + "reference": "ef8926ff849863bfea234e99ee827947bedd86b0", + "shasum": "" + }, + "require": { + "php": ">=7.1.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.18", + "phan/phan": "^2 || ^3 || ^4", + "phpunit/phpunit": "^7.2 || ^8 || ^9" + }, + "suggest": { + "ext-iconv": "Either \"ext-iconv\" or \"ext-mbstring\" is requried.", + "ext-mbstring": "Either \"ext-iconv\" or \"ext-mbstring\" is requried." + }, + "type": "library", + "autoload": { + "psr-4": { + "Jfcherng\\Utility\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jack Cherng", + "email": "jfcherng@gmail.com" + } + ], + "description": "A high performance multibytes sting implementation for frequently reading/writing operations.", + "support": { + "issues": "https://github.com/jfcherng/php-mb-string/issues", + "source": "https://github.com/jfcherng/php-mb-string/tree/1.4.8" + }, + "funding": [ + { + "url": "https://www.paypal.me/jfcherng/5usd", + "type": "custom" + } + ], + "time": "2023-04-17T14:22:37+00:00" + }, + { + "name": "jfcherng/php-sequence-matcher", + "version": "3.2.9", + "source": { + "type": "git", + "url": "https://github.com/jfcherng/php-sequence-matcher.git", + "reference": "5de2243aa611a66395d85ba1a9169b439bd13e4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jfcherng/php-sequence-matcher/zipball/5de2243aa611a66395d85ba1a9169b439bd13e4d", + "reference": "5de2243aa611a66395d85ba1a9169b439bd13e4d", + "shasum": "" + }, + "require": { + "php": ">=7.1.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.19", + "phan/phan": "^2.5 || ^3 || ^4 || ^5", + "phpunit/phpunit": ">=7 <10", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jfcherng\\Diff\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jack Cherng", + "email": "jfcherng@gmail.com" + }, + { + "name": "Chris Boulton", + "email": "chris.boulton@interspire.com" + } + ], + "description": "A longest sequence matcher. The logic is primarily based on the Python difflib package.", + "support": { + "issues": "https://github.com/jfcherng/php-sequence-matcher/issues", + "source": "https://github.com/jfcherng/php-sequence-matcher/tree/3.2.9" + }, + "funding": [ + { + "url": "https://www.paypal.me/jfcherng/5usd", + "type": "custom" + } + ], + "time": "2023-03-11T06:56:44+00:00" + }, { "name": "laminas/laminas-code", "version": "4.7.1", @@ -16233,12 +16469,12 @@ "platform": { "php": "^7.4 || ^8.0", "ext-ctype": "*", + "ext-dom": "*", "ext-gd": "*", "ext-iconv": "*", "ext-intl": "*", "ext-json": "*", - "ext-mbstring": "*", - "ext-dom": "*" + "ext-mbstring": "*" }, "platform-dev": [], "platform-overrides": { diff --git a/src/Services/LogSystem/LogDataFormatter.php b/src/Services/LogSystem/LogDataFormatter.php index c6ecd84f..7e5935e7 100644 --- a/src/Services/LogSystem/LogDataFormatter.php +++ b/src/Services/LogSystem/LogDataFormatter.php @@ -50,7 +50,14 @@ class LogDataFormatter public function formatData($data, AbstractLogEntry $logEntry, string $fieldName): string { if (is_string($data)) { - return '"' . mb_strimwidth(htmlspecialchars($data), 0, self::STRING_MAX_LENGTH, ) . '"'; + $tmp = '"' . mb_strimwidth(htmlspecialchars($data), 0, self::STRING_MAX_LENGTH, ) . '"'; + + //Show special characters and line breaks + $tmp = preg_replace('/\n/', '\\n
', $tmp); + $tmp = preg_replace('/\r/', '\\r', $tmp); + $tmp = preg_replace('/\t/', '\\t', $tmp); + + return $tmp; } if (is_bool($data)) { diff --git a/src/Services/LogSystem/LogDiffFormatter.php b/src/Services/LogSystem/LogDiffFormatter.php new file mode 100644 index 00000000..92b6d814 --- /dev/null +++ b/src/Services/LogSystem/LogDiffFormatter.php @@ -0,0 +1,76 @@ +. + */ + +namespace App\Services\LogSystem; + +use Jfcherng\Diff\DiffHelper; + +class LogDiffFormatter +{ + /** + * Format the diff between the given data, depending on the type of the data. + * If the diff is not possible, an empty string is returned. + * @param $old_data + * @param $new_data + * @return string + */ + public function formatDiff($old_data, $new_data): string + { + if (is_string($old_data) && is_string($new_data)) { + return $this->diffString($old_data, $new_data); + } + + if (is_numeric($old_data) && is_numeric($new_data)) { + return $this->diffNumeric($old_data, $new_data); + } + + return ''; + } + + private function diffString(string $old_data, string $new_data): string + { + return DiffHelper::calculate($old_data, $new_data, 'Combined', + [ //Diff options + 'context' => 2, + ], + [ //Render options + 'detailLevel' => 'char', + 'showHeader' => false, + ]); + } + + private function diffNumeric($old_data, $new_data): string + { + if ((!is_numeric($old_data)) || (!is_numeric($new_data))) { + throw new \InvalidArgumentException('The given data is not numeric.'); + } + + $difference = $new_data - $old_data; + + //Positive difference + if ($difference > 0) { + return sprintf('+%s', $difference); + } else if ($difference < 0) { + return sprintf('%s', $difference); + } else { + return sprintf('%s', $difference); + } + } +} \ No newline at end of file diff --git a/src/Twig/LogExtension.php b/src/Twig/LogExtension.php index 7440a9b4..a56b9275 100644 --- a/src/Twig/LogExtension.php +++ b/src/Twig/LogExtension.php @@ -21,20 +21,27 @@ namespace App\Twig; use App\Services\LogSystem\LogDataFormatter; +use App\Services\LogSystem\LogDiffFormatter; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -class LogExtension extends AbstractExtension +final class LogExtension extends AbstractExtension { - public function __construct(LogDataFormatter $logDataFormatter) + + private LogDataFormatter $logDataFormatter; + private LogDiffFormatter $logDiffFormatter; + + public function __construct(LogDataFormatter $logDataFormatter, LogDiffFormatter $logDiffFormatter) { $this->logDataFormatter = $logDataFormatter; + $this->logDiffFormatter = $logDiffFormatter; } public function getFunctions() { return [ - new TwigFunction('format_log_data', [$this->logDataFormatter, 'formatData'], ['is_safe' => ['html']]) + new TwigFunction('format_log_data', [$this->logDataFormatter, 'formatData'], ['is_safe' => ['html']]), + new TwigFunction('format_log_diff', [$this->logDiffFormatter, 'formatDiff'], ['is_safe' => ['html']]), ]; } } \ No newline at end of file diff --git a/templates/log_system/details/helper.macro.html.twig b/templates/log_system/details/helper.macro.html.twig index 97d776b8..1081cdda 100644 --- a/templates/log_system/details/helper.macro.html.twig +++ b/templates/log_system/details/helper.macro.html.twig @@ -46,6 +46,9 @@ {% if new_data is not empty %} {% trans %}log.element_changed.data_after{% endtrans %} {% endif %} + {% if new_data is not empty and old_data is not empty %} {# Diff column #} + {% trans %}log.element_changed.diff{% endtrans %} + {% endif %} @@ -76,6 +79,14 @@ {% endif %} {% endif %} + + {% if new_data is not empty and old_data is not empty %} + + {% if new_data[field] is defined and old_data[field] is defined %} + {{ format_log_diff(old_data[field], new_data[field]) }} + {% endif %} + + {% endif %} {% endfor %} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index d27d2eba..5ca07a4d 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -11355,5 +11355,11 @@ Element 3 Data after change + + + log.element_changed.diff + Diff + +