New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SelectOneMenu: html reserved chars are shown as entities #9336
Comments
Reproducer: MonkeyPatch: if (PrimeFaces.widget.SelectOneMenu) {
PrimeFaces.widget.SelectOneMenu.prototype.renderSelectItem = function(item, isGrouped) {
var content = "";
var $item = $(item);
var label, dataLabel;
var title = $item.data("title");
var escape = $item.data("escape");
var cssClass;
if (item.tagName === "OPTGROUP") {
label = $item.attr("label");
if (escape) {
label = $("<div>").text(label).html();
}
cssClass = "ui-selectonemenu-item-group ui-corner-all";
} else { //OPTION
if (escape) {
label = $item.html();
if ($item.text() === " ") {
label = $item.text();
}
} else {
label = $item.text();
}
cssClass = "ui-selectonemenu-item ui-selectonemenu-list-item ui-corner-all";
if (isGrouped) {
cssClass += " ui-selectonemenu-item-group-children";
}
}
dataLabel = this.escapeHTMLIfNecessary(label.replace(/(<([^>]+)>)/gi, ""));
if ($item.data("noselection-option")) {
cssClass += " ui-noselection-option";
}
content += '<li class="' + cssClass + '" tabindex="-1" role="option"';
if (title) {
content += ' title="' + title + '"';
}
if ($item.is(':disabled')) {
content += ' disabled';
}
content += ' data-label="' + dataLabel + '"';
content += '>';
content += label;
content += '</li>';
if (item.tagName === "OPTGROUP") {
content += this.renderSelectItems($item, true);
}
return content;
};
PrimeFaces.widget.SelectOneMenu.prototype.escapeHTMLIfNecessary = function(value) {
return String(value).replace(/[<>"'`=\/]/g, function(s) {
return PrimeFaces.entityMap[s];
});
};
} |
My example is still broken @melloware primefaces/primefaces/src/main/resources/META-INF/resources/primefaces/forms/forms.selectonemenu.js Lines 1555 to 1557 in d450cb7
The value received from $item.html() is an already escaped value:primefaces/primefaces/src/main/resources/META-INF/resources/primefaces/forms/forms.selectonemenu.js Lines 1540 to 1545 in d450cb7
I removed lines 1555 to 1557 and now it works flawlessly. |
Let me look. I had your whole example working yesterday? |
@NicolaIsotta did you run my reproducer I attached. it has that patch as well as I properly <f:selectItem itemValue="A" itemLabel="A"/>
<f:selectItem itemValue="B" itemLabel="#{testView.string}" itemEscaped="false"/>
<f:selectItem itemValue="C" itemLabel="& > <" itemEscaped="false"/> |
ampersand is not html code, Mojarra's h:selectOneMenu handles it without needing
So there's a double escape of the value, one of the two can be safely removed. |
I'll try to be clearer with an example. <p:selectOneMenu>
<f:selectItem itemValue="A" itemLabel="#{testView.ampersand}" />
</p:selectOneMenu>
<h:selectOneMenu>
<f:selectItem itemValue="A" itemLabel="#{testView.ampersand}" />
</h:selectOneMenu> And this is the java: @Getter
@Setter
private String ampersand = "&"; The rendered output, with both Mojarra and MyFaces, is: I expect primefaces to show "&" as both the dropdown item and the current value, just like the standard h:selectOneMenu. It does not make any sense to show two different values. In my message #9336 (comment) I wrote a possible fix, let me know if I need to open a new issue and/or a pull request. |
OK that but your ORIGINAL issue was @Getter
private String label = "<&>"; are you saying removing the following fixes it: if (escape) {
dataLabel = PrimeFaces.escapeHTML(label.replace(/(<([^>]+)>)/gi, ""));
} Let me submit a PR and let the Integration tests run. |
OK two Integration test failures:
|
I think the major difference between |
I saw. I'll try find another solution. |
I will close for now as it works as designed but let me know if you come up with a solution or you can just leave yours MonkeyPatched to leave that code out. |
@josefplch I just wrote an Integration test. I added new SelectItem("GitHub \"9336\" Quoted", "GitHub \"9336\" Quoted") Then in my Test I check the value... Assertions.assertEquals("GitHub \"9336\" Quoted", options.get(8).getText()); And the Generated HTML: <li class="ui-selectonemenu-item ui-selectonemenu-list-item ui-corner-all ui-selectonemenu-item-group-children" tabindex="-1" role="option"
data-label="GitHub "9336" Quoted" aria-selected="false" id="form:selectonemenu_8">GitHub "9336" Quoted</li> So everything is passing right now with NO changes. What am I doing differently than you? |
@melloware Well, that’s weird… Without my modification, with the same SelectItem definition, in both Chrome and Firefox, I get: <li class="ui-selectonemenu-item ui-selectonemenu-list-item ui-corner-all ui-selectonemenu-item-group-children" tabindex="-1" role="option"
data-label="GitHub " 9336"="" quoted"="" aria-selected="true" id="form:selectonemenu_1">GitHub "9336" Quoted</li> The label logged via JavaScript is I use PrimeFaces 12.0.0, running it with Java 11.0.17+8, Mojarra 2.3.18. |
Oh yeah I am running 13.0.0-SNAPSHOT in my Integration Tests but it has the above MonkeyPatch included which is why I am confused you needed to add. |
Can you try this MonkeyPatch which is exactly the code my Integration Test is using? if (PrimeFaces.widget.SelectOneMenu) {
PrimeFaces.widget.SelectOneMenu.prototype.renderSelectItem = function(item, isGrouped) {
var content = "";
var $item = $(item);
var label;
var title = $item.data("title");
var escape = $item.data("escape");
var cssClass;
if (item.tagName === "OPTGROUP") {
label = $item.attr("label");
if (escape) {
label = $("<div>").text(label).html();
}
cssClass = "ui-selectonemenu-item-group ui-corner-all";
} else { //OPTION
if (escape) {
label = $item.html();
if ($item.text() === " ") {
label = $item.text();
}
} else {
label = $item.text();
}
cssClass = "ui-selectonemenu-item ui-selectonemenu-list-item ui-corner-all";
if (isGrouped) {
cssClass += " ui-selectonemenu-item-group-children";
}
}
dataLabel = PrimeFaces.escapeHTML(label.replace(/(<([^>]+)>)/gi, ""));
if (escape) {
dataLabel = dataLabel.replace("&", "&");
}
if ($item.data("noselection-option")) {
cssClass += " ui-noselection-option";
}
content += '<li class="' + cssClass + '" tabindex="-1" role="option"';
if (title) {
content += ' title="' + title + '"';
}
if ($item.is(':disabled')) {
content += ' disabled';
}
content += ' data-label="' + dataLabel + '"';
content += '>';
content += label;
content += '</li>';
if (item.tagName === "OPTGROUP") {
content += this.renderSelectItems($item, true);
}
return content;
}
} |
Sure. It handles the quotes correctly, but after click, the label for |
But i believe that is correct behavior. If you want it like that don't you need |
When you show |
@josefplch i think I got it if you want to try this one... I updated my integration tests with your new scenario. if (PrimeFaces.widget.SelectOneMenu) {
PrimeFaces.widget.SelectOneMenu.prototype.renderSelectItem = function(item, isGrouped) {
var content = "";
var $item = $(item);
var label, dataLabel;
var title = $item.data("title");
var escape = $item.data("escape");
var cssClass;
if (item.tagName === "OPTGROUP") {
label = $item.attr("label");
if (escape) {
label = $("<div>").text(label).html();
}
cssClass = "ui-selectonemenu-item-group ui-corner-all";
} else { //OPTION
if (escape) {
label = $item.html();
if ($item.text() === " ") {
label = $item.text();
}
} else {
label = $item.text();
}
cssClass = "ui-selectonemenu-item ui-selectonemenu-list-item ui-corner-all";
if (isGrouped) {
cssClass += " ui-selectonemenu-item-group-children";
}
}
dataLabel = this.escapeHTMLIfNecessary(label.replace(/(<([^>]+)>)/gi, ""));
if ($item.data("noselection-option")) {
cssClass += " ui-noselection-option";
}
content += '<li class="' + cssClass + '" tabindex="-1" role="option"';
if (title) {
content += ' title="' + title + '"';
}
if ($item.is(':disabled')) {
content += ' disabled';
}
content += ' data-label="' + dataLabel + '"';
content += '>';
content += label;
content += '</li>';
if (item.tagName === "OPTGROUP") {
content += this.renderSelectItems($item, true);
}
return content;
};
PrimeFaces.widget.SelectOneMenu.prototype.escapeHTMLIfNecessary = function(value) {
return String(value).replace(/[<>"'`=\/]/g, function(s) {
return PrimeFaces.entityMap[s];
});
};
} |
@melloware Results for the updated MonkeyPatch:
I think these two items should behave the same way – keep |
I think it does. Can you post your monkey patch I can run it against the existing integration tests which are all passing. |
Sure. if (PrimeFaces.widget.SelectOneMenu) {
PrimeFaces.widget.SelectOneMenu.prototype.renderSelectItem = function(item, isGrouped) {
var content = "";
var $item = $(item);
var label;
var title = $item.data("title");
var escape = $item.data("escape");
var cssClass;
if (item.tagName === "OPTGROUP") {
label = $item.attr("label");
if (escape) {
label = $("<div>").text(label).html();
}
cssClass = "ui-selectonemenu-item-group ui-corner-all";
} else { //OPTION
if (escape) {
label = $item.html();
if ($item.text() === " ") {
label = $item.text();
}
} else {
label = $item.text();
}
cssClass = "ui-selectonemenu-item ui-selectonemenu-list-item ui-corner-all";
if (isGrouped) {
cssClass += " ui-selectonemenu-item-group-children";
}
}
// The original MonkeyPatch used: "var dataLabel = label;" which allowed HTML injection.
var dataLabel = escape ? label.replaceAll('"', '"') : PrimeFaces.escapeHTML(label);
if ($item.data("noselection-option")) {
cssClass += " ui-noselection-option";
}
content += '<li class="' + cssClass + '" tabindex="-1" role="option"';
if (title) {
content += ' title="' + title + '"';
}
if ($item.is(':disabled')) {
content += ' disabled';
}
content += ' data-label="' + dataLabel + '"';
content += '>';
content += label;
content += '</li>';
if (item.tagName === "OPTGROUP") {
content += this.renderSelectItems($item, true);
}
return content;
}
} |
It looks like the Integration tests are passing but your fix has 1 failing scenario that mine does not. Here is a runnable reproducer: From your Stack Overflow pos your scenario 4: new SelectItem("< <i>italics</i>", "< <i>italics</i>", "", false, true) Is broken with your fix and will not select the item. Where with mine the item is selected properly. It looks properly in the dropdown but the value is NOT selectable. |
If I change your monkeypatch to my escapeIfNecessary it all works including the scenario above var dataLabel = escape ? label.replaceAll('"', '"') : this.escapeHTMLIfNecessary(label); |
@melloware Thanks for your effort, but for some reason, my fix works in my product and all the items are both rendered properly and selectable as expected. It passed both our automatic and manual tests. Unfortunately, I don’t have any more time to explore the issue, so I will just stick with my solution; I hope I have at least contributed to the extension of officially tested cases. |
Does this also effects version 11? I just got a user report of this issue on a build running version 11.0.8. The selection list shows "This & That" but the selected value shows " |
Yep you can use this MonkeyPatch for your 11.0.8 release as well. |
So to track this for the version 11, I assume just create a new issue and link it to this one? |
No this is community tracker only. For 11 you would need to report through your PRO account. See: https://github.com/primefaces/primefaces#community--elite--pro |
Describe the bug
In the selected value of a p:selectOneMenu, html reserved chars (eg: &, <, >) are shown as entities:
(If itemEscaped="false" chars are not shown at all)
Reproducer
Expected behavior
No response
PrimeFaces edition
Community
PrimeFaces version
12.0.0
Theme
No response
JSF implementation
Mojarra
JSF version
2.3.18
Java version
11
Browser(s)
No response
The text was updated successfully, but these errors were encountered: