Dynamic Html Attribute format in Razor - c#

How can I format my own attribute in the codebehind?
The idea is to overwrite CSS inline style so I added a property to my Model.
public string GetBannerBackgroundStyle
{
get
{
return !string.IsNullOrEmpty(GetBannerImageMediaUrl) ? string.Format("style=background-image: url('{0}')", GetBannerImageMediaUrl) : string.Empty;
}
}
In the view I simply set the property on the HTML
<div class="anything" #Model.GetBannerBackgroundStyle></div>
The output however is scrambled. It generates something but not properly as if the quotes weren't closed.
Any better ideas?
Thanks in advance!
UPDATE:
public HtmlString GetBannerBackgroundStyle
{
get
{
return new HtmlString(!string.IsNullOrEmpty(GetBannerImageMediaUrl) ? string.Format("style=background-image: url('{0}')", GetBannerImageMediaUrl) : string.Empty);
}
}
This didn't seem to work :s
UPDATE2:
public IHtmlString GetBannerBackgroundStyle
{
get
{
return new HtmlString(!string.IsNullOrEmpty(GetBannerImageMediaUrl) ? string.Format("style=\"background-image: url('{0}')\"", GetBannerImageMediaUrl) : string.Empty);
}
}
This did work ^^

From MSDN:
The Razor syntax # operator HTML-encodes text before rendering it to the HTTP response. This causes the text to be displayed as regular text in the web page instead of being interpreted as HTML markup.
That's where you can use Html.Raw method (if you don't want/can't change your property to return an HtmlString, please remember that right way to do it is that one as suggested by SLaks):
Use the Raw method when the specified text represents an actual HTML fragment that should not be encoded and that you want to render as markup to the HTTP response.
<div class="anything" #Html.Raw(Model.GetBannerBackgroundStyle)></div>
That said I wouldn't put that logic inside your model. An helper method, some JavaScript...but I wouldn't mix styling with data. In your example it should be:
<div class="anything"
style="background-image: url('#Model.GetBannerImageMediaUrl')">
Please note that your code was also wrong because you're missing quotes for HTML style attribute:
if (!String.IsNullOrEmpty(GetBannerImageMediaUrl))
return String.Empty;
return String.Format("style=\"background-image: url('{0}')\"", GetBannerImageMediaUrl);
---^ ---^

You need to make your property return an IHtmlString to tell Razor to not escape it.
First, however, you need to properly escape it yourself, in case the URL has tags or quotes.

How about the code snippet below?
<div class="anything" #(Model != null && !string.IsNullOrWhiteSpace(GetBannerImageMediaUrl)
? "style=background-image: url('" + GetBannerImageMediaUrl + "')"
: string.Empty)></div>

Related

How to implement asp-append-version="true" to background-image property?

I am trying to implement the HTMLTagHelper asp-append-version="true" to my images.
The problem is as regards the DOM distribution, I am not assigning the attribute to an <img> tag but to to a <div> containing the image with the background-url property.
Moreover, the div is generated before all the DOM is loaded and I don't know if there would be a different approach of doing it.
One is obvious, change the div to an img tag, but I don't want it as my design has to remain the same.
My javascript has hitherto been like this:
cardHTML += '<div asp-append-version="true" class="card littleCard" style="background-image: url(/Content/Img/Especialistas/LittleCard/' + especialista.idEspecialista + '.jpg' + ')' + '" >';
cardHTML += '</div>';
The asp-append-version="true" won't work on the div tag.
Any ideas on how to find an approach of dealing with this ?
Thanks
You can create a custom TagHelper to target all elements having an inline style attribute. The following example I've tried looks working fine but if you want something more standard (similar to ImageTagHelper, ...), you can try looking into the base class UrlResolutionTagHelper. I'm not so sure why it need to be more complicated in there in which basically you need to resolve the URL before actually processing it more. I've tried with a simple IFileVersionProvider and it works for relative paths as well (of course the resolved path should be at the current server's web root).
The following simple example works fine for attribute values of HtmlString (which is almost the usual case, some custom rendering may inject IHtmlContent that is not of HtmlString, for such complicated cases, you can refer to the source code for UrlResolutionTagHelper, even copying almost the exact relevant code there is fine):
//target only elements having an inline style attribute
[HtmlTargetElement(Attributes = "style")]
public class InlineStyleBackgroundElementTagHelper : TagHelper
{
readonly IFileVersionProvider _fileVersionProvider;
const string BACKGROUND_URL_PATTERN = "(background(?:-image)?\\s*:[^;]*url)(\\([^)]+\\))";
public InlineStyleBackgroundElementTagHelper(IFileVersionProvider fileVersionProvider)
{
_fileVersionProvider = fileVersionProvider;
}
//bind the asp-append-version property
[HtmlAttributeName("asp-append-version")]
public bool AppendsVersion { get; set; }
//inject ViewContext from the current request
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (AppendsVersion)
{
if (output.Attributes.TryGetAttribute("style", out var styleAttr))
{
//the value here should be an HtmlString, so this basically
//gets the raw plain string of the style attribute's value
var inlineStyle = styleAttr.Value.ToString();
var basePath = ViewContext.HttpContext.Request.PathBase;
inlineStyle = Regex.Replace(inlineStyle, BACKGROUND_URL_PATTERN, m =>
{
//extract the background url contained in the inline style
var backgroundUrl = m.Groups[2].Value.Trim('(', ')', ' ');
//append the version
var versionedUrl = _fileVersionProvider.AddFileVersionToPath(basePath, backgroundUrl);
//format back the inline style with the versioned url
return $"{m.Groups[1]}({versionedUrl})";
}, RegexOptions.Compiled | RegexOptions.IgnoreCase);
output.Attributes.SetAttribute("style", inlineStyle);
}
}
}
}
Usage: just like how you use the asp-append-version on other built-in tag helps. (like in your example).

How can I fix improper Neutralization of Script-Related HTML Tags in a Web Page?

We recently run VeraCode and it failed the following method:
static public void WriteTargetAttribute(HtmlTextWriter writer, string targetValue)
{
if ((writer != null) && (!String.IsNullOrEmpty(targetValue)))
{
if (targetValue.Equals("_blank", StringComparison.OrdinalIgnoreCase))
{
string js = "window.open(this.href, '_blank', ''); return false;";
writer.WriteAttribute("onclick", js);
writer.WriteAttribute("onkeypress", js);
}
else
{
writer.WriteAttribute("target", targetValue);
}
}
}
The VeraCode fails on the last line: " writer.WriteAttribute("target", targetValue);"
What can I do to fix it?
Thank's
The problem is that 'targetValue' is being passed down to your method, but there is no neutralization of this before it gets used - the string gets uses 'as-is' so could contain scripts that cause harm. There is a good description explaining this and why it is a problem: http://www.veracode.com/images/pdf/top5mostprevalent.pdf
Because 'targetValue' will get rendered to the web page, someone could enter script which will get rendered on the final page. If 'targetValue' was a naughty snippet of code you are exposing yourself and your users to a security vulnerability.
Have a read of the tips on this cheat sheet: https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
You should be able to use HtmlEncode to make this safe HttpUtility.HtmlEncode(targetValue);
writer.WriteAttribute("target", System.web.HttpUtility.HtmlEncode(targetValue));

WebView.Invoke(string scriptname, string[]) explanation

WebView1.InvokeScript("eval", args);
I want to know whats the meaning of the word "eval". In MSDN library it says its the name of the script. But when I type something else instead of "eval" the code does not work. So whats the meaning of eval ? and what are the other words I can use instead of eval ?
It's the name of a (most commonly JavaScript) function within your HTML code. eval is just a predefined JavaScript function.
But this can be anything:
// HTML code
<html>
<script>
function alertme(name) {
alert('My name is: ' + name);
}
</script>
</html>
// C# code
public void Test() {
WebView1.InvokeScript("alertme", "Inigo Montoya");
}
This will show you a pop-up on your webpage printing the string "My name is Inigo Montoya".

Syntax for using C# to create multiple HTML attributes via attributes.add

This is long-winded but should be easy for one of you knowledgable chaps to workout.
I have a DotNetNuke webpage with a dynamic login link. If you are not logged in the link will be 'login' and have the appropriate URL to a login popup. If you are logged in the link will be 'logout' and likewise have an the appropriate URL to the webpage that handles logout.
When the page determines if you are logged in or not the HTML link gets built with an attribute of : onclick="return dnnModal.show('http://blahblah.com....').
The code that does this:
loginLink.Attributes.Add(" onclick", "return " + UrlUtils.PopUpUrl(loginLink.NavigateUrl, this, PortalSettings, true, false, 200, 550));
Regardless of what the link is, the ID and Class always remain the same. My problem is that I would like to replace the login text with an image, infact a different image for login and logout. The issue here is that because the ID and Class stay the same I can't just do it via CSS as I normally would, but I have been able to style classes based on their attributes. I have tested this by finding out the output of the creation of the HTML link and styling the class based on the 'href' attribute for example:
a #dnn_dnnLogin_loginLink .LoginLink [href="http://some very very long dynamically created URL.aspx"]{ styles here }
The problem with this is the login/logout links change based on what page you are currently on.
I do know that each of the two rendered options has a uniqe attribue that I could style and that's their "Text" attribute. So quite simply how do I add this attribute to be rendered in HTML so that I can style it with CSS?
I have tried several variations such as:
loginLink.Attributes.Add(" onclick", "return " + UrlUtils.PopUpUrl(loginLink.NavigateUrl, this, PortalSettings, true, false, 200, 550) " Text", + loginLink.Text);
In the hope that what would be rendered would be something like:
onclick="return dnnModal.show('http://localhost/CPD/tabid/87/ctl/Login/Default.aspx?returnurl=%2fCPD.aspx&popUp=true',/*showReturn*/true,200,550,true,'')" Text="Login"
So I could style:
a #dnn_dnnLogin_loginLink .LoginLink [Text="Login"]{styles here}
a #dnn_dnnLogin_loginLink .LoginLink [Text="Logout"]{styles here}
But instead I get a generic error. I have tried various ways of writing the line without success, I just don't know the syntax.
Could someone point me in the right direction? I so hope I'm not barking up the wrong tree as this would be a really simple solution to my initial problem.
Thanks,
Edit - Code for the whole page if that helps?
using System;
using System.Web;
using System.Web.UI;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Services.Exceptions;
using DotNetNuke.Services.Localization;
using DotNetNuke.UI.Modules;
namespace DotNetNuke.UI.Skins.Controls
{
public partial class Login : SkinObjectBase
{
private const string MyFileName = "Login.ascx";
public string Text { get; set; }
public string CssClass { get; set; }
public string LogoffText { get; set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
try
{
if (!String.IsNullOrEmpty(CssClass))
{
loginLink.CssClass = CssClass;
}
if (Request.IsAuthenticated)
{
if (!String.IsNullOrEmpty(LogoffText))
{
if (LogoffText.IndexOf("src=") != -1)
{
LogoffText = LogoffText.Replace("src=\"", "src=\"" + PortalSettings.ActiveTab.SkinPath);
}
loginLink.Text = LogoffText;
}
else
{
loginLink.Text = Localization.GetString("Logout", Localization.GetResourceFile(this, MyFileName));
}
loginLink.NavigateUrl = Globals.NavigateURL(PortalSettings.ActiveTab.TabID, "Logoff");
}
else
{
if (!String.IsNullOrEmpty(Text))
{
if (Text.IndexOf("src=") != -1)
{
Text = Text.Replace("src=\"", "src=\"" + PortalSettings.ActiveTab.SkinPath);
}
loginLink.Text = Text;
}
else
{
loginLink.Text = Localization.GetString("Login", Localization.GetResourceFile(this, MyFileName));
}
string returnUrl = HttpContext.Current.Request.RawUrl;
if (returnUrl.IndexOf("?returnurl=") != -1)
{
returnUrl = returnUrl.Substring(0, returnUrl.IndexOf("?returnurl="));
}
returnUrl = HttpUtility.UrlEncode(returnUrl);
loginLink.NavigateUrl = Globals.LoginURL(returnUrl, (Request.QueryString["override"] != null));
if (PortalSettings.EnablePopUps && PortalSettings.LoginTabId == Null.NullInteger)
{
loginLink.Attributes.Add(" onclick", "return " + UrlUtils.PopUpUrl(loginLink.NavigateUrl, this, PortalSettings, true, false, 200, 550));
}
}
}
catch (Exception exc)
{
Exceptions.ProcessModuleLoadException(this, exc);
}
}
}
}
CSS classes are just designed for this purpose, and they are supported by all browsers that use CSS styling (even very old ones). You don't have to fight around with obscure selectors that are referencing some link that could change and break your styling again.
Since you said you already have a class assigned to these tags, you just want to specify an additional one. You can have more than one class assinged to a tag. See the W3C css class page for more info, in section 'Attribute Values':
Specifies one or more class names for an element. To specify multiple
classes, separate the class names with a space, e.g. . This allows you to combine several CSS classes for one
HTML element.
You can set the second class simply by appending it to the WebControl.CssClass string, separated by a space:
loginLink.CssClass = loginLink.CssClass + " login";
or
loginLink.CssClass = loginLink.CssClass + " logout";
this way you can access it via a single class selector or even the multiple class selector (only selects those tags that have both classes assigned) in your CSS style sheet:
.LoginLink.login { /* styles here */ }
.LoginLink.logout { /* styles here */ }
The text on the login/logout button is not stored in the Text="" attribute, but in the InnerHTML node. So your CSS selector would not apply. (I also think that the spacings in the selector are wrong, and that this solution would not support multilingual buttons, etc.)
Usually this type of styling would be implemented by in the Skin Editor (Admin/Skins/scroll down to section Skin Designer), where you select Skin or Container, File, Token=LOGIN, Setting=Text and LogoffText, and add a value src=path/to/a.gif. However, the skin designer seems to be broken in 6.1.x (bug report)
You might still try and have a look at the login.ascx and login.ascx.cs files in the admin\Skins directory of your DNN installation. Edit the code to assign loginLink.ImageUrl depending on Request.IsAuthenticated.

How to display html elements like links in errors rendered via Html.ValidationSummary()

One of my error message renders a link. However, Html.ValidationSummary() encodes it and therefore it displays as follow:
An account with the mobile or email you have specified already exists.
If you have forgotten your password, please Reset it.
Instead, it should render as:
An account with the mobile or email you have specified already exists.
If you have forgotten your password, please Reset it.
The error is added to the ModelState inside view as follows:
if (...)
{
ViewData.ModelState.AddModelError(string.Empty, string.Format("An account with the mobile or email you have specified already exists. If you have forgotten your password, please {0} it.", Html.ActionLink("Reset", "Reset")));
}
In short, how should I prevent Html.ValidationSummarry() to selectively/entirely encoding html in errors.
The current HTML helpers for displaying error messages do not support this. However, you could write your own HTML helpers that display the error message without HTML escaping it, i.e. they would treat the error message as raw HTML.
As a starting point, you could use the ASP.NET MVC source code from Codeplex, specifically the ValidationSummary method of the ValidationExtensions class:
public static string ValidationSummary(this HtmlHelper htmlHelper, string message, IDictionary<string, object> htmlAttributes) {
// Nothing to do if there aren't any errors
if (htmlHelper.ViewData.ModelState.IsValid) {
return null;
}
string messageSpan;
if (!String.IsNullOrEmpty(message)) {
TagBuilder spanTag = new TagBuilder("span");
spanTag.MergeAttributes(htmlAttributes);
spanTag.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName);
spanTag.SetInnerText(message);
messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
}
else {
messageSpan = null;
}
StringBuilder htmlSummary = new StringBuilder();
TagBuilder unorderedList = new TagBuilder("ul");
unorderedList.MergeAttributes(htmlAttributes);
unorderedList.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName);
foreach (ModelState modelState in htmlHelper.ViewData.ModelState.Values) {
foreach (ModelError modelError in modelState.Errors) {
string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */);
if (!String.IsNullOrEmpty(errorText)) {
TagBuilder listItem = new TagBuilder("li");
listItem.SetInnerText(errorText);
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
}
}
}
unorderedList.InnerHtml = htmlSummary.ToString();
return messageSpan + unorderedList.ToString(TagRenderMode.Normal);
}
You can then change this method to treat the error message as raw HTML.
Two warnings though:
You're changing the meaning of certain properties of the ModelState class. While you get away with using your own HTML helpers now, a future version of ASP.NET MVC might introduce changes that no longer work with this approach.
Be very careful about not using error messages that aren't properly escaped so you don't expose your web app to XSS attacks. Certain standard validation annotation might not work any longer since they don't HTML escape the error message.

Categories