Dynamically generate CSS file from database in ASP.NET MVC - css

I'm looking to add some basic theme support to my web application, with users being able to customise various parts of the look. These include things like colours, text sizes, fonts, other basic things. I will be storing these in the database and loading them each time a page is accessed.
My question is, how do I go about generating a dynamic CSS file based upon these database values?
I would prefer to do something that is cache-able, but extensible so that if I want to add more editable styles then it wouldn't be a bit issue.

I think the simplest way would be to add something like the following action method to a controller:
public class CssController : Controller {
public ActionResult GetCss() {
StringBuilder sb = new StringBuilder();
Dictionary<string, string> cssValues = new Dictionary<string, string>();
// populate dictionary with values from your database
sb.AppendLine(".myDivClass {");
foreach (var entry in cssValues) {
sb.AppendLine(entry.Key + ": " + entry.Value);
}
sb.AppendLine("}");
return Content(sb.ToString(), "text/css");
}
}
Now in your page you can reference it like so:
<link href="<%: Url.RouteUrl(new { controller= "CssController", action = "GetCss" }) %>" rel="stylesheet" type="text/css" />
OP EDIT: I made some small changes to the method, but the general premise remains. This is the version I used:
public class CssController : Controller
{
public ContentResult GetTheme()
{
var builder = new StringBuilder();
IDictionary<string, IDictionary<string, string>> css = new Dictionary<string, IDictionary<string, string>>();
/* Populate css object from the database */
foreach (var selector in css)
{
builder.Append(selector.Key);
builder.Append(" { ");
foreach (var entry in selector.Value)
{
builder.Append(string.Format("{0}: {1}; ", entry.Key, entry.Value));
}
builder.AppendLine("}");
}
return Content(builder.ToString(), "text/css");
}
}

Related

How to use parameters in Twitter Bootstrap loaded dynamically from the database

I have a website where I need multiple themes.
So www.mysite.com/Client1/ uses red buttons and www.mysite.com/Client2/ uses blue buttons.
The number of clients are dynamic stores in a DB, and the colors are also stored in the DB. Can be changed at anytime by the client.
Currently I am using Twitter Bootstrap LESS files and ASP MVC Optimization (bundle).
My App_Start BundleConfig looks like this:
var cssTransformer = new CssTransformer();
var stylesBundle = new StyleBundle("~/Content/bootstrap");
.Include("~/Content/less/bootstrap.less")
stylesBundle.Transforms.Add(cssTransformer);
bundles.Add(stylesBundle);
In variables.less
#btnPrimaryBackground: #linkColor;
The color of #btnPrimaryBackground should change when different urls are called.
How do I change the less variable to use a parameter from my another source (database or other)?
Since Web Optimization does not play nice with dynamic dontent, I decided to not use it.
Instead I have made am ASP MVC ActionResult for LESS, and reference that.
<link rel="stylesheet" type="text/css" href="#Url.Action("Styles", "Theme")">
My ASP MVC Controller looks like this:
public class ThemeController : Controller
{
public ActionResult Styles()
{
var parameters = new Dictionary<string, string>
{
{"themeColor1", "Get theme color 1 here"},
{"themeColor2", "Get theme color 2 here"}
};
var themeLessFilePath = Server.MapPath("~/Content/less/theme.less");
using (var stream = System.IO.File.OpenRead(themeLessFilePath))
{
return new DotLessResult(stream, parameters, true);
}
}
}
And the LESS ActionResult like this:
public class DotLessResult : ActionResult
{
public IDictionary<string, string> Parameters { get; set; }
public string Less { get; set; }
public bool Minify { get; set; }
public DotLessResult(string less, IDictionary<string, string> parameters = null, bool minify = false)
{
Less = less;
Parameters = parameters ?? new Dictionary<string, string>();
Minify = minify;
}
public DotLessResult(Stream stream, IDictionary<string, string> parameters = null, bool minify = false)
: this(new StreamReader(stream).ReadToEnd(), parameters, minify) { }
public override void ExecuteResult(ControllerContext context)
{
var output = Less;
//TODO: Not the way to do this!
foreach (var key in Parameters.Keys)
{
output = Regex.Replace(output, #"#" + key + #":\s*\S+;", "#" + key + ":" + Parameters[key] + ";");
}
var lessEngine = dotless.Core.LessWeb.GetEngine(new DotlessConfiguration { MinifyOutput = Minify, MapPathsToWeb = true, Web = true, CacheEnabled = false});
var css = lessEngine.TransformToCss(output, (string)null);
context.HttpContext.Response.ContentType = "text/css";
using (var writer = new StreamWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8))
{
writer.Write(css);
writer.Flush();
}
}
}
Its NOT the best solution, but it works on my machine TM.
Dont forget to implement some kind of output caching as it will most like be hit alot, and not change very often.

how to print ASP.NET MVC2 view from server

I'm looking for a way to print ASP.NET/ Mono MVC2 view from ASP.NET application running in Windows 2003 server.
I tried code below based on Programmatically "hello world" default SERVER-side printer in ASP.NET MVC
but this outputs raw html string. How to print view as formatted text using free software?
Order layout is created as html partial view. If there is other free way to print out formatted order, I can create layout in other form instead of html.
Only free solution which I have found requires to use Windows Forms WebBrowser control but this looks not reasonable in MVC2 application which is running under Mono also.
I looked into Rotativa ( http://nuget.org/packages/Rotativa/ ) but it looks like it doesnt allow to print html.
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Web.Mvc;
public class PrintController : Controller
{
string body;
public ActionResult Complete()
{
body = RenderViewToString<TestOrder>("~/Views/Checkout/Order.ascx", new TestOrder() { Number = "1" });
PrintOrder();
return View("PaymentComplete");
}
void PrintOrder()
{
// https://stackoverflow.com/questions/12229823/programmatically-hello-world-default-server-side-printer-in-asp-net-mvc
var doc = new PrintDocument();
doc.PrinterSettings.PrinterName = "HP Laserjet 1200";
doc.PrintPage += new PrintPageEventHandler(ProvideContent);
doc.Print();
}
void ProvideContent(object sender, PrintPageEventArgs e)
{
e.Graphics.DrawString(body,
new Font("Arial", 12),
Brushes.Black,
e.MarginBounds.Left,
e.MarginBounds.Top);
}
string RenderViewToString<T>(string viewPath, T model)
{ // https://stackoverflow.com/questions/483091/render-a-view-as-a-string
ViewData.Model = model;
using (var writer = new StringWriter())
{
var view = new WebFormView(viewPath);
var vdd = new ViewDataDictionary<T>(model);
var viewCxt = new ViewContext(ControllerContext, view, vdd, new TempDataDictionary(), writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
}
public class TestOrder
{
public string Number;
}
There is an article about convert HTML to PDF using iTextSharp: http://www.dotnetspider.com/resources/43589-How-convert-HTML-PDF-ASP-NET.aspx

send data from silverlight to asp.net (or php?) when the user click on a button

If I click on a button of my Silverlight app, another third-party web app must gets some data.
My experience, until now, has been to create web service functions that you can call when you need, but in this case I have to give the possibility to the customer to "handle the click event on the button". In the actual case the third-party app is ASP.Net, but, if it were possible, I would like to do something portable.
Before to start with some crazy idea that will comes in my mind, I would ask: How would you do that?
Pileggi
I Use This Class To Create And Post a Form Dynamically
public class PassData
{
public static PassData Default = new PassData();
public void Send(string strUrl, Dictionary<string, object> Parameters, string ContainerClientID = "divContainer")
{
var obj = HtmlPage.Document.GetElementById(ContainerClientID);
if (obj != null)
{
HtmlElement divContainer = obj as HtmlElement;
ClearContent((HtmlElement)divContainer);
HtmlElement form = HtmlPage.Document.CreateElement("form");
form.SetAttribute("id", "frmPostData");
form.SetAttribute("name", "frmPostData");
form.SetAttribute("target", "_blank");
form.SetAttribute("method", "POST");
form.SetAttribute("action", strUrl);
if (Parameters != null)
foreach (KeyValuePair<string, object> item in Parameters)
{
HtmlElement hidElement = HtmlPage.Document.CreateElement("input");
hidElement.SetAttribute("name", item.Key);
hidElement.SetAttribute("value", item.Value.ToString());
form.AppendChild(hidElement);
}
divContainer.AppendChild(form);
form.Invoke("submit");
ClearContent((HtmlElement)divContainer);
}
}
private void ClearContent(System.Windows.Browser.HtmlElement obj)
{
foreach (HtmlElement item in obj.Children)
{
obj.RemoveChild(item);
}
}
}
divContainer is id of a div in html

SyndicationFeed: Content as CDATA?

I'm using .NET's SyndicationFeed to create RSS and ATOM feeds. Unfortunately, I need HTML content in the description element (the Content property of the SyndicationItem) and the formatter automatically encodes the HTML, but I'd rather have the entire description element wrapped in CDATA without encoding the HTML.
My (simple) code:
var feed = new SyndicationFeed("Title", "Description",
new Uri("http://someuri.com"));
var items = new List<SyndicationItem>();
var item = new SyndicationItem("Item Title", (string)null,
new Uri("http://someitemuri.com"));
item.Content = SyndicationContent.CreateHtmlContent("<b>Item Content</b>");
items.Add(item);
feed.Items = items;
Anybody an idea how I can do this using SyndicationFeed? My last resort is to "manually" create the XML for the feeds, but I'd rather use the built-in SyndicationFeed.
This worked for me:
public class CDataSyndicationContent : TextSyndicationContent
{
public CDataSyndicationContent(TextSyndicationContent content)
: base(content)
{}
protected override void WriteContentsTo(System.Xml.XmlWriter writer)
{
writer.WriteCData(Text);
}
}
then you can:
new CDataSyndicationContent(new TextSyndicationContent(content, TextSyndicationContentKind.Html))
For those for whom the solution provided by cpowers and WonderGrub also didn't work, you should check out the following SO question, because for me this question was actually the answer to my occurence of this problem!
Rss20FeedFormatter Ignores TextSyndicationContent type for SyndicationItem.Summary
Judging from the positive answer from thelsdj and Andy Rose and then later the 'negative' response from TimLeung and the alternative offered by WonderGrub I would estimate that the fix offered by cpowers stopped working in some later version of ASP.NET or something.
In any case the solution in the above SO article (derived from David Whitney's code) solved the problem with unwanted HTML encoding in CDATA blocks in an RSS 2.0 feed for me. I used it in an ASP.NET 4.0 WebForms application.
This should work.
item.Content = new TextSyndicationContent("<b>Item Content</b>",TextSyndicationContentKind.Html);
I had the same problem as some where the WriteContentsTo override wasn't being called in cpowers example (still no idea why). So, I changed it to inherit from the SyndicationContent class instead. Not sure if this is the best solution, but worked great in my situation.
public class CDataSyndicationContent : SyndicationContent
{
public CDataSyndicationContent(string content)
{
Text = content;
}
public override SyndicationContent Clone()
{
return new CDataSyndicationContent(Text);
}
public override string Type
{
get { return "html"; }
}
public string Text { get; private set; }
protected override void WriteContentsTo(XmlWriter writer)
{
writer.WriteCData(Text);
}
}
It might be too late but I leave my solution. I added it as a ElementExtension then it works for me. My environment is .NET 4.5.
XNamespace nsDefault = "http://www.w3.org/2005/Atom";
var content = new XElement(nsDefault + "content");
content.Add(new XCData("<b>Item Content</b>"));
item.ElementExtensions.Add(new SyndicationElementExtension(content));
try this
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = false;
//settings.ProhibitDtd = false;
using (XmlReader reader = XmlReader.Create(rssurl, settings))
Here is what we did :
public class XmlCDataWriter : XmlTextWriter
{
public XmlCDataWriter(TextWriter w): base(w){}
public XmlCDataWriter(Stream w, Encoding encoding): base(w, encoding){}
public XmlCDataWriter(string filename, Encoding encoding): base(filename, encoding){}
public override void WriteString(string text)
{
if (text.Contains("<"))
{
base.WriteCData(text);
}
else
{
base.WriteString(text);
}
}
}
And then to use the class :
public StringBuilder CDataOverwiriteMethod(Rss20FeedFormatter formatter)
{
var buffer = new StringBuilder();
//could be streamwriter as well
using (var stream = new StringWriter(buffer))
{
using (var writer = new XmlCDataWriter(stream))
{
var settings = new XmlWriterSettings() {Indent = true};
using (var xmlWriter = XmlWriter.Create(writer, settings))
{
formatter.WriteTo(xmlWriter);
}
}
}
return buffer;
}
The shortest way to do this is:
.Content = SyndicationContent.CreateXhtmlContent("<![CDATA[The <em>content</em>]]>")
That will be outputted in the XML as
<entry>
…
<content type="xhtml"><![CDATA[The <em>content</em>]]></content>
…
</entry>
Not an elegant solution, I admit, but it works properly – just tried on a project of mine.
try
item.Content = "<![CDATA[" +
SyndicationContent.CreateHtmlContent("<b>Item Content</b>") + "]]>";

How to retrieve all key-value pairs in a resource file located in App_GlobalResources

In my ASP.NET MVC application, I manage localized texts in .resx files located in App_GlobalResources folder. I am able to retrieve any text value in any file knowing its key.
Now, I want to retrieve all key/value pairs in a particular resource file in order to write the result to some JavaScript. A search revealed that I might be able to use ResXResourceReader class and iterate through the pairs; however the class is unfortunately located in the System.Windows.Forms.dll and I don't want to wire that dependency to my web app. Is there any other way I can implement this feature?
I've found the solution. Now no need to reference Forms.dll.
public class ScriptController : BaseController
{
private static readonly ResourceSet ResourceSet =
Resources.Controllers.Script.ResourceManager.GetResourceSet(CurrentCulture, true, true);
public ActionResult GetResources()
{
var builder = new StringBuilder();
builder.Append("var LocalizedStrings = {");
foreach (DictionaryEntry entry in ResourceSet)
{
builder.AppendFormat("{0}: \"{1}\",", entry.Key, entry.Value);
}
builder.Append("};");
Response.ContentType = "application/x-javascript";
Response.ContentEncoding = Encoding.UTF8;
return Content(builder.ToString());
}
}
Okay, no other answer. Seems like referencing Forms.dll is the only way right now. Here's the code I came up with.
public class ScriptController : BaseController
{
private const string ResxPathTemplate = "~/App_GlobalResources/script{0}.resx";
public ActionResult GetResources()
{
var resxPath = Server.MapPath(string.Format(ResxPathTemplate, string.Empty));
var resxPathLocalized = Server.MapPath(string.Format(ResxPathTemplate,
"." + CurrentCulture));
var pathToUse = System.IO.File.Exists(resxPathLocalized)
? resxPathLocalized
: resxPath;
var builder = new StringBuilder();
using (var rsxr = new ResXResourceReader(pathToUse))
{
builder.Append("var resources = {");
foreach (DictionaryEntry entry in rsxr)
{
builder.AppendFormat("{0}: \"{1}\",", entry.Key, entry.Value);
}
builder.Append("};");
}
Response.ContentType = "application/x-javascript";
Response.ContentEncoding = Encoding.UTF8;
return Content(builder.ToString());
}
}

Resources