Update: Programmatically listing delegate controls

In my last blog post I discussed a method to programmatically read delegate controls for a specific control ID within a site. I did come up with a problem with this though – when my delegate controls were listed as a specific control type (with a ControlAssembly and ControlClass attribute in m Control element) they would load fine, but whenever I was using a User Control (with the ControlSrc attribute) they would never be returned to my code.

I managed to figure this one out very easily with the help of the beta version of Reflector Pro – I debugged my way into the stuff happening under the hood of the DelegateControl class and found that I had missed something rather obvious – when I was creating the DelegateControl to query for the controls with it was never added to a page, and as such when SharePoint attempted to call Page.CreateControl (as you do when you want to load a user control) the Page object was always null, thus why I was getting nothing back (and apparently these errors were being logged to the 12 hive, but I couldn’t see it at the time in amongst the rest of the rubbish there)

The solution – in my case I was calling this from a web part, so what I did was just add the DelegateControl object to the controls collection, did the query with the code form yesterdays post and then when I was done I removed the DelegateControl form the Controls collection of the web part – this solved the problem nicely, I hope that helps!

Programatically listing delegate controls

UPDATE: If you are using this code and can’t load user controls, then check out my update that explains the issue and how to resolve it

Delegate Controls are in my opinion one of the cooler things you can use when putting together a UI in SharePoint (and have previously talked about rolling out web parts as delegate controls). I’ve recently come up with a need to be able to read what delegate controls are stored against a specific controlID programatically though, which can be done but you need to get a little tricky to do it.

If you pull out reflector and have a look through the DelegateControl code, you will see that in it’s CreateChildControls method it refers to a class called SPElementProvider. This is the class that provides the mechanism for retrieving the delegate controls and returning them to the DelegateControl object to render on the page. Since this is an internal class we can’t call to it directly to do what we need to, so I took to it a different way. I created an instance of a DelegateControl, set the properties and then used reflection to call the “CreateChildControls” method, which will call to the SPElementProvider to go and get the objects, and then will add them to the Controls collection of the DelegateControl.

The last bit required to make this work is adding some HttpContext if you are running your code outside of SharePoint itself (I originally wrote this as a console app to test that it would work so had to do it). Once you create the context and run the code you can easily get to the controls – speaking of the code, here it is:

using (SPSite site = new SPSite("http://serverUrl"))
{
    using (SPWeb web = site.OpenWeb())
    {
        bool contextCreated = false;
        if (HttpContext.Current == null)
        {
            contextCreated = true;
            HttpRequest request = new HttpRequest(string.Empty, web.Url, string.Empty);
            HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter()));
            HttpContext.Current.Items["HttpHandlerSPWeb"] = web;
        }

        DelegateControl dc = new DelegateControl
        {
            AllowMultipleControls = true,
            ControlId = "AdditionalPageHead" //This can be whatever control Id you are looking for
        };

        MethodInfo method = dc.GetType().GetMethod("CreateChildControls", BindingFlags.NonPublic | BindingFlags.Instance);
        method.Invoke(dc, null);

        foreach (Control control in dc.Controls)
        {
            // Do something with control, it is returned as a System.Web.UI.Control here
        }

        if (contextCreated) HttpContext.Current = null;
    }
}

So this code will work both within SharePoint and in a console app, as it will create the context only if it is needed. Outside of that you can do whatever you need to with those delegate controls once you have them!

Managed accounts in SharePoint 2010

Continuing with my posts on the new features of SharePoint 2010, today I thought I would cover managed accounts. Basically managed accounts are a way of controlling your service accounts through the SharePoint central administration, including simple ways to change which services run under which accounts, as well as an inbuilt mechanism for changing the passwords of these accounts (and even automating the process of updating the passwords). If you had looked at updating service account details in MOSS you would have come across this article on MSDN that explains the process – and there are a lot of steps!

The settings for managed accounts are found in Central Administration in the security section, under the heading of General Security.

image

The first option “Configure managed accounts” will provide you with a list of managed accounts that are in use on the farm.

image

When you edit the items in this list you can specify a password expiration policy, notification settings, update the password for each account as well as see a list of farm components that the service account is running. 

The second option on the main security page, “Configure service accounts”, allows you to change which service account a specific service uses. You simply select the service, select the account and click save, very easy (especially when you think about how its done in MOSS).

image

The last link for “Configure password change settings” will allow you to specify how often the monitoring process checks the status of accounts for expiration, who gets notified as well as the time it waits between setting passwords.

That’s about it, just a quick introduction to managed accounts that will hopefully shine some light on one of the many new features in SharePoint 2010!

I’m a SharePoint MVP!

I got to start the new year with some fantastic news, I have been awarded the MVP award for SharePoint Server! I definately need to thank Ishai Sagi who has been a great mentor and friend, and Ben Walters who worked with me to make the SharePoint Saturday events in Sydney and Melbourne such a great success. Also the guys and girls who come along to the Canberra SharePoint User Group each month, it’s great being able to get together each month and talk SharePoint with you all, and lastly to everyone else out there who is involved with the SharePoint community – there are so many people out there who spend a lot of their own time doing stuff for the community, and it’s great being a part of that, so a big thank you to everyone!

Hiding content from anonymous users

Have you ever put together a master page for a public facing MOSS site and found that you are left with the “sign in” link where the user menu is? Or do you not like the empty tables that get rendered to your sites visotrs that are normally there to house the publishing console? Well fear no more, there is a very, very easy way to hide content in a master page or page layout from anonymous users. This method takes the idea of inheriting from the SPSecurityTrimmed control that I used when I wanted to hide content when a specific publishing field was blank, and applies it to hiding content when a user is anonymous, here is the code:

    public class HideFromAnonymousUser : SPSecurityTrimmedControl
    {
        protected override void Render(HtmlTextWriter Output)
        {
            if (HttpContext.Current.Request.IsAuthenticated)
            {
                base.Render(Output);
            }
        }
    }

This is really basic, basically we just check to see if the call to the page is authenticated, and if it is we render the contents. This means we can hide any part of a master page we want by using the control like this:

<div id="userBar">
<table cellpadding="0" cellspacing="0" align="right" border="0">
<tr>
<td>
<wssuc:Welcome id="ExplitLogout" runat="server" />
</td>
<td>
<PublishingSiteAction:SiteActionMenu runat="server" />
</td>
</tr>
</table>
</div>

In this example I have a table with the user menu and site actions menu in it, so stuff I obviously don’t want my anonyous users knowing is there. By wrapping it up in the hide from anonymous user tag, any anonymous user won’t only not see the controls, but they will also not see the table that I’m using to lay them out the way I want as well!

Custom 404 handling in SharePoint

rIf you have ever put together a WCM/Publishign site in MOSS, chances are someone at some point has asked you the question “Can we put in a custom page instead of the default file not  found one?”. In ASP.NET this is easy enough to do, you just update the CustomErrors section of your web.config file and away you go, but in SharePoint you’ll find it’s done slightly differently.

Basically if you have a look at the SPWebApplication object, you’ll see it has a property called “FileNotFoundPage”, which by default will be set to “sps404.html”. If you do  a quick search over the 12 hive for this one you’ll see that is lives at %12%\template\layouts\1033. Open it up and you can see the page is driven from JavaScript and will redirect you to spsredirect.aspx to attempt to redirect you (which as far as I know will land you on the standard SharePoint error page with a file not found error as opposed to just the usual file not found error in your browser).

Lucky for us we can easily update the page that you are sent to when a file isn’t found, and you can even do it through a feature so you don’t have to go messing around in the 12 hive (which is evil). Firstly, set yourself up new feature, scope it to the web application and add a receiver to it. In the receiver add some code to update that FileNotFoundPage property to a custom file name that you create and update the web app.

public override void FeatureActivated(SPFeatureReceiverProperties Properties)
{
    SPWebApplication webApp = Properties.Feature.Parent as SPWebApplication;
    webApp.FileNotFoundPage = "custom404.html";
    webApp.Update();
}

public override void FeatureDeactivating(SPFeatureReceiverProperties Properties)
{
    SPWebApplication webApp = Properties.Feature.Parent as SPWebApplication;
    webApp.FileNotFoundPage = "sps404.html";
    webApp.Update();
}

I include the deactivating so that when you turn your feature off, the normal 404 handling is put back in place.

Next, make sure your WSP file with the feature in it also creates that custom file not found page for you – just take a copy of sps404.html and include it in your project. Lastly, update this file so that it will send your users to  an appropriate page, there is one line of code in that html file to change for this:

STSNavigate("/_layouts/MyCustomPage.aspx?oldUrl=" + requestedUrl);

That page you put into this line and be anything, so you can put an ASPX page in the layouts folder that has some code behind it to handle the 404’s appropriately (maybe look up against a list of some sort and direct users to content that has been moved perhaps?). You can do whatever you need to here, the original URL that was requested comes to you in the query string there so you are free to use it as you wish.

One thing to note about this though is that it relies on JavaScript to work, if you hit a page that doesn’t exist when this page is called you will see that it takes you to a page with this text

“File Not Found. Please turn on client scripting or browse with a browser that supports client scripting to allow the 404 url dreict feature to work.”
(Note the typo in direct there – it actually spits it out like that, thats not one of my typos!)

This is done through the use of a meta refresh tag in that html file, so you could look at changing the value of that as well so that when users without JavaScript hit the page they are taken somewhere that displays a friendlier error screen as well, but it is something to keep in mind.

Other than that, this is all pretty straightforward and doesn’t take long to implement, so think about using it on your next site to add a little bit of polish to it :-)

Creating collection objects in PowerShell

Just a quick post on some PowerShell today – I have been doing some work around deplying WSP files through PowerShell (doing it programatically, not through STSADM) and I did come up against one thing that got me a bit  stumped. When you use the “New-Object” cmdlet you need to get it to create a collection object (in my case I basically needed Collection but done in PowerShell).

It is doable, but the catch is that when you specify the object  that the collection is made up of you need to use the fully qualified name of the class, including its assembly. So for me to create the SPWebApplication collection I used:

New-Object "System.Collections.ObjectModel.Collection``1[[Microsoft.SharePoint.Administration.SPWebApplication, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c]]"

 So you can use that to  create collections and other generic lists, enjoy!

Customising table formats for the rich text editor

The rich text editor in a publishing site of SharePoint has got a bunch of different things about it that you can customise, which is great for providing ways for your authors to create content that is going to meet your sites design rules, and yesterday I came across another possible customisation – the table formats. Basically what I’m talking about here is in the window you get when you create or edit a table you can choose a style for it from 5 predefined formats, and these are entirely customisable and are the subject of today’s post.

As with the styles in the drop down menu of the editor, these table styles are all customised through the use of CSS. This is pretty well document on MSDN over at http://msdn.microsoft.com/en-us/library/ms561507.aspx so I won’t waste time talking about the process. I will mention one thing about what is there though, in that the way it says to use the styles and the code sample it gives is slightly wrong to get it to actually work.

The problem stems from the fact that when you provide custom table styles the default ones won’t be selectable for users anymore, but the CSS for them will still be pumped down to the page, which means that if you create a table style for number 1 for example, you need to make sure that the CSS you create will override what the OOTB style has (so an example could be tha you don’t want your rows to have a background colour, but the first table style that Microsoft provided has a background colour, so your CSS needs to specifically state background:none or else it will inherit the Microsoft one). This becomes a problem when you need to ensure that you override things in every possible style, like first column, last column and each row.

Lastly, I also found the code sample on MSDN didn’t work the way I thought it would, in that I had to prefix a lot of the styles with the main table class and some tags as well to get everything to work right. Here is a list of the CSS styles that I applied things to in order to get this working:

  • Table
    • .ms-rteTable-1
  • Table Header Row
    • .ms-rteTable-1 TR.ms-rteTableHeaderRow-1
    • .ms-rteTable-1 TD.ms-rteTableHeaderFirstCol-1
    • .ms-rteTable-1 TD.ms-rteTableHeaderLastCol-1
    • .ms-rteTable-1 TD.ms-rteTableHeaderOddCol-1
    • .ms-rteTable-1 TD.ms-rteTableHeaderEvenCol-1
  • Table Body
    • .ms-rteTable-1 TR.ms-rteTableOddRow-1
    • .ms-rteTable-1 TR.ms-rteTableEvenRow-1
    • .ms-rteTable-1 TD.ms-rteTableFirstCol-1
    • .ms-rteTable-1 TD.ms-rteTableLastCol-1
    • .ms-rteTable-1 TD.ms-rteTableOddCol-1
    • .ms-rteTable-1 TD.ms-rteTableEvenCol-1
  • Table Footer Row
    • .ms-rteTable-1 TR.ms-rteTableFooterRow-1
    • .ms-rteTable-1 TD.ms-rteTableFooterFirstCol-1
    • .ms-rteTable-1 TD.ms-rteTableFooterLastCol-1
    • .ms-rteTable-1 TD.ms-rteTableFooterOddCol-1
    • .ms-rteTable-1 TD.ms-rteTableFooterEvenCol-1

So once you have the right style names/prefixes to apply it’s a straight forward process, and you can have as many styles as you like. I think this makes a great way to apply the layouts to tables because it means that if your users are using this to style them, you can then update your CSS later and all the tables that were styled this way will be updated right away, where as tables that users manually had to set up colours/fonts/etc will not, so this is a nice UI win in my opinion!

Updated SPLinkChecker on CodePlex

Just wanted to drop a quick note to say that I have just release v0.2 of the beta for the SharePoint link checker solution on CodePlex. The updates include:

  • Performance improvements to make scans a bit quicker
  • The reports are stored in temporary files until they are pushed back up to central admin, freeing up memory
  • The URLs that are checked are now properly cached so a HTTP request will not be sent to the same URL twice
  • A time limit of 45 minutes was added to prevent an issue where the timer job would essentially time out and restart, preventing it from saving a report because the scan wouldn’t finish. Now if the scan goes for that long it will save the report with everything that it has already found.

So as you might be guessing, these changes came about while running the tool on some larger SharePoint sites. I think what I’m going to need to do is ditch the model that sees this run as a SharePoint timer job and swap it for something that is a Windows Service that runs on the server instead. That way I won’t be limited to a time limit to run the job within, I just need to change the UI to allow it to schedule the jobs in a way that the Windows Service will know about them, and then I need to look at the deployment model as well, because a WSP file can’t deploy a Windows service (I have a couple of ideas up my sleeve for this one though!)

Anyway, get on over to http://splinkchecker.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=34450 to download it and be sure to let me know what you think of it