More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  Points to sharePhotosProfileFriendsBlog Tools Explore the Spaces community

Blog

July 04

Canberra SharePoint User Group July Meeting - Document Information Panel and Best of CodePlex

It's time again for the usual user group meeting in Canberra. Here are the details of what is coming up:

This month Stephen Muller from InFront systems will tell us all about How to leverage the Document Information Panel in Office 2007 to provide a user friendly metadata entry form for SharePoint.

Demo will include:

  • Overview of the DIP.
  • Show basic SharePoint usage.
  • Open a basic DIP in InfoPath.
    • Show how to edit it
    • Discuss limitations
  • Make changes.
  • Save and demo.

A great prize awaits in this presentation, as we will ask for your ideas for panel developments, and Stephen will reward the best idea.

After that we will demo and discuss the open source project in codeplex for SharePoint and vote on the top 10! If you have a favourite codeplex project that you want us to highlight, please fill in this survey to let us know about it – otherwise, we will focus only on the most downloaded ones.

It should be a good one! Make sure you register and get along to it!

July 01

Restricting page layout use in sub-sites

This is one that I had no idea was even possible, but apparently you can restrict which page layouts a site in SharePoint can use, no matter what page layouts are available in the root sites gallery. The way this became apparent to me is that the OOTB search centre web site will only you to create pages using the 4 OOTB search related page layouts. If you create a new page layout that uses the same content type (Welcome Page for those who are interested) it still won't show up on the list of allowed page layouts when you create a page in search centre.

So to add a page layout to the allowed page layouts collection, use some code that goes like this (This is the code I used to add a page layout to the search centre site):

   1: using (SPSite site = new SPSite("http://[servername]/searchcenter")) 
   2: { 
   3:     using (SPWeb web = site.OpenWeb()) 
   4:     { 
   5:         PublishingSite psite = new PublishingSite(site); 
   6:         PublishingWeb pweb = PublishingWeb.GetPublishingWeb(web); 
   7:         PageLayoutCollection allLayouts = psite.GetPageLayouts(false); 
   8:         PageLayout[] allowedLayouts = pweb.GetAvailablePageLayouts(); 
   9:  
  10:         PageLayout[] newPageLayotus = new PageLayout[allowedLayouts.Length + 1]; 
  11:         for (Int32 i = 0; i < allowedLayouts.Length; i++) 
  12:         { 
  13:             newPageLayotus[i] = allowedLayouts[i]; 
  14:         } 
  15:         newPageLayotus[newPageLayotus.Length - 1] = allLayouts["/_catalogs/masterpage/[page layout name].aspx"];
  16:         pweb.SetAvailablePageLayouts(newPageLayotus, false); 
  17:         pweb.Update(); 
  18:     } 
  19: } 

The code above uses the PublishingSite and PublishingWeb classes, so be sure to reference Microsoft.SharePoint.Publishing to your code. So basically what we are doing here is to get an array of the currently allowed page layouts in the search centre, as well as a collection of all the page layouts in the site collection. We then build a new array that has one additional item in it and for the extra item we specify a specific page layout from the all page layouts collection. We then set the new array to be the available page layouts for the search centre publishing web object, and update the web to commit the changes.

So this same principal could apply to any number of scenarios. You can tell if a PublishingWeb object is allowing all page layouts through the IsAllowingAllPageLayouts property. My idea here is that in a feature receiver you could control what page layouts are used in a site, then tie that feature to a site definition (either in a custom definition or through feature stapling if changing the OOTB site definitions is your thing). This way you can provide some more control over the look and feel of a publishing site. Very cool stuff in my opinion.

June 27

Determining which event item corresponds to a given event workspace

The scenario for today's post is this - you are working with an event workspace that was created from an event item at a top level site (by selecting the event workspace option when you create/edit the item). You now in code need to know which item in the events list corresponds to the event workspace itself. This could be because your code needs to know some metadata about the event itself (like its location, time, or some other field you may have added to the top level list).

I had a look through the SPMeeting class, and there didn't appear to be any easy way to do this. Here is what I ended up having to do to get the list item:

   1: using (SPSite site = new SPSite ("http://Url_of_Meeting_Workspace"))
   2: {
   3:     using (SPWeb meetingWeb = site.OpenWeb())
   4:     {
   5:         using (SPWeb eventHostWeb = site.OpenWeb("/events")) // this is the server relative URL to the site with the event list in it
   6:         {
   7:             // Get the list the item is in, and the information about the meeting
   8:             SPList eventList = eventHostWeb.Lists["Calendar"]; // the name of the event list
   9:             SPMeeting meetingInfo = SPMeeting.GetMeetingInformation(meetingWeb);
  10:  
  11:             // This query will get the list item from the list
  12:             SPQuery meetingQuery = new SPQuery();
  13:             meetingQuery.Query = "<Where><Eq><FieldRef Name='Workspace' /><Value Type='Text'>" + meetingWeb.ServerRelativeUrl + "?InstanceId=" + meetingInfo.InstanceId.ToString() + "</Value></Eq></Where>";
  14:             
  15:             // This is the SPListItem that represents the meeting item in the calendar
  16:             SPListItem eventItem = eventList.GetItems(meetingQuery)[0];
  17:         }
  18:     }
  19: }

This code isn't the cleanest I have put together (it doesn't have any error trapping) but it will do for the purpose of this example. Basically you get the SPWeb objects of the event workspace and the site that contains the calendar list of events, get the SPMeeting object for the event to help build a query that will allow you to get the list item from the list easily. Once you have that list item you can go ahead and grab whatever information from it you need (or even update it if you need to).

An example of how this could be used is in a custom web part for a meeting workspace that was designed to show the full metadata about the event - you could read the data from the list item and put it into a nice web part so that when a user hits the event site they can see all of the events info, the way you want them to see it.

June 13

Updating SharePoint's web.config file from code

A lot of the time when you are writing custom code for SharePoint you will find yourself needing to make changes to the web.config file to allow your code to run, or to provide settings for it, or whatever else it may be. This is all well and good, but when you have a lot of custom code with a lot of changes and your production environment has multiple front end servers or multiple web sites for different zones, you will find yourself with a lot of changes to make and you will need to make them in multiple web.config files.

To get around this, the SharePoint API provides us with the "SPWebConfigModification" class. This class will allow you to make changes to the web.config file of an SPWebApplication object, which means when the change is applied it will go to every site on every front end web server for that web application. Pretty cool huh? The code looks a little like this:

   1: SPWebApplication WebApp;
   2: SPWebConfigModification mod = new SPWebConfigModification(add[@key='myAppSetting'], "configuration/appSettings"); 
   3: mod.Owner = "Test Changes"; 
   4: mod.Sequence = 0; 
   5: mod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode; 
   6: mod.Value = "<add key=\"myAppSetting\" value=\"test setting\" />"; 
   7: WebApp.WebConfigModifications.Add(mod); 

The above will add a setting to the <appSettings> part of the document. Now what makes this whole set up very dev friendly is that you can also remove the changes quite easily. You can parse the WebConfigModifications property of your web application and remove the changes you need to.

To really make this shine, use this code in a WebApplication scoped feature. This way you can set the owner property to be the GUID id of the feature, so when the feature is deactivated your changes can be pulled back out. Also then when new servers are added to your environment, the feature will be activated and the changes are applied without any other thought.

There are some issues with this class that may cause you some problems.

  • If the type is EnsureSection (for top level sections of the config document, like system.web and appSettings) they will not be removed when you remove the change. You can still remove the contents of each section, leaving you with an empty section, but the section itself will stay. If you manually take it out, the section will also re-appear when another change is made to the config file through the SPWebConfigModification class. This is only a problem for custom sections really, as default sections that are empty shouldn't be a problem
  • The Sequence property is supposed to allow you to order your changes - it doesn't. Changes are applied in alphabetical order of the Value property, so if you have changes that are required to appear in a specific order in your config file, you might need to look for a different way to do this.

Those are the only two big problems I found. If you want to read up more on how to use this class, there is a great article on Mark Wagner's blog, which goes into a lot more detail than I have here, it is definitely worth a read.

June 05

Making master page images relative to the current site collection

When you are working with master pages in SharePoint, there are times that you will put in your own custom images. This is fine, except it can often be tricky to get the src attribute right if your master page is going to be used in a site collection that is not at the root of the web site (for example /sites/mynewsite).

To get the image URL right, you can use this bit of code that I got from Ben Robb:

   1: <img src="<% $SPUrl:~SiteCollection/Style%Library/Images/MyImage.jpg %>" alt="My Image" runat="server"/>

The important thing to note here is the runat="server" attribute, without it the tag will just spit out the code as it is without inserting the correct URL. So by doing this your master page will become mobile enough to be run from any site collection, no matter where it resides.

UPDATE:

I have just discovered that if you put this into a master page that you are applying in a feature, you will get the following error when you attempt to browse the site:

An error occurred during the compilation of the requested file, or one of its dependencies. The type or namespace name 'Publishing' does not exist in the namespace 'Microsoft.SharePoint' (are you missing an assembly reference?)

This is because to translate the URL the publishing libraries are required. So the simple workaround is to make sure the following code goes at the top of you master page with the other directives:

   1: <%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   2: <%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   3: <%@ Register TagPrefix="PublishingVariations" TagName="VariationsLabelMenu" src="~/_controltemplates/VariationsLabelMenu.ascx" %>
   4: <%@ Register Tagprefix="PublishingConsole" TagName="Console" src="~/_controltemplates/PublishingConsole.ascx" %>
   5: <%@ Register TagPrefix="PublishingSiteAction" TagName="SiteActionMenu" src="~/_controltemplates/PublishingActionMenu.ascx" %>
June 03

Enabling AJAX in SharePoint web applications

I'm finally back from my leave, so I thought I had better get back to making some useful posts. Today I came across some issues trying to use AJAX enabled controls that we had written in a MOSS application. There is no reason that you can't do this, but if you are going to do it you have to make some changes to the web.config file of each MOSS web application that will use AJAX controls.

The SharePoint team blog has a good post on how to do this, but the post is related to the very early versions of AJAX. If you have version 3.5 of the .NET Framework installed on your server, things need to be just slightly different, as the AJAX controls have been bundled in with the framework itself, and therefore the DLLs that need to be referenced have different version numbers.

So to get AJAX going in MOSS when .NET 3.5 is installed on the server, follow the instructions in the post I linked to above, but change the version number in everything that refers to system.web.extensions from 1.0.61025.0 to 3.5.0.0 and everything will be sweet.

If you don't put the additional sections required for AJAX into the web.config file of an AJAX using MOSS web application, you will find that any pages that require AJAX will throw errors on almost all of your JavaScript, and the status bar at the bottom of the page will read "Please wait while scripts are loaded" and will not change. So if you are seeing this error, you simply need to update your web.config file for the relevant MOSS app (remember these are usually in C:\inetpub\wwwroot\wss\virtualdirectories\portnumber\) and your set.

May 18

Meet my new daughter - Vanessa Margaret Farnhill

This is totally off topic for this blog but I had to share it with the world anyway (and it will help explain the lack of posts over the coming weeks!) - I'm now a dad! Alicia gave birth to little Vanessa Margaret Farnhill on the 15th of May, weighing in at 3.32 Kg (7 punds 5 ounces for those who don't like the metric system), and at a length of 47cm long. She really is adorable, and I'm looking forward to getting to know her and watch her grow. Friends of mine on Facebook will find the main photo stash there, but here are a couple of ones that I like :-)
 
Mum and bub Vanessa at a few hours old What are you looking at? :-)Westsiiiide Me and my gorgeous girl Vanessa and the bear Pa got her :-) 
May 12

Changing navigation settings in code

In a previous post I have discussed how to add links to a quick launch list in a SharePoint site. Now I have had to go a step further and update the navigation settings for a site, such as telling to to include pages and sub-sites automatically in the navigation. This is easy enough to change when you know how, it is all based around some site settings, as follows:

   1: SPWeb web;
   2: web.AllProperties["__IncludeSubSitesInNavigation"] = "False";
   3: web.AllProperties["__InheritCurrentNavigation"] = "False";
   4: web.AllProperties["__NavigationShowSiblings"] = "False";
   5: web.AllProperties["__IncludePagesInNavigation"] = "False";
   6: web.Update();

Each of these settings are fairly self explaining, and you can set them all to be either True or False. As always, don't forget the web.Update() at the end to record you changes to the database.

May 09

Change the master page with code

From time to time it might be necessary to change the master page settings of a page with some code (a current example I have is with a feature. when it is activated it changes the current sites master page to a different one). This is easy enough to do through the properties of SPWeb:

   1: SPWeb web;
   2: web.MasterUrl = "/catalogs/masterpage/newfile.master";
   3: web.CustomMasterUrl = "/catalogs/masterpage/newfile.master";
   4: web.AlternateCssUrl = "/Style Library/mystyles.css";

This will work for you, but when you browse to the master page settings in the site, it will still look like it is being inherited from the parent. This is because there is a site setting that needs to be changed to tell the site that the master page is not inheriting. Use this code to make sure this happens:

   1: Hashtable webSettings = web.AllProperties;
   2: webSettings["__InheritsMasterUrl"] = "False";
   3: webSettings["__InheritsCustomMasterUrl"] = "False";
   4: webSettings["__InheritsAlternateCssUrl"] = "False";

The above code updates the site settings so the UI will accurately reflect the settings you have applied. Once this code has run, remember to finish off with a web.Update() statement to commit all the changes your code has made.

May 01

Error: Value cannot be null. Parameter name: g

If you work with custom site definitions and do it right, you wont ever see this error come up - me on the other hand, had a bit of a shocker last Friday and chased my tail for half a day trying to figure this one out. Basically you will get this error in your 12 hive log (I think you get "Unknown error on the screen" when you try to create a site from a site definition that has some invalid XML in the WebFeatures section of the ONET.xml file.

A common mistake is the casing of the attributes (remember that XML is case sensitive), so for example <Feature Id="" /> wont work, but <Feature ID="" /> is the one you need, so by having the ID attribute cased wrong, you will see this error.

So the best recommendation I have for dealing with this rather useless error message, go back to the onet.xml file for you site definition, and have a good hard look at the XML to make sure it is all valid.

April 30

Nesting Master Pages in MOSS

The project I am on is starting to get into some 'look and feel' territory, which means we are of course doing some master pages. What we wanted to achieve was to have a single master page so that the look and feel of the site is defined in a single place, instead of in multiple master pages - but lets be honest, a single master page will rarely cut it for you, especially if you have multiple types of sites that need to look slightly different. This is where master page nesting comes into play.

ASP.NET 2.0 introduced us to master pages, and with it a feature called nesting of master pages. This is exactly what it sounds like, where a master page can have its own master page, so you create a parent-child relationship of sorts between them. Given that WSS and MOSS are just ASP.NET applications at their base level, they too can use nested master pages (although none of the out-of-the-box functionality uses it).

So what we have done is created a single master page that has the sites main header and side navigation on it, and we are using child master pages to define different "layouts" for each page - such as one master page has a right bar down the side, and we have another master page that has controls on it for meeting workspaces, but they all share the same parent master page, so when the look and feel needs to change, chances are it will only need to be done in the one parent file.

The easiest way we found to achieve this was based on a blog post from Ari Bakker, where he demonstrates renaming the content areas with 'Base' at the end of them in the parent master page, and in the child master page you add content to the "base" content area, inside which will go the actual content area (as it was in the parent master, before it was renamed with the 'base' bit on the end). This worked fantastically, especially for the meeting workspace as we could add the meeting specific controls to the master page (Like the out-of-the-box MWSMaster.master file in every meeting workspace) and still keep the look and feel centralised.

If you are developing look and feel for any MOSS site, I would seriously be considering using nested master pages - they will greatly simplify the maintenance of the site.

April 21

Postback from TreeView check boxes

The ASP.NET TreeView control is a handy thing in plenty of places in ASP.NET applications, but when I went to use one in a project I am working on, I discovered that there is no AutoPostback property for the checkboxes that are provided, and considering I am using my TreeView in an UpdatePanel this was a bit of a problem for me - but there is a pretty easy work around for it.

First of all, get this JavaScript onto you page somewhere:

   1: function postBackCheckBox()
   2: {
   3:     var o = window.event.srcElement;
   4:     if (o.tagName == 'INPUT' && o.type == 'checkbox' && o.name != null && o.name.indexOf('CheckBox') > -1)
   5:     {
   6:         __doPostBack('', '');
   7:     }
   8: }

Then Add this line of code to get your control to use that JavaScript:

   1: myTreeView.Attributes.Add('onclick', 'postBackCheckBox()');

Lastly (and you can ignore this if you are not using a TreeView inside an update panel) you need to add a button that will postback your update panel for you, so add a linkButton to the page, set its text property to be an empty string, and then put this code in to get the script manager to refresh the update panel of the JavaScript.

   1: myScriptManager.RegisterAsyncPostBackControl(myLinkButton);

Also you will need to change the "__doPostBack('', '');" line of the JavaScript to be "__doPostBack('myButton', '');" where myButton is the client side ID of the link button control.

Once you have done this, fire up your page and check a box in your treeview, it will then postback on its own (or if you did it inside an update panel, the panel will refresh with the new content).

April 17

Trigger an UpdatePanel from outside the control

I have been working on a control that is using some AJAX stuff, which is a bit new to me because I have avoided AJAX for a few reasons in the past. Anyway once you get into it, the controls that MS have added to the .NET framework make it so very very easy to get some basic AJAX functionality onto a page.

The main control used for this is the UpdatePanel. When you put one of these bad boys on your page, if something inside the panel causes a postback, it will still do your postback, but only the content up the UpdatePanel will refresh on the screen. That is all handled by the .NET framework, not a single line of JavaScript is written - very cool stuff. (I have read that the JavaScript postback that occurs is pretty fat and isn't gonna save you a lot of bandwidth, so if you are really out to shrink the load time, then you are probably better looking elsewhere).

Anyway, I had a need to cause the UpdatePanel to update, but I needed a control that wasn't in the panel to do it. It's pretty easy to handle, and here is how you do it. Basically you register the control with the ScriptManager, then when the ScriptManager is spitting out the JavaScript for its AJAX post back stuff, your control is included. So if you have put the ScriptManager on the page all you need to do is this in your OnLoad event:

   1: ScriptManager1.RegisterAsyncPostBackControl(myButton);

That's it, now when the control 'myButton' throws a postback, it will do it via AJAX, and the update panel will refresh. Too easy.

April 14

Canberra SharePoint user group April Meeting - Security with ForeFront/Customising the Search Centre

This month's user group meeting is on Wednesday night, and we have two people presenting this month.

Derek Moir from Microsoft will present about Microsoft ForeFront - security for SharePoint. This is a chance for the IT professionals to come in and hear about defending your SharePoint installation from viruses and attacks.

Then, Patrick Tisseghem - an MVP from Brussels will be presenting a session about a number of techniques that are explained in the latest MS Press book "Inside Index and Search Engines: MOSS 2007" regarding the customization of the Search Center. The 100% demo-driven session starts with the proper configuration of the index engine to the full customization of the pages and Web Parts that are part of the Search Center.

As always, the beer and pizza are on the house, so come along, meet some fellow SharePoint enthusiasts, and you will probably learn a thing or two as well. To register for the event, check out the details on the Canberra user group web site at http://www.sharepointusers.org.au/Canberra/Lists/Events%20Calendar/DispForm.aspx?ID=10.1.4&Source=http%3A%2F%2Fwww%2Esharepointusers%2Eorg%2Eau%2FCanberra%2FLists%2FEvents%2520Calendar%2Fcalendar%2Easpx.

April 10

Run code when a meeting workspace is created

I'm not sure how common this one is, but I thought I would add it here just in case I ever needed to do it again! Basically the requirement is that we have to perform a specific task every time a new meeting workspace is created. After a little bit of thought, the easiest way to do this is with an event handler, specific the SPListEventReceiver. If you have an event receiver that you attach to your events list with the following code, you can handle new event workspaces being created.

   1: public class MeetingWorkspaceHandler : SPItemEventReceiver
   2: {
   3:     public override void ItemUpdating(SPItemEventProperties properties)
   4:     {
   5:         if (properties.BeforeProperties["Workspace"] == null)
   6:         {

7: if (properties.AfterProperties["Workspace"] != null &&
!String.IsNullOrEmpty(properties.AfterProperties["Workspace"].ToString()))

   8:             {
   9:                 string url = properties.AfterProperties["Workspace"].ToString().Split(new char[] { ',' })[0];
  10:                 using (SPSite site = new SPSite(url))
  11:                 {
  12:                     using (SPWeb web = site.OpenWeb())
  13:                     {
  14:                         // Do stuff here with web as the meeting workspace
  15:                     }
  16:                 }
  17:             }
  18:         }