I've just been (struggling) to create a new theme for SharePoint but kept getting the following error:

A theme with the name "MY-THEME-HERE 1011" and version already exists on the server

It turns out that my ThemeID (and the name of the folder) were too long - how the error message above gives any indication of that is beyond be! Anyway all you need to do is make sure the ThemeID is a maximum of 8 characters. Once I shortened it everything worked fine.

I'm new to SharePoint so ignore me if this 'common knowledge' - I thought I'd share for any other newbies out there...


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

I've been experimenting with writing my own C# URL Rewriter using a HttpModule. It's surprisingly simple as .NET kindly has the following (where newURL is the path to the actual file) :

HttpContext.RewritePath(string newURL)

So this allows a URL such as:

/my-friendly-url

to actually point to:

/pages/content.aspx?id=5

 

Problem:

All was going well, but some pages were giving the following error for no apparent reason:

Cannot use a leading '..' to exit above the top directory.

After some investigation I tracked the problem down to any asp.net HyperLink controls that had an ImageUrl property set. More specifically those starting with a '~/', if I removed the tilde the problem went away. Suffice to say I wasn't entirely happy about this 'workaround' so set about looking for an answer to why it was happening, and a better solution.

 

Cause:

After what seemed like hours of googling I came across a great article (link at the end of this post) on 4guysfromRolla.com. The problem turns out to be a bug in the HyperLink control, whereby it 'double-resolves' the ImageUrl property (basically it doesn't check if the path has already been resolved further down the stack and calls base.ResolveClientUrl(imageUrl) every time).

 

Solution:

Thankfully the solution is impressively simple through the use of a .NET ControlAdapter. If you've not come across them before, ControlAdapters let you hook into (and/or alter) the rendering process of any control, without having to the change the implementation of it in every page (I don't want to have to change all my <asp:HyperLink /> definitions throughout my solution).

So to fix the problem i'm going to create a ControlAdapter (for all browsers - typically you'd use them for Browser specific rendering differences) that will override the HyperLink's render method, crucially not calling base.ResolveClientUrl(imageUrl), instead leaving the ImageUrl value 'as-is'. (see below)

public class HyperLinkControlAdapter : ControlAdapter
    {
        protected override void Render(HtmlTextWriter writer)
        {
            HyperLink hyperlink = this.Control as HyperLink;
            if (hyperlink == null)
            {
                base.Render(writer);
                return;
            }

            // This code is copied from HyperLink.RenderContents (using
            // Reflector). References to "this" have been changed to
            // "hyperlink", and we have to render the begin and end tags.
            string imageUrl = hl.ImageUrl;
            if (imageUrl.Length > 0)
            {
                // Let the HyperLink render its begin tag
                hyperlink.RenderBeginTag(writer);

                Image image = new Image();

                // I think the next line is the bug. The URL gets
                // resolved here, but the Image.UrlResolved property
                // doesn't get set. So another attempt to resolve the
                // URL is made in Image.AddAttributesToRender. It's in
                // the callstack above that method that the exception
                // or improperly resolved URL happens.
                //image.ImageUrl = base.ResolveClientUrl(imageUrl);
                image.ImageUrl = imageUrl;

                imageUrl = hl.ToolTip;
                if (imageUrl.Length != 0)
                {
                    image.ToolTip = imageUrl;
                }

                imageUrl = hl.Text;
                if (imageUrl.Length != 0)
                {
                    image.AlternateText = imageUrl;
                }

                image.RenderControl(writer);

                // Wrap up by letting the HyperLink render its end tag
                hyperlink.RenderEndTag(writer);
            }
            else
            {
                // HyperLink.RenderContents handles a couple of other
                // cases if its ImageUrl property hasn't been set. We
                // delegate to that behavior here.
                base.Render(writer);
            }
        }
    }

Next we need to tell the web site to use the ControlAdapter, this is done by creating a .browser file in the App_Browsers folder:

<browsers>
    <browser refID="Default">
        <controlAdapters>
            <adapter controlType="System.Web.UI.WebControls.HyperLink"
                     adapterType="MyProject.HyperLinkControlAdapter" />
        </controlAdapters>
    </browser>
</browsers>

And that's it, no more bug!

Worryingly Microsoft were first told about the bug in November 2006, yet in January 2007 set it's status to 'Closed (won't fix)'. Thankfully there is a nice fix/work-around.

Click here to read the 4guysfromRolla.com article.


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

Creating a custom HttpHandler is fairly simple, all you need to do is implement the IHttpHandler interface.

public class MyHttpHandler : IHttpHandler

I recently needed to access the Session object from within my HttpHandler to check if a value existed, however the HttpContext.Session object was always null! After several minutes of pulling my hair out I discovered I simply needed to implement an additional 'marker' Interface. As I only needed Read Only access to the session object I used IReadOnlySessionState as follows:

public class MyHttpHandler : IHttpHandler,
System.Web.SessionState.IReadOnlySessionState

However if you need write access simply use IRequiresSessionState:

public class MyHttpHandler : IHttpHandler,
System.Web.SessionState.IRequiresSessionState


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

I was recently trying to check if IIS's SMTP sever was working correctly. To help I turned on the logging, leaving the default "W3C Extended Log Format" selected. I sent the test email and checked the log - to say it was unhelpful is an understatement! I was wrongly assuming that "extended" format meant it would have more information than the other logging formats which didn't have "extended" in their name!

Enter Steve who suggested I change the format to Microsoft IIS Log File Format (shown in the image below). I did, and the log file was suddenly completely what I was expecting and I could straight away see what I needed (the 'TO' email address in this case).

IIS_Logging

The above principle also works for normal web site logging to!


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

If you've ever wanted to indent ListItems in an ASP.NET DropDownList you've probably come up against a brick wall if you use a simple space. It will indent in the actual HTML OK, but browsers will simply ignore the whitespace and render all the items inline.

The trick is to do the following to enter the space, not use the spacebar:

Hold down "ALT" while typing 0160

Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

If you want to write to the Server's Event log from ASP.NET you'll first need to tweak some security settings. The first change is easy, however the second took me a while to find.

Step One:

Grant "Full Control" to the "IIS_WPG" group to the following registry key:

HKLM\SYSTEM\CurrentControlSet\Services\EventLog

Step Two:

One of the security restrictions added to IIS 6 under Windows Server 2003 was much tighter ACLs (Access Control Lists) on the event logs. This restricts what accounts can read and write to the logs (application, system & security). To overcome this you'll need to add the following value. Incase you're worried what security hole I'm opening up for you there's a breakdown of what it means at the end of the post.

(A;;0x0002;;;AU)

Add the above value to the end of the existing "CustomSD" value found at following path (pick relevant one):

HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application

HKLM\SYSTEM\CurrentControlSet\Services\EventLog\System

HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Security

So mine went from:

O:BAG:SYD:(D;;0xf0007;;;AN)(D;;0xf0007;;;BG)(A;;0xf0007;;;SY)(A;;0x7;;;BA)(A;;0x7;;;SO)(A;;0x3;;;IU)(A;;0x3;;;SU)(A;;0x3;;;S-1-5-3)

to: (scroll to all the way to the right to see the change in bold):

O:BAG:SYD:(D;;0xf0007;;;AN)(D;;0xf0007;;;BG)(A;;0xf0007;;;SY)(A;;0x7;;;BA)(A;;0x7;;;SO)(A;;0x3;;;IU)(A;;0x3;;;SU)(A;;0x3;;;S-1-5-3)(A;;0x0002;;;AU)

The value being added is written in Security Descriptor Definition Language (SDDL) and is as follows:

  • A = Access allowed.
  • ;; = token delimiter (don't know why there's two of them?)
  • 0x0002 = Permission to write log files.
  • ;;; = token delimiter (again - don't know why there's three of them?)
  • AU = Authenticated Users.

Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

I recently stumbled across these Remote Desktop keyboard shortcuts so thought I'd share them...

RDP Session Local Equivalent Description
CTRL + ALT + END CTRL + ALT + DEL Windows Security Dialog
CTRL + ALT + BREAK (none) Toggle RDP from window to Full Screen
ALT + INSERT CTRL + ALT + TAB Cycle through running programs
ALT + HOME WINDOWS Open Start Menu
ALT + DELETE ALT + SPACEBAR Open Control Menu
CTRL + ALT + (num pad) MINUS ALT + PRNT SCREEN Places image of active window on Clipboard
CTRL + ALT + (num pad) PLUS PRNT SCREEN Places image of entire desktop on Clipboard

If anyone has any others let me know...


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

If you've existing XML files that you need to manipulate in C# there is no need to write your own class by hand. Visual Studio ships with a little known tool XSD.exe which will do all the leg work for you.

Step 1 (from a command prompt):

This will create an XSD based on the XML file, which in step 2 will allow the tool to create the C# class(s)

XSD.exe [XML_FILE]

Step 2 (from a command prompt):

Call the tool again, this time providing the newly created .xsd file, along with /c (for classes, you could use /d for a dataset) and /n for your project's namespace

XSD.exe [XSD_FILE] /c /n:[PROJECT NAMESPACE]

Other parameters that might be useful are /o: (out) which specifies the output directory

Step 3:

Add the newly created .cs file to your project, and bingo, job done!


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

Following on from my last post I've since discovered how to access the User's Profile in an external Class Library (like I've mentioned before I prefer to factor out all business logic code into a separate class library).

Anyway the code is almost the same, although unfortunately you don't get strongly typed properties out of the box (unless you create your own profile object, inheriting from System.Web.Profile.ProfileBase).

Here's how to access the current user's properties (using FirstName/LastName) as before, only this time in a separate dll...

string firstname = HttpContext.Current.Profile["FirstName"];
string lastName = HttpContext.Current.Profile["LastName"];

And as before it's also possible to access other user's profiles (where 'Brad' is the user's username):

ProfileBase profileBase = ProfileBase.Create("Brad", true);
string firstname = profileBase["FirstName"];
string lastname = profileBase["LastName"];

Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

One of the many improvements in ASP.NET 2.0 was the Provider based architecture, and the most under used (certainly by me) is the User Profile Provider (click here for more info). You simply define the properties you want your users to have in the web.config (see below)

<profile enabled="true" defaultProvider="MyProvider">
     <providers>
       <add name="MyProvider" connectionStringName="MyConnection"
            applicationName="/" 
            type="System.Web.Profile.SqlProfileProvider" />
     </providers>
     <properties>
       <add name="FirstName" type="string" />
       <add name="LastName" type="string" />
     </properties>
   </profile>

And .NET takes care of the rest... you can then access the profile programmatically as follows:

string firstname = Profile.FirstName;
string lastname = Profile.LastName;

Simple!

There are a couple of problems using the SqlProfileProvider, firstly the data is serialised and stored in one field in the database - making it trickier to run SQL queries on it... and the second (or so I thought until recently) is accessing other user's profile data - for example an administrator should be able to see the other user's FirstName and LastName values.

However the second issue isn't a problem at all, it is in fact possible to view/edit another user's profile with the following code:

ProfileCommon userProfile = (ProfileCommon)ProfileCommon.Create("Brad", true);
userProfile.FirstName = "Paul";
userProfile.LastName = "Bradley";
userProfile.Save();

Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

If like me you've more than one computer at home and would like to be able to RDP to them directly then read on! If you follow the steps below you will be able to add extra "listening" ports to each of your computers so you can access them from anywhere... The reason you need to do this is to allow you to specify specific port forwarding rules in your firewall/router. The default RDP port is 3389 so I leave my main computer alone, but if i want to access my media centre I can get to it by adding :3390 to my hostname/ip. Any other computers you can just increase the port number.

Step one:

Run regedit from a command line on the computer

Step two:

Browse to the following key:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\
Control\Terminal Server\WinStations\RDP-Tcp  

Step three:

Export the key to your desktop - it will create a .reg file.

Step four:

Open the newly exported registry file in Notepad.

Step five:

The second line of the file should contain the path of the exported registry key - its important to change this, otherwise when you re-import it the existing RDP settings will be overriden. I prefer to create additional rules so you can always still access the computer normally inside your network. So the second line should end with:

WinStations\RDP-Tcp]

I suggest changing it to:

WinStations\RDP-Tcp-3390]

This will then create a brand new key when we re-import it later

Step six:

Still in notepad search for "PortNumber", it will look like this:

"PortNumber"=dword:00000d3d

You need to change its value - but its in hex so you'll need to use an online tool - or calculator in scientific mode. The following sets the port number to 3390:

"PortNumber"=dword:00000d3e

Step seven:

Save the file (make sure it's saving as a .reg)

Step eight:

Double click the saved file which will re-import it. (accept the confirmation messages)

Step nine:

Run regedit again and browse to your new key - just to check its there ok.

Step ten:

Re-boot the machine and try RDPing with :3390 on the end of its IP address.

Step eleven:

Login to your router/firewall add an incoming rule for port TCP 3390 to the IP of the relevant computer.

 

Repeat the above steps for each of your home computers (making sure to change the new port number each time!)


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

Its good practice to separate out code that can be re-used into its own class library which can easy be referenced by other projects. Until now any resources such as images/JavaScript files that I'd created I needed to remember to copy to each web site that used the dll, ensuring that I created the correct directory structure so the paths would be correct. Then it dawned on me that you must be able to embed these files into the dll itself. Here's the steps I used to embed an image file for a AjaxControlTookit Calendar Extender that I was using in a custom server control (the same principle can be applied to any file you need - typically JavasScript and/or images will be embedded)

Step 1:

Add your file to the Class Library project

(in my example I added [PROJECT ROOT]/Resources/Calendar.png")

Step 2:

Select the newly added file and in the Properties window select:

Build Action = "Embedded Resource";

Step 3:

In the Project's AssemblyInfo.cs file add the following line:

//Replace "BradsNamespace" with the namespace of your project
//Replace "Resources" with the folder structure from the root of the project 
//to the file (using . instead of / )
//Replace "Calendar.png" with your file's name //Replace "img/png" with relevant type e.g. "text/javascript", "image/jpeg" etc [assembly: System.Web.UI.WebResource(
    "BradsNamespace.Resources.Calendar.png", "img/png")]

Step 3:

Now retrieve the Url for the file:

Image CalendarImage = new Image();

CalendarImage.ImageUrl = Page.ClientScript.GetWebResourceUrl(
 this.GetType(),
 //Replace following with the string entered in AssemblyInfo.cs
 "BradsNamespace.Resources.Calendar.png");

Finished:

And that's it! Now when you run it .NET will generate the Url for you in the format:

src="/WebResource.axd?d=_Y_kbnu3Fp4-fBKDGxpOa-LgnKcgPOIASf1ExUjshsj3373650125705280" 

Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

Unfortunately the following code will fail if the file in question is ReadOnly:

File.Delete(path);

So I'd always recommend using the following (especially when Visual Studio keeps annoying checking in files that it shouldn't concern itself with!!)

//Check the file actually exists
if (File.Exists(path))
{
   //If its readonly set it back to normal
//Need to "AND" it as it can also be archive, hidden etc if ( (File.GetAttributes(path) & FileAttributes.ReadOnly)
== FileAttributes.ReadOnly) File.SetAttributes(path, FileAttributes.Normal); //Delete the file File.Delete(path); }

Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

If you want to display the size of a file to the user simply doing the following will result in the size of the file in bytes:

new FileInfo(PathToMyFile).Length

However that's fairly useless in today's world of bigger and bigger files so along comes this neat bit of code:

public static string GetFileSizeAsString(long size)
{
    double s = size;
    string[] format = new string[] { "{0} bytes", "{0} KB",  "{0} MB", "{0} GB", "{0} TB", "{0} PB", "{0} EB", "{0} ZB","{0} YB" };
    int i = 0;
    while (i < format.Length-1 && s >= 1024)
    {
       s = (int)(100 * s / 1024) / 100.0;
       i++;
    }
return string.Format(format[i], s.ToString("###,###,###.##")); }

This will return you the following strings (depending on how big the file is...)

  • 160 bytes
  • 345 KB
  • 34.7 MB
  • 1.2 GB
  • 1.76 TB
  • 8.19 PB and so on...

If you get a file bigger than an 1,024 Yottabytes (YB) then tough! I'm sure you'll agree its not really that much of a limitation!


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

UpdatePanels work fine with almost all controls, one control that it does 'break' however is FileUpload. If you try and upload a file it will seem to work - but if you check FileUpload.HasFile it will always return false. Rubbish if you've got an UpdatePanel in your MasterPage! However there is a solution, in fact it's not the UpdatePanel that breaks it, but the asynchronous PostBack instead. So a neat trick is to register the button you're using to cause the upload to use a normal PostBack. This can be done as follows:

this.ScriptManager.RegisterPostBackControl(btnUpload);

Then when the user clicks the upload button they'll get a normal PostBack (and therefore an uploaded file), while all the other controls on the page will continue to operate asynchronously - no need to re-jig the whole layout of the site.


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

I've just been investigating Microsoft SQL Server 2005 Reporting Services (SSRS) and in a word its great! However this post is about fixing a problem I found when trying to use the ReportBuilder from other computer.  When you click "ReportBuilder" in the SSRS web-based UI it will fail with a 401 denied error. By default (for security) the "ReportServer" virtual directory doesn't allow anonymous access, and Microsoft's ClickOnce technology doesn't seem to allow you to provide a username and password - fairly limited I think you'll agree.

Anyway on to the solution... if you give anonymous access to the whole virtual directory it will download ok - but when you come to login it will constantly fail, complaining that the Internet guest account doesn't have the required permission - even if you're providing your own credentials! Also I wouldn't be happy about giving this kind of access to the whole virtual directory as I'm a newbie to SSRS so I'm not sure what security holes I'd be exposing.

However after some digging I discovered that if you just give anonymous access to "ReportServer/ReportBuilder" folder within IIS then bingo it will let you download - and when you come to login it will work ok.

And for the security conscious amongst us, in fact you can be more secure than that, only the following files require anonymous access for it to work:

  • ReportServer/ReportBuilder/ReportBuilder.application
  • ReportServer/ReportBuilder/ReportBuilder.exe
  • ReportServer/ReportBuilder/ReportBuilder.exe.manifest

Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

I've just come back to do a "Phase II" of an old EPiServer 4.61 project and was having issue's with none of the child pages working, or even the CSS being loaded in the CMS area. I decided to investigate the style sheet error in the admin area - it was looking for /util/styles/system.css but if I looked in the file system such a file didn't exist... hmm! Then I realised it must be a virtual path and remembered that pre version 5 EPiServer used a custom error page to return virtual files and to do its friendly URL re-writing. So I looked in IIS and bingo there was the problem.... IIS was using the default 404 error page, once it was changed to /Util/Notfound.aspx everything sprung into life...

Tip from Steve: If you Open EPiManager and click on the offending site you'll get a warning message saying that IIS's configuration is incorrect - do you want to fix the 404 error page value! If only I'd thought to look there earlier!!


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

I seem to spend my whole life re-licensing EPiServer sites during development, then again when it gets moved to stage and finally production. Each time I go to EPiServer's site and can never find a simple link to generate a licence and always end up having to go the download area, re-register my details blah blah blah.

Anyway here's the link I should have been using - and it's now firmly bookmarked!!

http://license.ep.se/public/


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

Who hates trying to work out what a complex C# method does that you didn't write? I know I do! Now who also hates writing the aforementioned comments... yep me too again! Along comes GhostDoc... a nice little plug-in for Visual Studio that automatically generates XML comments for your C# code! All you need to do is hit the hot key (which you can define) or right click and choose "Comment this..." and bingo a method or property you had selected now has a comment. GhostDoc uses some clever behind the scenes code based on the name of the method/property and the data types involved to make a "best guess" effort on giving it a comment  - now I admit some of its suggestions do provide a few comedy results - but either way its often a good starting point!

I suggest you give it a go...

http://www.roland-weigelt.de/ghostdoc/


Bookmark with :
Digg It!