Friday, February 27, 2009
DotNetNuke, Roles and Invalid value for 'encrypted ticket parameter'
The reason? I had too many friends :)
Basically the problem was that DNN was inserting a very long string into a cookie....
To work around this I'll need to either change my code to not use the DNN roles structure or insert an HTTPModule to tweak their Roles behaviour.
Here's the DNN thread: http://www.dotnetnuke.com/Community/Forums/tabid/795/forumid/111/threadid/234880/scope/posts/threadpage/7/Default.aspx
Tuesday, February 24, 2009
I was sure I'd already blogged this - colors (colours) darker and lighter
Integrating Twitter
On the send side, I used the tweetsharp project to send tweets - if you're a www.runsaturday.com user then you'll have seen this - if not, then you can't see it! - http://code.google.com/p/tweetsharp/
On the browse side, I used twitter.js to show people's tweets on their runsaturday profile - see http://www.runsaturday.com/Profile/TabId/82/UserId/3/Default.aspx for an example - http://remysharp.com/2007/05/18/add-twitter-to-your-blog-step-by-step/
Both libraries come recommended
Sunday, February 22, 2009
Starting to think about internationalisation
Yes, I had to lookup were that was! (It is on Google Maps but not on all of them - e.g. not on Google static maps!)
Anyways, it's got me thinking about internationlisation (i18n) for runsaturday. I think it should be possible to do this... I might have a go soon...
Need to go back over my code first and find out which modules have strings naughtily hardcoded... but then I think it should be possible to translate the existing site quite quickly - maybe in less than a week....
It must be done! But then so must a lot of other things....
Stuart
Saturday, February 21, 2009
Preprocessor directives must appear as the first non-whitespace character on a line
"Preprocessor directives must appear as the first non-whitespace character on a line"
What this actually meant was I'd tried to use two DataBinding expressions inside the same property -
<asp:Label ID="UserName" runat="server" Text='<%# Eval("UserName") %><%# Eval("TeamMemberNameText") %>'></asp:Label>
To solve it I just pulled it down to:
<asp:Label ID="UserName" runat="server" Text='<%# Eval("UserAndTeamMemberNameText") %>'></asp:Label>
Wednesday, February 18, 2009
Replacing the far too secure random password generation in DotNetNuke
It looks like ASH783934_w3w-r - i.e. not very easy to remember.
I wanted to replace it - so used a class like....
public class SlodgeDNNMembershipProvider : DotNetNuke.Security.Membership.AspNetMembershipProvider
{
static List<string> PasswordBase = new List<string>()
{
"fishfinger","rabbit",
... lots of other simple keywords,
"racing"
};
public override string GeneratePassword(int length)
{
// length ignored - hope this does not hurt the SQL layer!
return GeneratePassword();
}
public override string GeneratePassword()
{
Random r = new Random();
int index = r.Next(PasswordBase.Count);
if (index >= PasswordBase.Count) // according to the intellisense help this should not happen
index = PasswordBase.Count - 1;
int number = r.Next(100);
return string.Format("{0}{1:00}", PasswordBase[index], number);
}
}
And then inserted this into the web.config layer using:
<members defaultProvider="AspNetMembershipProvider">
<providers>
<clear/>
<add name="AspNetMembershipProvider" type="SlodgeDNNMembershipProvider.SlodgeDNNMembershipProvider, SlodgeDNNMembershipProvider" providerPath="~\Providers\MembershipProviders\AspNetMembershipProvider\"/>
</providers>
</members>
Seemed to work first time - which is always suspicious!
Monday, February 16, 2009
Notes on YetAnotherForum.net speed
However... I have also had some problems - which I think I've started to solve...
Here are my notes - as submitted to http://forum.yetanotherforum.net/yaf_postsm31803_Problems-with-slow-forum-not-on-godaddy--and-my-solution.aspx#post31803
I've been using this on my DotNetNuke site - http://www.runsaturday.com - and I've had some problems with slowness - similar I think to some of the GoDaddy problems much discussed here.
My speed problems (I think) were:
- that my mail server is occasionally slow to react - so the sending of emails is often slow... and this seems to be done on the forum main thread.
- that the user images returned from the forum are not sent with caching information - so they are requested far too often by clients.
To work around these problems:
1. I've added this code to the mail sender in forumpage.cs - I've not fully tested it works yet - but the speed improvement seems OK:
static DateTime MailLastSent = DateTime.Now;
class MailSendLock
{
private int i = 0;
}
static MailSendLock MailSendLockObject = new MailSendLock();
private void TriggerMailIfNecessary()
{
if ((DateTime.Now - MailLastSent).TotalSeconds > NumSecondsBetweenMailSendAttempts)
{
lock (MailSendLockObject)
{
if ((DateTime.Now - MailLastSent).TotalSeconds > NumSecondsBetweenMailSendAttempts)
{
MailLastSent = DateTime.Now;
System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(SendMailThread));
}
}
}
}
2. I've added this code to the resource.ashx.cs file:
{
context.Response.Cache.SetExpires(DateTime.Now.AddDays(7.0));
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetValidUntilExpires(false);
}
public void ProcessRequest( HttpContext context )
{
if ( context.Request.QueryString ["r"] != null )
{
// resource request
GetResource( context );
}
else if ( context.Session ["lastvisit"] != null )
{
if ( context.Request.QueryString ["u"] != null )
{
GetResponseLocalAvatar( context );
AddCaching(context);
}
else if ( context.Request.QueryString ["url"] != null && context.Request.QueryString ["width"] != null && context.Request.QueryString ["height"] != null )
{
GetResponseRemoteAvatar( context );
AddCaching(context);
}
else if ( context.Request.QueryString ["a"] != null )
{
GetResponseAttachment( context );
AddCaching(context);
}
}
else
{
// they don't have a session...
context.Response.Write( "Please do not link directly to this resource. You must have a session in the forum." );
}
}
I've been incredibly busy
I've been really very busy indeed writing Javascript of all things.
Lots of Google Maps code - for the output take a look at the new Course Mapper on RunSaturday - see:
http://www.runsaturday.com/Maps/tabid/98/Default.aspx
and:
http://www.runsaturday.com/Maps/tabid/99/course/1/Default.aspx
And you can also see it used in action in this iframe example: http://lodge.stuart.googlepages.com/examplepage
Monday, February 09, 2009
Reflection on my own base classes - DNN 4 vs DNN 5 pain
However, someone has really upset me today by completely changing the base class functionality of AuthenticationConfigBase - which means that it's really hard to make an authentication module which will work in both DNN4 and in DNN5. Basically you get haunted by MissingMethodException's surrounding GetPortalSetting and PortalController.GetPortalSettingsDictionary.
To get around this, I had to reflect on the Config's own base class to find out what to use...
Here's the code.... I'm sure it could be more efficient - but it doesn't matter too much for where it is (it's not loaded very often)
Load settings:
// Methods
protected Config(int portalId)
: base(portalId)
{
this._ApplicationKey = Null.NullString;
this._Enabled = Null.NullBoolean;
this._IncludeHelp = Null.NullBoolean;
this._SecretKey = Null.NullString;
this._LocationXDReceiver = Null.NullString;
bool tryDnn4Methods = false;
try
{
Dnn5LoadSettings(portalId);
}
catch (Exception /*e*/)
{
tryDnn4Methods = true;
}
if (!tryDnn4Methods)
return;
try
{
Dnn4LoadSettings();
}
catch (Exception e)
{
throw new ApplicationException("Cannot initialise Facebook Connect config", e);
}
}
private void Dnn4LoadSettings()
{
if (!bool.TryParse(HackGetValueForDnn4("FacebookConnect_Enabled"), out this._Enabled))
this._Enabled = false;
this._ApplicationKey = HackGetValueForDnn4("FacebookConnect_ApplicationKey");
this._SecretKey = HackGetValueForDnn4("FacebookConnect_SecretKey");
if (!bool.TryParse(HackGetValueForDnn4("FacebookConnect_IncludeHelp"), out this._IncludeHelp))
this._IncludeHelp = false;
this._LocationXDReceiver = HackGetValueForDnn4("FacebookConnect_LocationXDReceiver");
}
private void Dnn5LoadSettings(int portalId)
{
this._Enabled = PortalController.GetPortalSettingAsBoolean("FacebookConnect_Enabled", portalId, Null.NullBoolean);
this._ApplicationKey = PortalController.GetPortalSetting("FacebookConnect_ApplicationKey", portalId, Null.NullString);
this._SecretKey = PortalController.GetPortalSetting("FacebookConnect_SecretKey", portalId, Null.NullString);
this._IncludeHelp = PortalController.GetPortalSettingAsBoolean("FacebookConnect_IncludeHelp", portalId, Null.NullBoolean);
this._LocationXDReceiver = PortalController.GetPortalSetting("FacebookConnect_LocationXDReceiver", portalId, Null.NullString);
}
private string HackGetValueForDnn4(string valueToGet)
{
Type type = this.GetType();
System.Reflection.PropertyInfo info = type.GetProperty("ModuleSettings", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.Instance);
if (info == null)
throw new ApplicationException("Cannot initialise Facebook Connect - no ModuleSettings found");
System.Collections.Hashtable hashTable = (System.Collections.Hashtable)info.GetValue(this, null);
string value = (string)hashTable[valueToGet];
if (value == null)
value = string.Empty;
return value;
}
internal void HackSetValueForDnn4(string valueToSet, string value)
{
ModuleController controller = new ModuleController();
Type type = this.GetType();
System.Reflection.PropertyInfo info = type.GetProperty("AuthenticationModuleID", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.Instance);
if (info == null)
throw new ApplicationException("Cannot initialise Facebook Connect - no ModuleSettings found");
int authenticationModuleID = (int)info.GetValue(this, null);
controller.UpdateModuleSetting(authenticationModuleID, valueToSet, value);
}
public static void UpdateConfig(Config config)
{
try
{
Dnn5UpdateSettings(config);
}
catch (Exception /*e*/)
{
}
try
{
Dnn4UpdateSettings(config);
}
catch (Exception e)
{
// ignored
//throw new ApplicationException("Cannot save Facebook Connect config", e);
}
}
private static void Dnn4UpdateSettings(Config config)
{
config.HackSetValueForDnn4("FacebookConnect_Enabled", config.Enabled.ToString());
config.HackSetValueForDnn4("FacebookConnect_ApplicationKey", config.ApplicationKey);
config.HackSetValueForDnn4("FacebookConnect_SecretKey", config.SecretKey);
config.HackSetValueForDnn4("FacebookConnect_IncludeHelp", config.IncludeHelp.ToString());
config.HackSetValueForDnn4("FacebookConnect_LocationXDReceiver", config.LocationXDReceiver);
ClearConfig(config.PortalID);
}
private static void Dnn5UpdateSettings(Config config)
{
PortalController.UpdatePortalSetting(config.PortalID, "FacebookConnect_Enabled", config.Enabled.ToString());
PortalController.UpdatePortalSetting(config.PortalID, "FacebookConnect_ApplicationKey", config._ApplicationKey.ToString());
PortalController.UpdatePortalSetting(config.PortalID, "FacebookConnect_SecretKey", config.SecretKey.ToString());
PortalController.UpdatePortalSetting(config.PortalID, "FacebookConnect_IncludeHelp", config.IncludeHelp.ToString());
PortalController.UpdatePortalSetting(config.PortalID, "FacebookConnect_LocationXDReceiver", config._LocationXDReceiver.ToString());
ClearConfig(config.PortalID);
}
Sunday, February 08, 2009
Facebook Connect for DotNetNuke finally documented...
I've finally documented the facebook connect auth system I produced for dotnetnuke.
The doc is up on http://www.cirrious.com/Research/FacebookConnect/tabid/59/Default.aspx - pdf format to follow soon
I'll be releasing the software for a nominal fee through snowcovered and through DNN showcase in the very near future!
Got to get some sleep now!
Stuart
Thursday, February 05, 2009
Back on dnn user names again
update dnn_users set username='JulesR', displayname='JulesR' where username='JROSE02'
update aspnet_Users set username='JulesR' ,loweredusername='julesr' where username='JROSE02'
update yaf_user set name = 'JulesR' where name='JROSE02'
My previous post on this had a typo!
At least I hope the change worked....
Wednesday, February 04, 2009
White Paper Draft - Azure development
Monday, February 02, 2009
Another twist on stacka
I got inspired to spin out another version of stacka.
This time http://www.snowdotnet.com - a place to show your snowmen off :)
Not going to plug it too hard - I think I might have friend new website apathy by now....
However, one interesting techie point to make:
- because I've run out of webspace, this app is not running purely on Azure
- instead the storage is 100% azure
- but the web app is running on my GoGrid server
- and the async worker role has been abandoned - instead I'm doing the picture processing synchronously.
Anyone want to post any pictures, please do :)
Experiments with Azure - changing the structure of live data
On with the post....
The problems...
I wanted to update my http://www.stacka.com and http://www.clouddotnet.com Azure apps - especially adding recent comments and recent ratings to the front page.
And so the problems started.... it was very revealing about my data structure....
- I had the PartitionKey as StackId
- I had the RowKey as a reverse time index (a bit like Steve Marx's blog examples - see http://blog.smarx.com)
Problem 1: Now because there is no "order by" allowed in the Linq for Azure table storage - so the order returned is the partition key, row key order... this presented a challenge in how to get all my data back in the right order
Secondly, I had not stored simple data like Stack title (site title for clouddotnet) in the rating and comment entries
Problem 2: Because there is no "join" allowed in the Linq for Azure table storage (and nor is Contains allowed) then listing comments/ratings alongside their stack/site names was going to be really slow.
Thirdly, I wanted to present a random set of Stacks (or sites) - rather than just the latest
Problem 3: How do you pick a random set when there's no order by, no count, etc available?
To solve these problems....
I had to go back to my data schema - which in Azure, of course, is just the public properties of my data classes.
- I've bodged it....
- While my data size is so small, I'm actually pulling back all the comments and ratings into app memory and sorting them there. To help prevent some slowness I'm using the HttpContext.Current.Cache cacheing with a one minute absolute time expiration)
- What I need to do in the longer term is either to change the PartitionKey and RowKey so I can get results returned by time while still being able to search quickly by stack - I think this will be simple enough to do - but I think it will require a change in table name.
- (An alternative in the long term would be to add another table to act as an index - but I think in this case that is not needed)
- I had to add on the public property StackTitle to the CommentRow class - and similarly for the RatingRow class.
- I then had to write some code to update the existing data to include these StackTitle properties - I did this in a simple Windows Form app that I ran on my local PC - it used the same class libraries as the real ASP.Net app - and upgraded the data live without any users noticing - very easy.
- Then I changed the ASP.Net code to use the new structures and deployed the new code :)
- I've bodged it....
- While my data size is so small, I'm actually pulling back all the stacks and selecting a random set there
- What I need to do in the longer term is to create separate lists of random items - I think I would do this in a worker role - maybe creating a new random list once every minute - and storing up to 10 random list in the azure table storage at any one time? This would be simple enough to do - and not too bad on processing or redundancy.
- Azure storage is a bit of a mindf*&k - you have to stop thinking relational - you definitely have to stop thinking normalised.
- Do not read this and think - "this is horribly inefficient"!
- Azure storage (like Amazon's SimpleDB and like Google's BigTable) is very very efficient
- It's just that you have to keep thinking about efficiency in terms of speed of retrieving data for the user - not in terms of number of bytes stored or in terms of duplicated data.
- It's a classic MIPs versus Memory trade off - and in the world of the cloud, then MIPs and Memory are both cheap, but when it comes to responding to the user then you cut MIPs at the expense of Memory.
- Changing the schema was really simple
- Working out what is actually now stored in the data store is a bit tricky - at one point I added some rows to my live data store with an extra column... those cells will live on for all time now - but I can't work out right now how to actually work out which rows have those cells.
- The use of a desktop tool to upgrade the data was beautifully simple (and it used https - so it was secure)
- While the bodges might:
- They're a good programming approach - as long as I know they work and as long as I know that they can be changed, then it's actually a solution that is scalable in development effort terms?
- They're a p1ss poor programming approach... there is definitely an argument that it's better to do the fix right first time...
You can see the results on:
- Before the upgrade the page looked like - http://www.stacka.com
- After the upgrade the front page looked like - http://www.clouddotnet.com
Sunday, February 01, 2009
Can I have some spam with my cloud?
e.g. this is the first spam entry already on clouddotnet:
http://www.clouddotnet.com/cloud/samujjal/samujjal
Kind of funny I suppose...
Saturday, January 31, 2009
A HUGE recommendation for parsing CSV formats (and other similar formats too)
http://www.codeproject.com/KB/database/CsvReader.aspx
Use it and love it.
Fast, well engineered and very powerful - downloaded it yesterday and had all sorts of stuff running 10 minutes later - then it helped me work out and workaround errors in the incoming files - just a really good bit of software for a really essential task.
(and while I'm recommending... Paint.Net looks like a very handy bit of kit too- http://www.getpaint.net/ !)
Friday, January 30, 2009
What a bunch of miserable take take take bastards all my blog readers are...
Oops!
Thursday, January 29, 2009
Some notes following yesterday's London .Net user group
Andrew Myhre
Digital Wall - Online urban decent, graphite in the 22nd century - a bit of a gimmick at the moment - but one that in a creative space could turn heads... and I would quite like my moblie phone to show me a digital wall... maybe (like twitter) this might yet have a future one day...
Scott Cowan
So there I was thinking I was good knowing about NUnit and having the intention of using NMock and RhinoMocks (and maybe even Castle's Inversion of Control) one day soon. And then along comes Behaviour Driven Testing... clearly yet more good intentions for me to add to my list - excellent blog name too - http://sleepoverrated.com/
Christian Blunden vs Ben Hall
Battle of the cruise controls.... as Harry Hill would say - fight!
I've got admit we used to use Cruise Control at my last job - and that it broke in the first month and we couldn't be bothered to fix it... oops. However, both these UIs looked much simpler to use... next time (more good intentions)
BlogUrlMissing vs http://blog.benhall.me.uk/
Abid Quereshi
An interesting overview of some lessons learned changing a company's culture... Interesting to hear about the pain... it's not something I've seen before - especially having never worked for a big company, having always had plenty of cheek (rather than courage) and having always got on with building stuff (new term learnt tonight - "lean" development)
BlogUrlMissing
Justin Davies
Unit testing with classic ASP. Really really really puts me to shame - a nice compact framework built around ASP (which he quite correctly described as "shit") to allow NUnit tests to be run - nice to see code rather than ppoints (again puts me to shame)
Blog - BeingNew
Ben Nunney
PowerShell - very amusing stories about what makes Ben get up every day. A bit of me dislikes all this empowering of sys admins with programming skills... but most of me knows that it means it frees me to go on and work on much more fun stuff.
twitter: @bennuk
BlogUrlMissing
Sebastien Lambla
Fluent-nhibernate - what can I say - my first sighting of Windows 7 in the wild - and it was running on a Mac Air... chapeau!
BlogUrlMissing
Me....
Next time less slides (no slides) and maybe a demo instead - but wifi is a necessity not a luxury :)
This is my blog!
Did I miss anyone?
Visual Studio's strangest error message yet....
I get a message box that says:
So what does this mean? Well, googling for the error reveals all sorts of hits - e.g. this thread is quite interesting http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/6adca20b-649f-41a4-8fa1-09534882d76c/ - but I haven't found a solution (yet)
Ho hum!
Update - I think this was something to do with the fact that one of my projects was a 2.0 target - while everything else was 3.5 - but really I'm still in the dark.
Wednesday, January 28, 2009
My presentation to the London Dot Net Users Group this evening...
I suspect I won't fit much of it in my 5 minute slot (I do like to talk...)
If you did come and you want me to do another talk ever again... then please login to stacka and upload a stack...
Alternatively if you came and you never want me to talk again... then please login to stacka and upload 2 stacks...
If you know anyone who likes to run/cycle/ski/walk with a GPS please do point them at RunSaturday - it's still growing :)
Here's the presentation embedded:
One other note - I've got lots of fun little development projects going on at the moment - all .NET 3.5, all C# - if anyone wants to get involved, then let me know and I'll try to work out how to parcel up some work. Not open source as such, but community collab on a small scale.