Skip to content

Fix #19368 - Improve line ending handling in grid editor#20076

Open
og-khushalpatel wants to merge 28 commits intophpmyadmin:QA_5_2from
og-khushalpatel:fix/preserve-crlf-on-value-click
Open

Fix #19368 - Improve line ending handling in grid editor#20076
og-khushalpatel wants to merge 28 commits intophpmyadmin:QA_5_2from
og-khushalpatel:fix/preserve-crlf-on-value-click

Conversation

@og-khushalpatel
Copy link
Copy Markdown
Contributor

Description

This pull request adds explicit line ending (CRLF / LF) handling to inline grid editing for text-like fields.
Browsers normalize textarea input to LF, which previously made it impossible for users to reliably view, preserve, or intentionally change the original line-ending format stored in the database. This PR addresses that gap by:

  • Detecting the original line-ending format (CRLF or LF) on the server
  • Exposing a line-ending selector in the inline edit area for applicable text fields
  • Allowing users to preserve or intentionally change the line-ending format
  • Ensuring idempotent behavior to avoid duplicated line breaks on repeated edits
  • Keeping rendering and comparison logic consistent across truncated and non-truncated values
    The implementation follows existing phpMyAdmin patterns and avoids altering behavior for non-textual field types.

Fixes #19368 - \r\n is replaced with \n when clicking on a value

Before submitting pull request, please review the following checklist:

  • [ X ] Make sure you have read our CONTRIBUTING.md document.
  • [ X ] Make sure you are making a pull request against the correct branch. For example, for bug fixes in a released version use the corresponding QA branch and for new features use the master branch. If you have a doubt, you can ask as a comment in the bug report or on the mailing list.
  • [ X ] Every commit has proper Signed-off-by line as described in our DCO. This ensures that the work you're submitting is your own creation.
  • [ X ] Every commit has a descriptive commit message.
  • [ X ] Every commit is needed on its own, if you have just minor fixes to previous commits, you can squash them.
  • Any new functionality is covered by tests.

@liviuconcioiu
Copy link
Copy Markdown
Contributor

liviuconcioiu commented Feb 5, 2026

It fixes the issue, except it introduces an old bug #17398. It also happens on empty values, without NULL.

05.02.2026_16.30.32_REC.mp4

Also, I think it should be Line ending:, without s, since only one line ending can be selected and work at a time. Also, the help icon should be moved after :.

@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

Hi @liviuconcioiu ,
thanks a lot for the review and for pointing these things out.

Just to clarify one point: the change in this PR does not introduce the old bug #17398 which has been demonstrated in the attached video. That said, I agree with your observation that it also occurs on empty values without NULL. I’ve addressed that case now in the latest update.

I’ve also updated the label to Line ending: (singular) and moved the help icon after the colon, as suggested.

Please let me know if you’d like me to adjust anything further. Thanks again for the helpful feedback!
[Screencast from 2026-02-06 19-50-46.webm]
(https://github.com/user-attachments/assets/9e5aadd7-66b5-4eb7-8ebb-e6b338e7abee)

@liviuconcioiu
Copy link
Copy Markdown
Contributor

UPDATE is still being triggered:

06.02.2026_16.50.48_REC.mp4

@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

@liviuconcioiu I’ve gone through the scenario again on my end and also shared the recording as proof, but I’m still unable to reproduce the issue you’re observing. To make sure I’m not missing something, could you please share the exact table structure/schema (including column types and any relevant constraints) that you’re using when encountering this problem?
Having the precise structure would really help me set up the same environment locally and understand where the mismatch might be coming from. I’d like to ensure the fix works correctly for your case as well.
Thanks in advance for your help.

@liviuconcioiu
Copy link
Copy Markdown
Contributor

I use MariaDB 12.2.1 on Windows.

CREATE TABLE `test` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `colA` varchar(255) DEFAULT NULL,
  `colB` varchar(255) NOT NULL,
  `colC` int(10) unsigned DEFAULT NULL,
  `colD` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`colD`)),
  `colE` uuid DEFAULT NULL,
  `colF` timestamp(6) NOT NULL DEFAULT current_timestamp(6),
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

INSERT INTO `test` (`id`, `colA`, `colB`, `colC`, `colD`, `colE`, `colF`) VALUES
(1, 'Lynne', 'Combs', 1, '{\"test\":100}', '7b42b69b-f744-11f0-9d12-806d971fcc98', '2026-01-22 03:43:14.629614'),
(2, 'Carlos', 'Lara', NULL, NULL, NULL, '2026-01-22 03:43:46.727948'),
(3, NULL, 'Lang', 200, '{\"x\":\"y\"}', NULL, '2026-01-22 03:44:20.661601'),
(4, NULL, '', NULL, NULL, 'c0b2cf7a-f744-11f0-9d12-806d971fcc98', '2026-01-22 03:45:11.126818');

@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

og-khushalpatel commented Feb 8, 2026

Hi @liviuconcioiu, thanks for sharing the exact table structure/schema.
This turned out to be caused by jQuery’s .data() cache auto-casting values (numbers / JSON objects) on first access, which broke the comparison. Normalizing both sides fixes it.. I’ve now normalized both the canonical old and new values before comparison, which avoids the unnecessary updates you mentioned.
Please let me know if this looks correct from your side.

@liviuconcioiu
Copy link
Copy Markdown
Contributor

Found another problem. If I change the line ending to a cell from LF to CRLF, the cell below will have the same line ending as the first, instead of keeping it.

CREATE TABLE `test` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `value` text DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

INSERT INTO `test` (`id`, `value`) VALUES
(1, 'test\ntest'),
(2, 'test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test <td class=\"text-right\">\n        test test test test test test test test test test test test test test test test test test test test</td>');
08.02.2026_10.56.03_REC.mp4

@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

Found another problem. If I change the line ending to a cell from LF to CRLF, the cell below will have the same line ending as the first, instead of keeping it.

Hi @liviuconcioiu, just a quick update! The issue you mentioned is now fixed. It was due to stale event handlers in the grid editor. Would appreciate a quick review when you’re free.

@liviuconcioiu
Copy link
Copy Markdown
Contributor

Everything is okay now!

@liviuconcioiu
Copy link
Copy Markdown
Contributor

There are some tests that don't pass:
https://github.com/phpmyadmin/phpmyadmin/actions/runs/22016015278/job/63835593911?pr=20076

CSS needs a little improvement:
https://github.com/phpmyadmin/phpmyadmin/actions/runs/22016015270/job/63835593397?pr=20076

And if you can update the baselines:
https://github.com/phpmyadmin/phpmyadmin/actions/runs/22016015270/job/63835593417?pr=20076

@og-khushalpatel og-khushalpatel force-pushed the fix/preserve-crlf-on-value-click branch from 5e6d48f to e2caafa Compare February 22, 2026 16:23
@williamdes
Copy link
Copy Markdown
Member

Due to how big the diff is, I think only 6.0 versions should be fixed. Maybe rebase onto the master branch.
@MauricioFauth what do you think ?

@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

og-khushalpatel commented Mar 1, 2026

Hi @williamdes @MauricioFauth @liviuconcioiu , I’m following up on this PR in case it got buried. Please let me know if any changes are needed from my side. Thank you!

@MoonE
Copy link
Copy Markdown
Contributor

MoonE commented Mar 5, 2026

@og-khushalpatel Please look into the failing test cases.

@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

There are some tests that don't pass: https://github.com/phpmyadmin/phpmyadmin/actions/runs/22016015278/job/63835593911?pr=20076

CSS needs a little improvement: https://github.com/phpmyadmin/phpmyadmin/actions/runs/22016015270/job/63835593397?pr=20076

And if you can update the baselines: https://github.com/phpmyadmin/phpmyadmin/actions/runs/22016015270/job/63835593417?pr=20076

@MoonE I believe I have already fixed the tests that were previously failing due to the changes in this PR.
There are still some failing tests, but they appear to be unrelated to the changes introduced in this PR. If you notice any tests failing specifically because of this PR, please point them out and I’ll be happy to address them

Copy link
Copy Markdown
Contributor

@MoonE MoonE left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't looked what causes the other failure.
But I don't see it failing in the master branch, so it has to be related to these changes.

Also the amount of added lines in phpstan-baseline.neon suggests you are running a different configuration than on the server, shouldn't be this many changes.

Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
@og-khushalpatel og-khushalpatel requested a review from MoonE March 7, 2026 16:47
Copy link
Copy Markdown
Contributor

@MoonE MoonE left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments on how to resolve the linter problems.

Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
@og-khushalpatel og-khushalpatel force-pushed the fix/preserve-crlf-on-value-click branch 2 times, most recently from f4b53bc to 41f80d2 Compare March 8, 2026 10:57
@og-khushalpatel og-khushalpatel requested a review from MoonE March 8, 2026 11:03
@og-khushalpatel og-khushalpatel force-pushed the fix/preserve-crlf-on-value-click branch 2 times, most recently from 3e5924c to faaeb5f Compare March 9, 2026 06:10
Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
@og-khushalpatel og-khushalpatel force-pushed the fix/preserve-crlf-on-value-click branch from faaeb5f to 9f3b983 Compare March 9, 2026 06:23
@og-khushalpatel og-khushalpatel requested a review from MoonE March 9, 2026 06:31
@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

og-khushalpatel commented Mar 9, 2026

@MoonE I believe that finally all pipeline failures have been fixed except for one which is not manifesting because of this PR-related changes. I am really grateful for you helped me understand pipeline failure debugging which was a bit scary for a beginner like me. Let me know if I should notify the maintainers for review.

Copy link
Copy Markdown
Contributor

@MoonE MoonE left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user edits a text value that at the begin has no line breaks, it's not possible to select the line breaks, even after closing and re-opening the edit box. It only shows up after a refresh of the whole browse result.

Would be great if it becomes visible after a new line is entered.

Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

If a user edits a text value that at the begin has no line breaks, it's not possible to select the line breaks, even after closing and re-opening the edit box. It only shows up after a refresh of the whole browse result.

Would be great if it becomes visible after a new line is entered.

Implemented the code to make the line ending selector visible after a newline is entered. Just check it out:

Screencast.from.2026-03-10.19-00-45.webm

@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

Hi, I looked into the CI failure and I believe it's unrelated to my changes. The error occurs in the stop-local-server cleanup script:

cat: /tmp/last_temp_dir_phpMyAdminTests: No such file or directory

My changes only move i18n strings from JavaScriptMessagesController.php into the Twig template and adjust JS logic in makegrid.js — none of which affect the Selenium server startup (php-fpm/nginx). This looks like a CI environment issue.
Could we re-run the failed job to confirm?

@og-khushalpatel og-khushalpatel requested a review from MoonE March 10, 2026 14:21
Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
Signed-off-by: Khushal Patel <khushal.b.patel01@gmail.com>
@MoonE
Copy link
Copy Markdown
Contributor

MoonE commented Mar 10, 2026

Is there a reason why the line ending detection is done server-side and why an additional original value has to be included in the html?

There is existing logic loading the full text when it is displayed truncated, so it would make sense to get the data only when it is needed. Otherwise this can inflate the html size by a lot, which can be a problem when you have columns containing very long strings.

Maybe check here if the cell contains a <br> and then use the response value to initialize the line-break select?

phpmyadmin/js/src/makegrid.js

Lines 1081 to 1108 in 44da39f

} else if ($td.is('.truncated, .transformed')) {
if ($td.is('.to_be_saved')) { // cell has been edited
var value = $td.data('value');
$(g.cEdit).find('.edit_box').val(value);
$editArea.append('<textarea></textarea>');
$editArea.find('textarea').val(value);
$editArea
.on('keyup', 'textarea', function () {
$(g.cEdit).find('.edit_box').val($(this).val());
});
$(g.cEdit).on('keyup', '.edit_box', function () {
$editArea.find('textarea').val($(this).val());
});
$editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
} else {
// handle truncated/transformed values values
$editArea.addClass('edit_area_loading');
// initialize the original data
$td.data('original_data', null);
/**
* @var sqlQuery String containing the SQL query used to retrieve value of truncated/transformed data
*/
var sqlQuery = 'SELECT `' + fieldName + '` FROM `' + g.table + '` WHERE ' + whereClause;
// Make the Ajax call and get the data, wrap it and insert it
g.lastXHR = $.post('index.php?route=/sql', {

This way there is no data added during normal browsing operation.

Comment on lines +4422 to +4423
string $data,
string $displayedData,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be part of the bug fix

Copy link
Copy Markdown
Contributor Author

@og-khushalpatel og-khushalpatel Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normalizing line-breaks to \n in $data requires performing string operations when initializing $canonicalValue. For that reason, I wanted to ensure $data is always treated as a string.

Initially, I handled this with an is_string() check because $data wasn't type-hinted. However, since $data is already documented as @param string $data, the linter flagged the conditional as unnecessary.

To resolve that while still ensuring type safety, I type-hinted $data as string instead. That's why I included it as part of the bug fix.

@og-khushalpatel
Copy link
Copy Markdown
Contributor Author

Is there a reason why the line ending detection is done server-side

Great Question! Yes, this is intentional. According to the HTML spec, <textarea> normalizes all line breaks to \n when returning its value, even if the original database value contains \r\n. This normalization was the root cause of the false "value changed" detection on the client side (i.e., #19368 ), since the client would always see a difference between the textarea value and the original, triggering an unnecessary save.
By detecting line endings server-side, we preserve the actual line-break style stored in the database and can compare accurately.

why an additional original value has to be included in the html?

We need the server-provided original value as a source of truth to prevent \n duplication on each save cycle. Without it, when converting between LF and CRLF (or vice versa), the <br> tags were being duplicated; leading to exponential growth of line breaks in the stored value with every update. Including the original value lets us diff correctly against what the database actually holds, rather than relying on the textarea's already-normalized content.

Otherwise this can inflate the html size by a lot, which can be a problem when you have columns containing very long strings

That's a great point! To address this concern, the HTML size won't actually inflate, since for full-text database columns, I'm storing the truncated value in the data-original-value attribute rather than the complete column content. So the overhead remains minimal regardless of how long the underlying strings are.You can verify it in the following video:
Screencast from 2026-03-11 12-01-22.webm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants