Monday, December 29, 2008
Some Facebook Connect problems
When debugging the Javascript using Firebug I could see that in one page I was getting the error - "hostWindow.FB.XdComm is undefined" inside the script xdcommreceiver.js on line 66 "func = hostWindow.FB.XdComm.Server.singleton.onReceiverLoaded;" - although this wasn't at first apparent as that line is wrapped in a "catch" statement by Facebook.
What I eventually deduced was.... somehow I'd included the facebook javascript init code twice in the same page. Sorting this out, made everything work OK again... phew!
A static Google Maps proxy in C#
This simple HTTP Handler is the core of what I wrote.
The reason I wrote it.... facebook doesn't seem to like talking to googlemaps directly.
The potential problem.... I think this falls completely within their guidelines (I am still using google maps within a web page), but I think my website could easily overreach their loading quotas - a limit of 1000 requests per user per day (and I think my website proxy will count as one user?)
I'll see how it goes....
using System;
using System.Web;
using System.Net;
using System.IO;
namespace MapControl
{
public class GMapProxy : IHttpHandler {
static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[0x1000];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
output.Write(buffer, 0, read);
}
public void ProcessRequest (HttpContext context)
{
string gmapUri = string.Format("http://maps.google.com/staticmap{0}", context.Request.Url.Query);
WebRequest request = WebRequest.Create(gmapUri);
using (WebResponse response = request.GetResponse())
{
context.Response.ContentType = response.ContentType;
Stream responseStream = response.GetResponseStream();
CopyStream(responseStream,context.Response.OutputStream);
}
}
public bool IsReusable {
get {
return false;
}
}
}
}
Sunday, December 28, 2008
Uploading large files to an ASP.NET app....
This can be achieved using the simple FileUpload control in ASP.
However, this basic control is very ugly and doesn't give much feedback.
Also, it doesn't seem to offer any zip compression support.
So I had a look around.
- Steve Sanderson had demoed a cute jquery sample at DDD7 - http://blog.codeville.net/category/jquery/ - this looked quite neat - but it's still quite fiddly - there's swf's, js's and httphandlers to write and install.
- Then there were commercial controls (mainly using ActiveX components) - e.g. http://fileup.softartisans.com/fileup-132.aspx - but this costs $199 and higher?!
- And that was pretty much all I found :(
The downside of this is that in the short term:
- I'm forcing the users to continue to suffer from the lack of UI feedback during large uploads...
- I'm forcing the users to zip files themselves.
Sorry for now, users!
What's new in DNN5
Here's the official blog announcement - http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2122/DotNetNuke-5-0-0-Released.aspx
And here's the full change release notes (way too much detail) - http://support.dotnetnuke.com/Main.aspx?ReturnUrl=%2fproject%2fChangeLog.aspx%3fPROJID%3d2&PROJID=2
So here's the highlight bullet points:
- There's new support for XHTML and CSS in skins - removing lots of the non CSS features (table dependent things) - skins should now be much easier to design - they should be less DNN specific.
- jQuery support has been added - along with a widget framework (widgets being a bit like modules only 100% javascript)
- Beneath the covers there are more unit tests and more testability - which should lead to long term improvements and stability
- IE8 web slice support has been added
- The installer has been improved
- Admin pages are now no longer quite so special - they're now just normal pages with admin rights - also "deny" has been added as well as "allow" to all the permissions grids.
Some cloud hosting options
My particular requirements are for a .NET host (so I need windows)
For cloud hosts in the UK:
- I had a look at Flexiscale - they seem to have a had a few negative customer reviews
- Then I had a look at elastichosts - they say they support Windows - but you'll need to install it yourself.
- I looked at Mosso - they seem reasonable but don't really give you much control - e.g. if I wanted the chart control installed then I'd have to submit a ticket and hope...
- And Amazon Europe are Dublin based (I think) but they are Linux not Windows at present.
I think I'm going to end up on Amazon EC2 in the US... but now I do also have an Azure invite - so I'll definitely also be giving that a spin.
Skiing was fabulous and....
And now I've returned home to discover.... that on xmas eve Microsoft sent me a present:
Thank you for your interest in Windows© Azure™.
Your invitation code is XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.
You can now sign up for a Windows Azure account at http://lx.azure.microsoft.com/fs . Please keep this email in a safe place.
This invitation to participate in the Windows Azure Community Technical Preview is subject to the following usage limits:
Total compute usage: 2000 VM hours
Cloud storage capacity: 50GB
Total storage bandwidth: 20GB/day
During the CTP, we reserve the right to suspend your account activity (this does not imply we will delete your cloud storage) if you exceed these usage limits.
Sincerely,
Azure Services Platform Team
Yes! 2009 is going to be fun :)
Friday, December 19, 2008
Going dark - going white!
But I will be back soon to launch a site or two - maybe including using EC2....
Other things coming soon.... C# 4.0 would be nice.... and maybe one day I'll get an Azure invite?
Happy Christmas all!
Quick link - to remind myself... Amazon S3 best practices
http://developer.amazonwebservices.com/connect/thread.jspa?threadID=26225
Setting up a pie chart so that each segment has the color of your choice
Here some example code...
int runListColorIndex = startingIndex;
int bikeListColorIndex = startingIndex;
int swimListColorIndex = startingIndex;
foreach (DataPoint p in series.Points)
{
ChartHatchStyle hatchStyle = ChartHatchStyle.None;
Color colorToUse = Color.ForestGreen;
switch (p.AxisLabel[0])
{
case 'R':
colorToUse = myCurrentFilterOptions.RunColor;
hatchStyle = HatchArray[runListColorIndex];
runListColorIndex++;
runListColorIndex %= HatchArray.Count;
break;
case 'B':
colorToUse = myCurrentFilterOptions.BikeColor;
hatchStyle = HatchArray[bikeListColorIndex];
bikeListColorIndex++;
bikeListColorIndex %= HatchArray.Count;
break;
case 'S':
colorToUse = myCurrentFilterOptions.SwimColor;
hatchStyle = HatchArray[swimListColorIndex];
swimListColorIndex++;
swimListColorIndex %= HatchArray.Count;
break;
}
p.Color = colorToUse;
p.BackHatchStyle = hatchStyle;
}
ThreeSharp - AWS C# Access - superb! Second attempt at posting!
using System;
using System.Collections.Generic;
using System.Text;
using Affirma.ThreeSharp.Model;
using Affirma.ThreeSharp.Query;
using System.IO;
using System.IO.Compression;
namespace Affirma.ThreeSharp.Wrapper
{
public class SlodgeThreeSharpWrapper : ThreeSharpWrapper
{
public SlodgeThreeSharpWrapper(String awsAccessKeyId, String awsSecretAccessKey)
: base(awsAccessKeyId, awsSecretAccessKey)
{
}
/// <summary>
/// Adds a string to a bucket, as an object - but compress it first!
/// </summary>
public void AddStringObjectWithCompression(String bucketName, String keyName, String data)
{
string compressedData = Compress(data);
this.AddStringObject(bucketName, keyName, compressedData);
}
/// <summary>
/// Gets a string object from a bucket, and returns it as a String
/// </summary>
public String GetStringObjectWithDecompression(String bucketName, String keyName)
{
string compressedData = GetStringObject(bucketName, keyName);
return Decompress(compressedData);
}
// The origin of the compress/decompress code in this region is unclear
// - it seems to be listed in many blogs
// Plus it looks identical to book content...
// Regardless, it doesn't look like it will be any infringement of IP to use it!
private static string Compress(string text)
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
MemoryStream ms = new MemoryStream();
using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
{
zip.Write(buffer, 0, buffer.Length);
}
ms.Position = 0;
MemoryStream outStream = new MemoryStream();
byte[] compressed = new byte[ms.Length];
ms.Read(compressed, 0, compressed.Length);
byte[] gzBuffer = new byte[compressed.Length + 4];
System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
return Convert.ToBase64String(gzBuffer);
}
private static string Decompress(string compressedText)
{
byte[] gzBuffer = Convert.FromBase64String(compressedText);
using (MemoryStream ms = new MemoryStream())
{
int msgLength = BitConverter.ToInt32(gzBuffer, 0);
ms.Write(gzBuffer, 4, gzBuffer.Length - 4);
byte[] buffer = new byte[msgLength];
ms.Position = 0;
using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
{
zip.Read(buffer, 0, buffer.Length);
}
return Encoding.UTF8.GetString(buffer);
}
}
}
}
Adding string compression to the ThreeSharp wrapper
However, these are notoriously bloated - so I thought I'd zip them first (yes, I know there are other compression formats available!)
Note - this optimisation was more for the benefit of network latency rather than for reducing required storage.
To do this I built this class (I added it to the existing namespace - you might want to use the code somewhere else?) - the two methods of importance are:
- AddStringObjectWithCompression
- GetStringObjectWithDecompression
Enjoy!
SNIP - Code moved to new post - http://slodge.blogspot.com/2008/12/threesharp-aws-c-access-superb-second.html - blogger garbled the first one (I must move to a better platform!)
ThreeSharp - AWS C# Access - superb!
All I can say is it is superb - very easy to use and looks very good all round :)
Only problem I've had so far is that the wrapper does not support European domains by default - you get a message about needing to specify the endpoint - the way around this is to add the SUBDOMAIN Format specifier to the wrapper constructor
public ThreeSharpWrapper(String awsAccessKeyId, String awsSecretAccessKey)
{
this.config = new ThreeSharpConfig();
this.config.AwsAccessKeyID = awsAccessKeyId;
this.config.AwsSecretAccessKey = awsSecretAccessKey;
// added by Stuart
this.config.Format = CallingFormat.SUBDOMAIN;
this.service = new ThreeSharpQuery(this.config);
}
Summary - a really good library - really easy to use - thanks to Affirma!
Writing text to an iframe in Javascript
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Untitled Page</title>
</head>
<body>
<script type="text/javascript">
var i = 0;
function doThing() {
i++;
var a = document.getElementById("stepsAchieved");
a.contentWindow.document.write("Here again " + i + "<br/>");
a.contentWindow.document.close();
}
</script>
<input type="button" value="hello" onclick="doThing();return false;" id="fred"/>
<hr />
<iframe src="" id="stepsAchieved" name="garminStepsAchieved" frameborder="1" height="200px" width="340px" scrolling="auto">
</iframe>
</body>
</html>
Thursday, December 18, 2008
Cloud time again?
Instead, this is a post about the fact that I'm probably going to have to resort to using some S3 storage for my current project. The reason? I've done my sums and it's needed!
According to my current estimates... it's not going to be unusual for one of my users to require 50MB of storage... so if I have 100 users... then I'll need 5GB... and if I get to 10000 users.... then I'll need 500GB. Based on this, it's definitely time to change my storage model - and (since Azure won't let me play) then that means it's now time for Amazon S3.
And the best place to start looking at this seems to be... three sharp - http://www.codeplex.com/ThreeSharp/ - looks like a really good project from http://www.affirmaconsulting.com/ - thanks guys :)
Escaping javascript strings
I used this blog post as a base: http://www.wwco.com/~wls/blog/2007/04/25/using-script-in-a-javascript-literal/
This is the C# I now have in place:
// see http://www.wwco.com/~wls/blog/2007/04/25/using-script-in-a-javascript-literal/
private static String EscapeJavascriptStringLiteralPlease(String str)
{
str = str.Replace("\\","\\\\"); // escape single backslashes
str = str.Replace("'","\\'"); // escape single quotes
str = str.Replace("\"","\\\""); // escape double quotes
str = str.Replace("<","\\<"); // escape open angle bracket
str = str.Replace(">","\\>"); // escape close angle bracket
return str;
}
But was there a way I could have done this straight from the standard ASP.Net libraries?
Into subversion
It's a bit scary how little instructions I'm reading in order to set this up (will have to go back and do it properly later?) - but I seem to be up and running now with VisualSVN as my server and with AnkhSVN as my source control plugin in Visual Studio
The only big problem I've had so far (other than not know what I'm doing) was with the database - I can't seem to get the SQL express database to have sufficient open security for IIS7.... this all came about because I moved folders...
I get this problem - http://social.msdn.microsoft.com/forums/en-US/sqlexpress/thread/6dfdcc22-7a81-4e8f-a947-c1ce6982d4b3/ - but none of the hints I've found solve it for me...
I will work this out!
String.Format - how do you escape curly braces
Basically if you want your output to be "{like INPUT_HERE this}" then you need to use:
string.Format("{{like {0} this}}", input);
i.e. you escape the curly braces by doubling them up.
CSS Dreams....
I think mainly because I've been working with some old ASP.NET and DNN controls, this has proven quite hard.
However, it's also clear that CSS itself is quite hard sometimes....
All I wanted to do was arrange my screen as
IMAGE TEXT
IMAGE TEXT
IMAGE TEXT
Where IMAGE is of an unknown height and TEXT is also multiline.
The sort of thing it would be easy to do with tables....
But it's proving very tricky in CSS. I keep getting effects like
IMAGE TEXT
IMAGE TEXT
TEXT CONTINUED
IMAGE TEXT
I've been all over the shop looking for solutions - faux columns provided some respite... but overall it's nasty! And I could have achieved the effect I wanted with tables in seconds....
Wednesday, December 17, 2008
Forcing some line breaks...
http://gojomo.blogspot.com/2005/03/cross-browser-invisible-word-break-in.html
In my ASP.NET code I was generating a long url with lots of ....&i=23&j=2343&k=232....
Internet Explorer was very strict at not line breaking this - so I need to encourage it.
Using the post above I decided on:
Response.Write(GenerateFullHRef(true).Replace("&", "&[wbr/]"));
Seemed to work well :)
Note to self - it's time to get a proper tool to post to blogger - this HTML code formatting is doing my nut in!
Azure Invitiation code still not received :(
Can't believe I ever believed I would get this within a week or two of signing up....
I wonder if I'll get it for Christmas?
Changing a chart to store the image on disk
1. change the web.config settings to include
[add key="ChartImageHandler" value="storage=file;timeout=20;dir=e:\inetpub\yourapp\temp\;deleteAfterServicing=false"/]
2. add this code to your chart initialisation
Chart1.ImageStorageMode = ImageStorageMode.UseImageLocation;
Chart1.ImageLocation = Request.ApplicationPath + "/temp/ChartPic_#SEQ(300,3)";
That seems to work for me :)
One word of warning though: This does open up a security hole - it means charts can be requested multiple times and URLs can be guessed!
Chart - allowing the image to be downloaded multiple times
[configuration]
[appsettings]
[add key="ChartImageHandler" value="storage=file;timeout=20;dir=e:\inetpub\whatever\temp\;deleteAfterServicing=false" /]
[/appsettings>
[/configuration]
Tuesday, December 16, 2008
Experimenting with facebook connect and Dotnetnuke
On the technical side, I've been using the Facebook Toolkit from Codeplex and this link on stackoverflow was very useful for working out the cookie confusion: http://stackoverflow.com/questions/323019/facebook-connect-and-aspnet
Currently, it's all gone quite smoothly :)
The only problems I've had:
- My skin didn't initially specify XHTML - so I had to add a .doctype file to my skins directory - if your skin is called AAA.ascx then you need to add AAA:
[skindoctype]
[![CDATA[ [!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"] ]] ]
[/skindoctype] - In order to add the xmlns for fb I did have to change the default.aspx code from DNN - it's not a big change, but it's still a bit sad I have to change the core :(
- I messed up the ordering of the facebook elements at one point.... for clarification, make sure that FB.init("API_KEY_HERE", "/dnn/connect/xd_receiver.htm"); is the last thing seen on your web page - not the first!
- I've been fighting with the lack of documentation - facebook seem to be relying on us dissecting their runaround example app...
- I've hit some problems with API calls resulting in "Session key expired or no longer valid" - but these are basically due to the user logging out of facebook - easy to workaround.
- Similarly when the user has logged out of facebook I've hit some problems with facebook's automatic buttons (e.g. with fb:login) - but again these are easy to workaround - you just use your own facebook links instead of the automatic buttons.
Using the AJAX Toolkit Tab Control in DNN DotNetNuke
However, when I came to use it in my DNN module I found it only displayed half the tabs...
A (not so quick) google revealed... http://vladan.strigo.net/2007/04/02/having-issues-displaying-ajax-control-toolkit-tab-control-in-dotnetnuke/
Basically the advice is to include this locally to your module:
:)
Follow up on the "Error executing child request for ChartImg.axd."
Have it reproducible.
- If I restart the webserver each time (I'm using the dev web server at present) with the chart inside the wizard then I get the error - until I move the chart outside the wizard.
- If I restart the webserver with the chart outside the wizard then it works, then when I move the chart inside the wizard again, then it still works.
(Not sure it's relevant, but in this current app my page contains a user control in which I've placed the wizard inside the last step of which I've placed another custom control inside which I've placed the chart)