|
Spaces home Points to sharePhotosProfileFriends | ![]() |
|
|
July 04 Canberra SharePoint User Group July Meeting - Document Information Panel and Best of CodePlexIt's time again for the usual user group meeting in Canberra. Here are the details of what is coming up:
It should be a good one! Make sure you register and get along to it! July 01 Restricting page layout use in sub-sitesThis 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 workspaceThe 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 codeA 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.
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 collectionWhen 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 applicationsI'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 FarnhillThis 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 :-)
May 12 Changing navigation settings in codeIn 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 codeFrom 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: gIf 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 MOSSThe 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 boxesThe 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 controlI 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 CentreThis 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 createdI'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: {
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: } |