I want to conditionally link a CSS file base on the user's Membership role. An administrator should link my admin.css file while everybody else (other roles and anonymous users) should display my global.css file. Can this be done?
Try this:
protected void Page_Init(object sender, EventArgs e)
{
HtmlLink css = new HtmlLink();
// add conditional logic to add correct css file
css.Href = "css/fancyforms.css";
css.Attributes["rel"] = "stylesheet";
css.Attributes["type"] = "text/css";
css.Attributes["media"] = "all";
Page.Header.Controls.Add(css);
}
Either you can create theme based on the role - AdminTheme (will contain admin.css) & GlobalTheme (will contain global.css), or else you can dynamically write the <link> element in the <head> tag after giving the runat="Server" attribute to it.
You can then set the page's theme dynamically in the PreInit or Init event based on the role.
Sure, when you're outputting the <head> section, have an if statement check a session variable that has their "user level", then output the corresponding <link> tag (or #import line, depending how you prefer to do your CSS).
If you set the head element of your page to run server-side, you can inject a new HtmlGenericControl into the header that represents the link if the user is in a particular role.
If you wish to enable/disable/show/hide controls based on the role this would be nothing more than "security by obscurity" since switching off styles or setting a browser to a specific css file to override what is actually served would easily display all that is secret.
Another issue would be caching. Some browsers like Opera/Firefox happily cache all that is cacheable, so the user will have to click "Reload page". You can probably disable caching but then your css will be downloaded all over again thus unnecessarily consuming traffic.
Related
I am facing Site-improve accessibility error that my page doesn't have a "lang tag" defined. How can I accomplish this in Kentico?
There are possibly two ways to achieve this. One is through C# .NET code in Kentico and the other is through Javascript code. I'm providing you both solutions
In order to fix this problem you have fix two tags for your tool to stop pointing accessibility error. You have to set both "lang" as well as "xml:lang" tag. One way to achieve this through pure Javascript is below. I am proposing this solution as it is independent of any platform.
(function() {
document.getElementsByTagName('html')[0].setAttribute('lang', 'en-US');
document.getElementsByTagName('html')[0].setAttribute('xml:lang','en-US');
})();
You may replace 'en' with language of your choice to correctly point the correct language in place
In Kentico you may achieve this by placing this code in your master template so that these tags can be added on all pages.
In your Kentico master template just add a Head HTML web part and put this code inside it.
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (CurrentDocument != null)
{
CMS.UIControls.ContentPage page= this.Page as CMS.UIControls.ContentPage;
if (page != null)
{
string lang= CMS.Localization.LocalizationContext.CurrentCulture.CultureCode;
page.XmlNamespace += " lang=\"" + lang + "\"";
page.XmlNamespace += " xml:lang=\"" + lang + "\"";
}
}
}
</script>
This should fix everything for you.
The recommended way is to open ~/CMSPages/PortalTemplate.aspx web form and edit the lang attribute manually just as you see fit.
Please note that generally speaking the modification of Kentico system files is not recommended, but in this case it is the best way to go. Just keep in mind to document all changes you make.
Edit:
Other solution which does not involve any customization is to use CMSPortalTemplatePage web.config key to set up path to a custom PortalTemplate file. This way you can clone Kentico's PortalTemplate.aspx, make modification in this cloned file and keep the original.
Possible usage:
<add key="CMSPortalTemplatePage" value="~/CMSPages/CustomPortalTemplate.aspx" />
I'm trying to identify the home page, and then sub-pages on nodes. Ideally as a class on the Body so i can make style changes based on which section the user is in.
I have two .master pages, Global and Site. The body tag is in Global, and Site is a child master.
As this is an English/French site, I'm hoping using the Node Name would be the easiest approach for me.
If your home page and internal pages are of different Page Type then you can benefit from the macro below. We use something like this to give Page type specific id on the body tag and is usually helpful
<body class="{% CurrentDocument.NodeClass.ClassName.ToString() #%}">
However, it can be tweaked to suit your needs.
I'd recommend you to implement one generic stylesheet, shared across all the pages, and a couple of section specific stylesheets.
I'm not 100% clear about what you are describing, but you could use NodeLevel direct as / is NodeLevel = 0, and everything else will be Level 1+
Here's what i've come up in the .cs of my global .Master. So far it seem to be giving me enough to work with. From here i can use js and css to target what i need.
string aliasPath = CMS.DocumentEngine.DocumentContext.OriginalAliasPath.ToLower().TrimStart('/');
if (aliasPath == "")
{
this.BodyClass += " home";
}
else
{
this.BodyClass += " " + aliasPath.Replace("/","_");
}
The simplest approach, if you are willing to add either a data attribute or an id attribute to the body tag, is to add something like this to the master page, in the text box of the body tag:
id="{%nodealias%}"
Since the node alias will typically be unique unless you have multiple pages, at different paths, with the same name, ID should work fine. I prefer to use "nodealias" as opposed to the friendly name because it ensures there will not be any special characters, and will replace white space etc with a dash "-". The above macro will add the node alias as an id attribute to the body tag. Here's a screenshot:
Alternative approach explained here: https://devnet.kentico.com/forums/f49/t43559/body-class which involves setting the body class using a combination of macros and code behind, or via a custom web part. Basically you can modify it using CMS.CMSHelper.CMSContext.CurrentBodyClass (this was written in 2014 so the syntax will be slightly different)
-Edit:
Adding to some of the other answers, if you'd rather add a stylesheet on a template or page level, you can do so. To add a stylesheet reference on every instance of a template, edit the template header properties:
If you want to include the stylesheet on any particular page, this can be accomplished by editing the General tab on the page level, but this will remove the main stylesheet you are using (depending on how you are including it in the page):
In the body section in the Master page you can start with
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if(CurrentDocument.DocumentName== "Site")
CMS.DocumentEngine.DocumentContext.CurrentBodyClass += "body-site";
else
CMS.DocumentEngine.DocumentContext.CurrentBodyClass += "body-global";
}
</script>
I want my user to be able to hit the submit button and have a string write into a css file. When I hit the submit button, I am getting the error message:
Access to the path 'C:/.....' is denied
This happens when running the site from localhost and on my hosting (123reg)
protected void btnSubmit(object sender, EventArgs e)
{
using (StreamWriter writer = new StreamWriter("B00101168.css"))
{
writer.Write("Word ");
writer.WriteLine("word 2");
}
}
The first problem is, you can't write to a file without setting permissions on the folder. See this link for details. Essentially, you must give the Internet Guest Account permission to write to the folder.
But, the bigger problem is, you probably shouldn't be trying to dynamically write a CSS file anyway. At least, not the way you are trying to do it. Can you explain why you are trying to dynamically change a CSS file on your server? If you can explain what you are trying to accomplish, I might have some suggestions on how to do it that work better than what you are trying to do.
UPDATE: You're using WebForms, and you're trying to dynamically generate CSS. So here's one way to do that.
Use a generic page handler -- a file that ends in .ashx. You dynamically create the CSS however you're doing it now, but instead of writing it to a file, you output it directly to the browser. Here is some (untested!) code:
In the DynamicStyles.ashx file, there is basically nothing to add from what it automatically generates.
In the DynamicStyles.ashx.cs file:
public void ProcessRequest( HttpContext context )
{
StringBuilder css = new StringBuilder();
// Use the StringBuilder to generate the CSS
// however you are currently doing it.
context.Response.ContentType = "text/css";
context.Response.Write( css.ToString() );
}
Then, in your code that needs the CSS file, include it just like you would any other CSS:
<link rel="stylesheet" type="text/css" href="/path/to/DynamicStyles.ashx">
Is there an accepted way to "move" a control.
My client wants to place a certain chunk of markup (representing some visual element) in one of several different places on the page. The locations are different to the point that I can't effect the change on CSS along (by floating it or something).
I considered just putting the control in multiple spots with Visible set to "false," then displaying the one in the place they wanted for that particular page.
However, the code for this control is not trivial -- there's a couple template sections, for instance. Having to dupe this in multiple places would get unwieldy. Also, I don't want to have to work with this control strictly from the code-behind for the same reason.
So, I'd like to put it in one place on the Web form, the move it around based on where I want it. Could I put Placeholders in different spots, have the control in one spot, then remove and add it to the right spot? I suspect this would work.
Does someone have a better idea? Is there a best practice for this?
I'd recommend using a placeholder control, moving your markup into a separate user control, then loading this at runtime and adding it to the relevant placeholder.
Eg.
// Load a user control
MyControl userCtrl = (MyControl) LoadControl("~/Controls/MyControl.ascx");
// Or create an instance of your control
SubclassedControl subclassedCtrl = new SubclassedControl();
// Do stuff with controls here
userCtrl.LoadData();
subclassedCtrl.Text = "Hello World";
// Check which placeholder to add controls to
PlaceHolder placeHolder = (foo=="bar") ? placeHolder1 : placeHolder2;
// Add the controls
placeHolder.Controls.Add(userCtrl);
placeHolder.Controls.Add(subclassedCtrl);
This will avoid cluttering up your page with unnecessary markup, and loading it at runtime will also avoid unnecessary confusion later, when another developer looks at the code and can't immediately see why a control is in one place in the markup, but renders on a completely different part of the page.
An alternative (and one I've seen done many times before) is through javascript and the DOM. Render your control inside a hidden div tag. So you would render your content here:
<div id='rendercontent' style='display:none'>
.. control here ..
</div>
Then, lets say you wanted to move it all here (the span tag is inside because that's what we're going to replace):
<div id='newlocation1'><span></span></div>
You would define the following javascript:
<script language="JavaScript">
function replaceNode(newElementID, targetElementID)
{
var targetElement=document.getElementById(targetElementID);
var newElement=document.getElementById(newElementID);
targetElement.replaceChild(newElement, targetElement.firstChild);
}
</script>
And when you want to move the content to the new location, call:
<script language="JavaScript">
replaceNode('rendercontent','newlocation1');
</script>
Do Web Parts do what you want to do?
Or, you can change the parent programmatically of your controls to move them into a separate area.
You can override the Render method and place the controls wherever you want in the html.
You only need to add controls to the Controls collection that must interact on the server. The rest of your HTML can just be written to the response stream. If you override Render you can create the html anyway you see fit, placing the controls in any order.
Below is an example of how to write out your html.
protected override void Render(HtmlTextWriter writer)
{
AddAttributesToRender(writer);
writer.RenderBeginTag(TagKey);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
_control.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();
}
You could always put panels in the pre-defined locations and add the control to the specific panel at runtime.. Here's an example adding a label (the label could be replaced with any control).
Dim lblDisplay As Label = New Label()
lblDisplay.ID = "myLabel"
lblDisplay.Text = "Some Text"
pnlDisplay.Controls.Add(lblDisplay)
As far as...
"Also, I don't want to have to work
with this control strictly from the
code-behind for the same reason."
I think you're going to have to do most of your work in the code behind.
PS.. a good example of the whole usercontrol setup can be downloaded here..
http://www.asp.net/downloads/starter-kits/time-tracker/
I have an ASP.NET server control which relies on JQuery for certain functionality. I've tried to add as a webresource.
My problem is my method of including the jquery file adds it to the body, or the form to be exact:
this.Page.ClientScript.RegisterClientScriptInclude(...)
The alternative to this is to add it as a literal in the head tag:
LiteralControl include = new LiteralControl(jslink);
this.Page.Header.Controls.Add(include);
The problem with this however is any existing code srcs in the head which use JQuery fail, as JQuery is loaded afterwards (ASP.NET adds the literal at the bottom of the control tree).
Is there a practical way of making JQuery an embedded resource, but loaded in the head first? Or should I give up now.
If you want to package up jQuery and embed it inside your own server control you should serve it to the client using the ScriptManager. From the top of my head you have to:
add jQuery.js to your project
under its "Build Action" Property,
make it an Embedded Resource
in the AssemblyInfo.cs for your
control add
[assembly: WebResource("<Your Server Control namespace>.jQuery.js", "application/x-javascript")]
Make your control inherit from
System.Web.UI.ScriptControl (or at
least implement IScriptControl)
Override GetScriptReferences:
protected override IEnumerable<ScriptReference>
GetScriptReferences()
{
return new ScriptReference[] {
new ScriptReference("<Your Server Control namespace>.jQuery.js", this.GetType().Assembly.FullName),
};
}
All of your own client script should be setup inside:
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
Which will then ensure the correct order of dependencies (ie jQuery will be available to your own client script).
Update:
A far easier way of doing it is to simply add the script tag dynamically, in your script and point to the google code hosting. e.g.
function include_dom(script_filename) {
var html_doc = document.getElementsByTagName('head').item(0);
var js = document.createElement('script');
js.setAttribute('language', 'javascript');
js.setAttribute('type', 'text/javascript');
js.setAttribute('src', script_filename);
html_doc.appendChild(js);
return false;
}
include_dom("http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js");
The function is taken from this article
Crecentfresh pushed me in the right direction, I also found
http://en.csharp-online.net/Creating_Custom_ASP.NET_AJAX_Client_Controls—IScriptControl.GetScriptReferences_Method.
My problem still remains though, the ScriptManager adds the references after the script in the head but I think this is an issue that can't be resolved. I've opted to answer myself but also upvoted crescentfresh.