I recently looked at some code that pulled a bunch of news items from a SharePoint list and displayed them in random order. The code was a little verbose, so I changed it to use the System.Data.DataSetExtension helper methods. The final code looked something like this:
//Perform the normal SPQuery
SPQuery query = new SPQuery();
// query options left for reader to insert
//the list variable is a reference to the SharePoint list you want to query
DataTable data = list.GetItems(query).GetDataTable();
Random rnd = new Random();
//Randomize the items using LINQ and some helper methods from System.Data.DataSetExtension
DataTable items = data.AsEnumerable().OrderBy((i) => (r.Next()));
The other nice thing about using the Random class is its flexibility to offer some other cool features. Just say you wanted to show the same random items all day, then it’s just a matter of seeding with the current day.
We’ve all probably worked on projects in the last 5 years or so that have involved reworking applications that were once built in MS access, in my case it was an access system that stored important safety testing information that was captured in an engineering workshop. It was originally designed and built by a mechanical engineer, who didn’t know much about database design. The system was redeveloped for a number of reasons including: - The data couldn’t be shared, it was locked away on a PC in the workshop, no analysis of the data could be performed.
- It didn’t scale well, only one user at a time could access it.
There are lots of other reasons why MS access shouldn’t be used for this type of information, but my point was that Access is a pretty poor tool for critical business information because it was difficult for the business to access this information. I think most people would share this view. Lets compare a couple of scenarios with SharePoint as the tool, so all the information would be stored in a list: - The data can be shared by webservices and RSS, with effort.
- It can scale, multiple users can access it at the same time.
But is the business data in a SharePoint list really easier to work with? Can it: - Be used in SQL Server analysis cube?
- Easily used in Reporting Services?
- Joined with other business data to see correlations?
- Perform complex real world queries?
- Do you really want to model your business data based on the limitations of SharePoint?
The answer is no. I still think that business data needs to live in a system designed for business data, i.e. A database: SQL Server. From here it can be queried, joined and more importantly shared, whether that be back into SharePoint or any other tool that supports a database (Reporting Services, Performance Point, Analysis Services etc). So now that brings me back to my comparison with MS Access, we are now doing lots of work moving systems away from MS Access, will we be doing the same thing in 5 years time, moving our SharePoint lists away from SharePoint? I think SharePoint lists have their place, no question, but not for line of business data. Keep the SP lists for trivial data that is not important to the overall operation of your business.
By default SharePoint will use impersonation, so the web.config file will have the setting: <identity impersonate="true" />
This means that if you try and connect to a SQL server database from say a custom web part, the connection will appear to SQL server as the user that requested the page. This is very useful if you care about the actual user, so for example if the database has permissions set based on this assumption.
However it is often useful to have just the application pool account connect to the SQL Server database or you may wish to give the application pool account permissions to connect to active directory but not every user.
It this case you will need to impersonate the application pool account. There is a bit of code floating around the web that uses P/Invoke to call ReverToSelf() from the advapi32.dll. It turns out it’s simpler than that:
using (HostingEnvironment.Impersonate()) { // access external resource as app pool account }
The HostingEnvironment.Impersonate() will do the same thing that as a call to RevertToSelf().
If you like me and organise your data access calls into nice methods that perform a discrete action such as:
public static void DeleteUser(int userID) { //do some database access }
You might be really loathed to wrap all these methods with the same code like:
public static void DeleteUser(int userID) { using(HostingEnvironment.Impersonate()) { //do some database access } }
Instead you might like to take a look at what a project such as PostSharp can offer:
"You can make your own custom attributes that will really add new behaviors to your code! This is sometimes called aspect-oriented programming (AOP) or policy injection."
This means that you can define your own attribute that will have code that will run on any invocation of your code:
public class ImpersonateAttribute : OnMethodInvocationAspect { public override void OnInvocation(MethodInvocationEventArgs eventArgs) { using(HostingEnvironment.Impersonate()) { eventArgs.Proceed(); } } }
So our code then looks like this:
[ImpersonateAttribute] public static void DeleteUser(int userID) { //do some database access }
Much easier to maintain. Now every bit of code with this attribute will run as the application pool account.
We needed the basic ability to redirect specific users to a different page. I first took a look at Adam Buenz excellent Redirection web part but we thought that it might be a little bit complex for our content authors to use, so I put together a really simple web part that gives the user the ability to select the users or groups via the standard SharePoint people picker interface. The idea is that you can select a number of groups or users to apply the redirection to, this could be as broad as all authenticated users or refined to a single user. Of course you need a way edit the web part, so another people picker dialog allows the user to select users or groups that this redirect does not apply to. If they match the exclusion, the web part will present them with the text ‘This account was excluded from redirection to <url>’. This exclusion group is normally the content authors. It is possible to put a number of these web parts on the same page, so if for example you wanted to redirect to a number of different sites based on AD group or user accounts, this should work fine. First step, as always is to add the solution to SharePoint, once the solution is deployed, be sure to activate the ‘Redirector web part’ feature at the site level: Once that is done, it’s just a matter of selecting the ‘Redirect web part’ from the web part gallery: Hopefully you’ll find some use for it. The source code can be downloaded here and the WSP can be found here. The code for this web part is fairly straight forward, I have a tool part that uses the SharePoint PeopleEditor control. This control has a property to return the selection of groups or accounts as a comma separated string. With this string I can then do a search to see if the current user is in the group like: private bool IsUserIn(string commaSeperatedList) { bool isMatch = false;
if (commaSeperatedList != string.Empty) { foreach (string group in commaSeperatedList.Split(',')) { if (Page.User.IsInRole(group) || Page.User.Identity.Name == group) { isMatch = true; } } } return isMatch; } So the web parts main logic looks like: bool redirectMatch = false; bool exclusionMatch = false;
redirectMatch = IsUserIn(selectedADVal); exclusionMatch = IsUserIn(excludeADVal);
if (redirectMatch == true && exclusionMatch == false) { Page.Response.Redirect( redirectUrl); } else { if (redirectMatch) { outputCtrl.Text = "This account was excluded from redirection to " + redirectUrl; } }
By default when you edit the tool part settings of a SharePoint web part and you click on the ‘…’ if the property is a string, you will get the default property builder: Clicking the above ‘…’ will launch the default string property builder: This popup is actually the ‘zoombldr.aspx’ page located in the _layouts virtual directory. There are a few problems with this builder, well it’s not so much the builder, the string property’s of the web part are serialised in a manner that strips out carriage returns. So if you were to spend time formatting the contents of this dialog, it will get wiped out when you save the contents. This is most apparent when you edit the XSLT of the core search results web part. How can you override this property? The key is to make use of the HtmlDesigner attribute on your web part property. There are two ways to make use of the attribute, the first way is to explicitly define the page which you want to load (i.e. change ‘url to page’): [System.Web.UI.WebControls.WebParts.WebBrowsable(true), System.Web.UI.WebControls.WebParts.WebDisplayName("Template"), System.Web.UI.WebControls.WebParts.WebDescription(""), System.Web.UI.WebControls.WebParts.Personalizable( System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.ComponentModel.Category("Settings"), System.ComponentModel.DefaultValue("") ] [HtmlDesignerAttribute("url to page", DialogFeatures = "dialogHeight:500px;dialogWidth:650px;help:no;status:no;resizable:yes")] public string CustomProp { get { return customProp; } set { customProp = value; } } Notice that you could also modify the parameters that get passed into the JavaScript popup window creation script, in the case above I have made the popup window larger than the default size. The problem with the above approach is that the URL that gets passed as the first parameter to the HtmlDesignerAttribute must be a constant value, since it’s used in the attribute declaration. However Microsoft have provided us with a nice way to change this behaviour. [System.Web.UI.WebControls.WebParts.WebBrowsable(true), System.Web.UI.WebControls.WebParts.WebDisplayName("Template"), System.Web.UI.WebControls.WebParts.WebDescription(""), System.Web.UI.WebControls.WebParts.Personalizable( System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.ComponentModel.Category("Settings"), System.ComponentModel.DefaultValue("") ] [HtmlDesignerAttribute(BrowserBuilderType.Dynamic, DialogFeatures = dialogHeight:500px;dialogWidth:650px;help:no;status:no;resizable:yes")] public string CustomProp { get { return customProp; } set { customProp = value; } } I’ve now added the BrowserBuilderType.Dynamic option. To get this to work, we now need to override the GetCustomBuilder method of your web part: protected override string GetCustomBuilder(stringpropertyName) { if(propertyName == "CustomProp") { return"url.aspx?"+ "some custom params"; } return base.GetCustomBuilder(propertyName); } Now every property that has BrowserBuilderType.Dynamic passed into the HtmlDesignerAttribute will get passed into the GetCustomBuilder method, this gives you the chance to create a URL that passes parameters to your custom builder page. Ok, so now you got the web part covered, what about the JavaScript that needs to run on your custom property builder page? The best thing to do is to look at the current zoomdldr.aspx page, when the page loads you can get the current arguments from: window.dialogArguments Then to save the arguments, save the string value back to: window.returnValue Now that you’ve got a fair idea about creating custom property builders, you can create builders that are specific to you needs, below is a screenshot of a property builder form that lets the user enter some c# code that gets compiled an injected into the web part, the need was to provide a nice interface that the end user (i.e. a programmer) could use. The property builder uses the EditArea control to format the code nicely. The user can then click the compile button which will do a compilation of the code and report any errors. I’ve also added some JavaScript code to get around the removal of the carriage return characters, which was a major pain point for us. This approach will give you the power to create property builders that more closely suit your needs and goals.
A little while ago, I was working with a customer who wanted to have the ability to write some annotations on a graph that was presented in a SharePoint site. The idea was that the graph was quite complex and it would help the end user understand it more if it was annotated by a domain expert. So I put together a little proof of concept web part that makes use of all that JQuery goodness. I figured that it didn’t matter if you were annotating complex graphs, or tagging someone’s face like the way flickr and facebook does, the concept was the same: So like I said the code is proof of concept, not production ready, but I thought I’d post it here anyway, if only to inspire ideas. I’ve stored the data in a list with the schema of Title: default setting (single line of text) JSON: single line of text Or: The data will look like this once some annotations have been added: So I’ve just stored the JSON serialised object as is. The web part will iterate over each item on that list and pump that JSON into some JavaScript that is written to the page, so when the page load’s it will insatiate a JavaScript array of annotations and then render them. Which looks something like: notes = [{ "x1": "11", "y1": "28", "height": "91", "width": "73", "note": "This is bill", "imgID": "0"}, { "x1": "86", "y1": "54", "height": "81", "width": "95", "note": "This is Kate", "imgID": "0"}]; You might ask why I have the imgID property, this is used if more than one image on the page is annotated. To have a play around with the web part, you can download the WSP here, you just need to deploy the solution in the normal manner (stsadm –o addsolution –filename <path to httpcode.imageannotate.wsp> Next step is to activate the Image Annotation web part feature on the site: Add the web part as normal from the web part menu: Once the web part is on the page, you need to set List setting to the SharePoint list that will store the JSON data (this list must conform to the schema above): The client ID is the most technical part, this is a selector that gets used directly by JQuery, so any valid JQuery selector will work. A couple of examples: This will find the default image on the homepage of a standard collabration portal: img[src*='/PublishingImages/newsarticleimage.jpg'] All the images produced by a Dundas chart: img[src*='SharePointChartAxd.axd'] Hopefully you get the idea. I haven’t added any way to delete the annotation from the image itself, rather you need to go to the custom list and delete the item from there. The web part is smart enough to not allow users who can’t add items to the list. Credit should also go to odyniec.net for the imgAreaSelect JQuery plugin which I’ve made use of. The web part injects lots of JavaScript into the page, so the code might look a little messy because of this, but the basic premise is pretty straight forward. The source code can be found here. It includes a HttpHandler (ImageAnnotate.ashx) for handling all the ajax calls from the client side script. Remember this is just some sample code, use it at your own risk.
If you are using the BDC in your MOSS application, you may have noticed that the AUDEvent table in the SSP database can grow quite large and quickly. You might think that the stsadm command –o trimauditlog would do the trick in reducing this table, but in fact it does not work for the SSP database only content databases. You can specify the audit setting in the Application Definition File. Simply add the Property ‘Audit’ to your Entity’s Properties section: <Entities> <Entity EstimatedInstanceCount="0" Name="dbo.Sales"> <Properties> <Property Name="Audit" Type="System.Boolean">false</Property> </Properties> … The setting of false will prevent any audit entries being written to the AUDEvent table.
Yesterday I presented at the Brisbane SharePoint User Group, my topic was Using the Business Data Catalog with User Profiles. It was loosely based on this screencast I did last year. The entire presentation was recorded using Live Meeting, it can be viewed by going to: https://www112.livemeeting.com/cc/microsoft/viewRecordings and use the Recording Code: 7B8B6D I’ve put the PowerPoint presentation for my talk here.
A while ago I wrote about a simple method to give web parts an easy to configure design experience. The method I came up with made use of the ASP.NET Page method ParseControl. In my original example, the developer had to do some manual work like finding the templated control once it was added to the page, this was a tedious and ugly design. At the time it was more of an idea rather than something that I had build seriously, but recently I’ve put a bit more thought into the idea of web parts with a configurable UI template. I’ve built on the original example, but with the goal of abstracting the ParseControl and ControlBinding implementation. This example simply writes a title and description: We want to give end users the ability to change the way this title and description look without any web part code changes. This is done via a template, which is a property of the web part: Notice this is ASP.NET syntax The template can be easily changed: To produce a different rendering, without the need for any developer assistance: From a developers point of view, we really want to just declare some variables and be able to use them as if they were part of the web part, without thinking about FindControl or any implementation detail. I put together a base web part that looks for any private variables with a custom attribute called ControlBinding and via reflection sets this variable to the control instance specified in the attribute, so for the above example the code for the web part would look like: public class TemplateTest : WebPartTemplateBase { [ControlBinding("titleLabel")] Label titleLabel = null;
[ControlBinding("descriptionLabel")] Label descriptionLabel = null;
protected override void OnPreRender(EventArgs e) {
if (titleLabel != null) { titleLabel.Text = "Some text set from code"; }
if (descriptionLabel != null) { descriptionLabel.Text = "some text description that was set in code"; } base.OnPreRender(e); } } So now all the developer needs to do is add an attribute to private variables with the name of the templated control they want to obtain an instance to. This fits very much with the code behind / partial class philosophy that web developers are used to working with. If we look at some more complex scenario’s we start finding a few little issues, for example lets say that we want to use a GridView to do some data binding, in a normal aspx page we would use code nuggets to do the data binding: <asp:GridView ID="GridView1" runat="server"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:label id="dataItemLabel" runat="server" Text='<%DataBinder.Eval(Container, "SomeValue"%>'></asp:label> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> If we used this in our template we would find that it doesn’t work. There is however a work around: We can subscribe to the RowDataBound event and we can find the controls we wish to data bind to: [ControlBinding("GridView1")] GridView gridView = null;
protected override voidOnPreRender(EventArgs e) { if(gridView != null) { gridView.RowDataBound += newGridViewRowEventHandler(gridView_RowDataBound); gridView.DataSource = GetData(); gridView.DataBind(); } base.OnPreRender(e); }
void gridView_RowDataBound(objectsender, GridViewRowEventArgs e) { Label itemLabel = e.Row.FindControl("dataItemLabel") as Label;
if(itemLabel != null) { itemLabel.Text = e.Row.DataItem.ToString(); //cast and use the data item } } In which case the template would look like: <asp:GridView ID="GridView1" runat="server"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:label id="dataItemLabel" runat="server"></asp:label> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> I’m still in the early stages of experimenting with this pattern, so it could be the case that it’s totally unsuitable for some scenario’s. If your interested in exploring this pattern a little more, you can download the code to the base class here.
If your coming to SharePoint from a non .NET developer world, the web.config file might be a confusing and daunting file. I’ve broken out the major sections that any SharePoint administrator or newbie developer should know: CallStack: By default SharePoint will display a nice friendly error message: But this will often be a vague message that isn’t really helpful in troubleshooting the real problem. To enable a detailed error message, find the following line in your web.config file: <SafeMode MaxControls="200" CallStack="false" DirectFileDependencies="10" TotalFileDependencies="50" AllowPageLevelTrace="false"> Then change the CallStack="false" to CallStack="true" This should also be coupled with another change in the web.config file: Custom Errors: The default will be to show custom errors, this will hide the ‘yellow screen’ that is the ASP.NET error reporting page: <customErrors mode="On" /> By changing mode="On" to mode="Off" The yellow screen will be enabled, which should provide a more detailed report of the error. Safe Controls: SharePoint has measures in place that ensures that only trusted controls are used throughout the system. SharePoint will only load controls that have a SafeControl entry in the web.config file. Normally the SharePoint solution will write these entries in the web.config file, which is under administrative control (because an administrator is deploying it). But often a developer won’t worry about packaging up a control if they are still developing, so they need to manually add a control to the Safe Controls section of the web.config file, a typical entry looks like: <SafeControl Assembly="Microsoft.SharePoint.Portal, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.Portal.WebControls.Alerts" TypeName="*" Safe="True" /> Blob cache: A SharePoint administrator should be aware of the next setting, because it can increase the performance of your site. The idea is that SharePoint will write any images, CSS or JS to a folder on disk rather than requesting the file from the content database. The following entry should be modified to enable the blob cache (more info here). <BlobCache location="C:\blobCache" path="\.(gif|jpg|png|css|js)$" maxSize="10" enabled="false" /> To enable the blob cache, change enabled="false" to enabled="true" and make sure that the location exists. Trust level: Another important concept that new developers need to come to terms with is that by default SharePoint will run under a custom trust level called ‘WSS_Minimal’. This means that they can’t deploy assemblies into the ‘bin’ directory, with the ‘WSS_Minimal’ setting they would need to deploy to the GAC. Normally in a development environment where the developer is constantly changing and testing the code, they will want to change the following line: <trust level="WSS_Minimal" originUrl="" /> to: <trust level="Full" originUrl="" /> Proxy Settings: Often in a corporate environment a proxy server will sit between your SharePoint farm and the internet. So operations like retriving external RSS feeds will not work with some configuration changes. The web.config file can be used to store the proxy information, by default the section will look like: <system.net> <defaultProxy> <proxy autoDetect="true" /> </defaultProxy> </system.net> to add a new proxy: <system.net> <defaultProxy> <proxy useSystemDefault="false" proxyaddress="http://proxy" bypassonlocal="false"/> </defaultProxy> </system.net> Hopefully now the web.config file isn’t such a mystery for the new SharePoint administrator or developer.
One of my favourite Dependency Injection (DI) containers is Jeremy Miller’s StructureMap, it’s full featured and light weight, but what I really like the most is the fluent configuration API which means we can write code like this: ForRequestedType<INumber>() .TheDefaultIsConcreteType<Number>();
I also like that you don’t need to put custom attributes in your code. It’s all pretty abstract so lets assume we have a simple interface, I’m deliberately keeping it simple: public interface INumber { int GetNumber(); } A simple implementation of this interface: public class Number : INumber { public int GetNumber() { return 999; } } I’ve built a really simple (read: don’t use this in anything other than an experiment, the DI container in SharePoint gets configured for every request, including for images and scripts). This handler calls StructureMapConfiguration.AddRegistry with a class that derives from Registry which contains our fluent configuration information. Why have I implemented this as a HTTPHandler? If you read the Guidance on How to enable Unity in a SharePoint Application (unity is Microsoft’s DI container), the first step is to modify the Global.asax file. I have a problem with doing this for the simple reason that your touching the SharePoint system files, any upgrade will likely cause problems. It really is a shame that you can’t access the application onstart event using another mechanism. I most certainly agree that the application onstart event is the perfect place to setup your DI container and if your happy to modify and deploy your changes to a global.asax file then go right ahead, put the code below into this file. So it got me thinking, would a DI container like StructureMap work if it was setup in the begin request method of a HttpHandler? so my test code is: namespace DIHttpModule { public class DIHttpModule : IHttpModule { public void Dispose() { }
public void Init(HttpApplication context) { context.BeginRequest += new EventHandler(context_BeginRequest); }
void context_BeginRequest(object sender, EventArgs e) { StructureMapConfiguration.AddRegistry(new DIServiceRegistry());
INumber numberGen = ObjectFactory.GetInstance<INumber>();
((HttpApplication)sender).Response.Write("Number: " + numberGen.GetNumber().ToString()); } }
public class DIServiceRegistry : Registry { protected override void configure() { ForRequestedType<INumber>() .TheDefaultIsConcreteType<Number>(); } } } So after I deploy and configure my HttpHandler I do in fact see the number ‘999’ display at the top of each page (it also breaks JavaScript etc. because it writes this value into every request) What has happened is that the call ObjectFactory.GetInstance<INumber>() has created an instance of Number(), we didn’t have to explicitly new up a Number() object. That is a pretty basic example, lets expand it a little: public class NumberPrinter { private INumber _number;
public NumberPrinter(INumber number) { _number = number; }
public string PrintNumber() { return _number.GetNumber().ToString(); } } Notice the constructor, it takes an INumber interface, the constructor is special as far as StructureMap is concerned, if StructureMap is asked to create an instance of NumberPrinter it will see that the constructor takes an INumber parameter and will attempt to pass in an instance of that type. So assuming we changed the HttpModule to the following code: NumberPrinter printer = ObjectFactory.GetInstance<NumberPrinter>(); ((HttpApplication)sender).Response.Write(" Number : " + printer.PrintNumber()); We can see that again our output is ‘999’, StuctureMap was smart enough to create an Instance of INumber and pass it in, if you think about it, that’s pretty cool. It means that you can replace any concrete Instance of INumber with a single configuration change, that is powerful when we want to test our object in isolation, which is a whole other topic. That’s a pretty trivial example, we did the StructureMap configuration and called ObjectFactory.GetInstance in the same method, now lets try it out in a web part which will be invoked later in the page life cycle (also remove the response.write from the HttpModule): public class NumberWebPart : System.Web.UI.WebControls.WebParts.WebPart { protected override void RenderContents(System.Web.UI.HtmlTextWriter writer) { NumberPrinter printer = ObjectFactory.GetInstance<NumberPrinter>(); writer.Write("Web Part: " + printer.PrintNumber()); } } Running this web part gives the expected result: Again this is a really trivial example, that’s the point, you can clearly see that we are using a DI tool called StructureMap to create instances of our objects. Sure you could do away with the HttpHandler and move the configuration into the web.config file. But I do think the HttpHandler method warrants more thought however, because its useful when you introduce NHibernate to the mix, which is another framework that needs to do some expensive start up, which is also suited to the global.asax methods, but it does have some characteristics that should be managed per request, but that’s another post. For now, hopefully I’ve proved some thought around DI and how can we manage the SharePoint deployments that really yell out for Application OnStart events without modifying the system file.
I came across an interesting problem today, the MySite link was incorrect, it was working the previous day. The first step I took was to look at the SSP, which showed that the correct My Site location was entered. The second step was to look at the Alternative Access Mappings, again they looked OK. By now I suspected that the ISA server was doing something funny, we had asked the network guys to publish a new rule, so I had a look at the rule settings and found the culprit: Notice the ‘Apply link translation to this rule’ checkbox, the ISA server was looking at the HTML returned by MOSS and changing the MySite entry, it’s actually a pretty cool feature of ISA that is handy if you are accessing your web application from the internet (to your intranet). But in my case it wasn’t needed and incorrectly setup. I thought it was an interesting problem, by logically stepping through each potential cause and eliminating it, the root cause was discovered pretty quickly.
A new whitepaper has been published on MSDN: Analyzing SharePoint Usage This whitepaper covers in detail the process of using LogParser to deeply analyse the logs produced by SharePoint. Some of the topics covered include: - Using LogParser
- Counting users
- User type distribution
- Requests (RPS) distribution over time
- Distinct users over time
- User agent distribution
- Slow pages
- Offlice web service usage
A lot of this information applies to any web application, not just SharePoint sites.
The following code will create a site based on a template, then it will add a new contributor group: //create the subsite .. subSite is an SPWeb object
SPWeb createdWeb = subSite.Webs.Add("Url", "Title", "Project Subsite", 1033, "Template Name", true, false);
createdWeb.BreakRoleInheritance(true);
SPMember member = createdWeb.Users[createdWeb.Author.LoginName];
//create the user groups ...
createdWeb.SiteGroups.Add(createdWeb.Title + " Contributors", member, createdWeb.Author, "Contributors to the site");
SPGroup newContribGroup = createdWeb.SiteGroups[createdWeb.Title + " Contributors"];
SPRoleDefinition contribRole = createdWeb.RoleDefinitions.GetByType(SPRoleType.Contributor);
SPRoleAssignment contribRoleAssignment = new SPRoleAssignment(newContribGroup);
contribRoleAssignment.RoleDefinitionBindings.Add(contribRole);
createdWeb.RoleAssignments.Add(contribRoleAssignment);
The above code will break the permission inheritance, so the created site will have unique permissions, this code could be refactored to also create Owners and Reader’s groups.
I was playing around with setting up the navigation created by a legacy custom site definition. Information posted for my reference more than anything else: <WebFeatures> <Feature ID="541F5F57-C847-4e16-B59A-B31E90E6F9EA"> <Properties xmlns="http://schemas.microsoft.com/sharepoint/"> <Property Key="InheritGlobalNavigation" Value="true"/> <Property Key="IncludeSubSites" Value="true"/> <Property Key="IncludePages" Value="false"/> <Property Key="InheritCurrentNavigation" Value="false"/> </Properties> </Feature> </WebFeatures>
Some of the settable properties are:
The boolean properties:
- IncludeInGlobalNavigation
- IncludeInCurrentNavigation
- InheritGlobalNavigation
- InheritCurrentNavigation
- ShowSiblings
- IncludeSubSites
- IncludePages
- SortAscending
The next properties take an enum:
- OrderingMethod - Automatic, ManualWithAutomaticPageSorting, Manual
- AutomaticSortingMathod - Title, CreatedDate, LastModifiedDate
If your interested in seeing how Commerce Server and SharePoint can be integrated, you might want to take a look at the December 2008 CTP of Commerce Server 2009: The bullet point of interest is: - Complete out-of-the-box e-commerce shopping site in SharePoint with new search functionality, new shopping features, and what-you-see-is-what-you-get (WYSIWYG) content management and design experiences. This helps to facilitate rapid assembly and maintenance of e-commerce Web sites by business users and creative professionals.
For more information about this upcoming release, the PDC session is a good place to start.
I’ve been working with a client this week getting Kerberos working on their SharePoint farm. As you would expect I spent most of my time working out what SPN’s needed to be created. I created a simple little tool that will help you sort out the basic SPN’s: SharePoint Kerberos SPN Creation Tool The idea is that you enter the details about your farm and the tool will generate the SPN’s that you need to create (you can copy / paste from this site into your console window). The client I was working with had a much more complex farm that included reporting services, analysis services and proxy servers that all needed SPN’s, this tool doesn’t cover those types of farms, but it will help you get the base portal services up and running, then you can work out what SPN’s to create in order to get the rest of the services functioning. Don’t forget that you’ll also need to set up delegation for each of the accounts. By far the best SharePoint Kerberos reference is: http://blogs.msdn.com/martinkearn/archive/2007/04/23/configuring-kerberos-for-sharepoint-2007-part-1-base-configuration-for-sharepoint.aspx
I’ve written a web part that can help you insert JavaScript into a SharePoint page. Currently there is nothing stopping you using a content editor web part, but it has a few limitations. First is the fact that the JavaScript doesn’t stand out, people may think that the content inside the editor is blank, when in fact it contains JavaScript. By having a dedicated web part for JavaScript it becomes clearer that JavaScript lives on the page, also we can add a few features that make working with JavaScript a little easier. I’ve made the chrome state set to None by default, so you won’t see the web part at all during normal render time (only design time). The properties: Page load JavaScript: This can be any JavaScript that you want to run when JQuery loads, that is any code you want to live inside of: $(document).ready(function(){});
Something cool to try out (from EndUserSharePoint) try adding: $('#LeftNavigationAreaCell').toggle(); This will remove the left hand navigation.
Page level JavaScript: This is JavaScript that you just want to live on the page, it could be globally scoped variables or some functions that you have defined.
Script Includes: Each new line can be the URL to a JavaScript file to be included in the page, this is particularly useful for including JQuery plugins.
Use Google Libraries: Just a little novelty, it will use the Google Ajax API’s to load JQuery instead of the embedded JQuery resource.
You can have multiple web parts on the same page, the best bit about this is that all the code will be output into one place, so if you have one web part with some page load JavaScript that has say: alert(‘load’); and anther that includes the left nav cell hide from above, the result in the page would be:
$(document).ready(function(){alert’load’); $('#LeftNavigationAreaCell').toggle(); });
After you deploy the solution, be sure to activate the feature under ‘site features’:
You can download the solution package from here.
A while ago I wrote a little tag cloud web part, I’ve updated that web part so that it has its own solution package and can be used stand alone. After you install and deploy the solution, make sure you activate the tag cloud feature in ‘site features’ Lets take the example of adding tags to a standard events calendar. First add a new column named ‘tags’ to the events list. Add the tag cloud web part to the page and set the following properties: You can specify the link that each tag will link to as well. By default it will link to the search center and try to search on the metadata property of the tag field. i.e: "/SearchCenter/Pages/Results.aspx?k={tagfield}:{tag}", using this format however you could link to any page and then maybe use a query string filter web part to pull the tag from the url. The final web part looks like: You can download the solution from here.
This week I ran into an interesting problem. When adding a workflow to a content type we saw the following error: Unable to validate data. at System.Web.Configuration.MachineKeySection.GetDecodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Int32& dataLength) at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString) I did the normal thing at searched on Google, only to find this KB (http://support.microsoft.com/kb/928028) which describes the error message, it gives the solution as ensuring that SharePoint is installed with the same path on each of the server. This seemed odd, since all our servers have a standard build. The error message indicated that the view state has been modified between postbacks, so it was back to the drawing board looking for a solution that involved some change that could possibly have some effect on the view state. After talking with the designer it turned out that he had made some changes to the application.master page. We replaced the altered file with a backed up version of the application.master and sure enough the workflow could be added without any issues. After a bit more experimentation it turned out that the problem was with this: <SharePoint:DelegateControl ID="MyDelegateControl" runat="server" controlId="SmallSearchInputBox" /> It seems that the small search box alters the view state in some way to cause it not to validate on postback. Hopefully this will save someone else the hassle of this error.
The latest patch Tuesday includes the following for SharePoint: Executive Summary This security update resolves a privately reported vulnerability. The vulnerability could allow elevation of privilege if an attacker bypasses authentication by browsing to an administrative URL on a SharePoint site. A successful attack leading to elevation of privilege could result in denial of service or information disclosure. You should seriously consider this patch and apply it.
I was recently asked a question around this scenario: If you log into a SharePoint Portal by using the ‘Sign in as Different User Button’ as User2 it works fine, however when you click on the ‘MySite’ link it will show the MySite of User1 not User2 as you would expect.  I should also point out that the MySite and Portal are running in separate web applications (this should give away the answer). So what is happening here is that when you click on the MySite link, this web applications asks the browser for the users authentication details, since it is a different URL (i.e. because its running in a separate web application, it will be a subdomain or even a different domain name) the browser will forward the logged in users credentials i.e. User1 (provided that it is in the local intranet zone), the browser will not keep User2’s credentials and will not forward them on. So the end effect is the scenario described above which may seem odd to the end user. The only way to get around this is to user the ‘RunAs’ command from windows and run the browser process as User2. What we’ve normally found is that the ‘Sign in as Different User’ option is normally only used by power users, these people will generally understand the problem if you explain it to them.
A couple of random tips for using lists inside of SharePoint: - Make use of the RSS features of Lists – Each document library publishes an RSS feed which really provides the user with a wealth of options, there is no site collection limits on how that feed is consumed, so it’s possible to use in their MySites etc. If you have Kerberos setup correctly it is possible that other systems can make use of the feed. RSS is a good option to supplement the alerts that a list can send, often users won’t want their email cluttered with alerts.
- Alerts – Granularity is the key to successful alerts, a user can be alerted when a specific view of a list changes. This can make the alert more useful to the end user, we don’t want to spam them.
- Folder Level Permissions beware – The ability to create folder-level and document level security permissions can really cause some headaches for new users and administrators. The feature is really powerful, but can also create complex problems that can be hard to solve. There has been lots of discussion around the need for folders in a document library, I think there is value to be gained from them personally.
- Picture Libraries – It should be noted that the picture libraries have limited support for thumbnail view, it’s not a major limitation but it’s likely to come up in discussions / training with new users.
At Tech-Ed Barcelona, the new SharePoint related features of Visual Studio 2010 were presented in the keynote: Taken from Paul Andrew’s Blog Server Explorer for SharePoint viewing Lists and other artefacts in SharePoint directly inside of Visual Studio Windows SharePoint Services Project (WSP file) Import to create a new solution Added a new web part project item and showed the Visual web part designer which loads a user control as a web part for SharePoint Showed adding an event receiver for SharePoint and using the wizard to choose the event receiver and to just create a source file with that event receiver. Added an ASPX workflow initiation form to a workflow project and showed how this workflow initiation form has designer capability Showed the packaging explorer and the packaging editor which lets you structure the SharePoint features and WSP file that is created I’ve highlighted the features that I think are exciting, it’s good to see that more tooling support is coming.
If your about to go onto a new client’s site to install MOSS or WSS, these are some of the things that need to be considered:
- Base windows install – Hopefully this step will be done for you, as well as any clustering setup either via a load balancer or via NLB.
- Network Access to Servers – Will we have machines that are on the same network as the servers, I’ve been given machines in a test lab that was impossible to use without the VMWare infrastructure client, needless to say that environment didn’t last very long.
- Admin Rights – We need accounts with admin rights to actually install SharePoint
- Active Directory Setup – It’s always a good idea to understand how the organisation is using AD.
- Service Accounts – Make sure the IT department understand the importance of service accounts, hopefully have them pre-created before you get onsite.
- DNS – While you put the request in for the service accounts, double check that your DNS entries are all setup. Have a think about all the zones you intend to use (i.e. are you using web application policy to give admin users god like rights on the admin.portal site?)
- Database – Make sure your setup account has permissions to create databases, you’ll be surprised at how often the DBA’s forget this task.
Remember always to select the complete install, never the web front end only option, you always want the option to change the servers configuration (i.e. start the search server).
In both MOSS and Search Server it is possible to configure an xml file that controls expansion and replacement words, so in effect this is a thesaurus file that you can customise with words that may be specific to your organisation. The classic example given is one which expands the technology acronyms (computer types sure do like them!): 1: <XML ID="Microsoft Search Thesaurus"> 2: <thesaurus xmlns="x-schema:tsSchema.xml"> 3: <diacritics_sensitive>0</diacritics_sensitive> 4: <expansion> 5: <sub>Internet Explorer</sub> 6: <sub>IE</sub> 7: <sub>IE5</sub> 8: </expansion> 9: <replacement> 10: <pat>NT5</pat> 11: <pat>W2K</pat> 12: <sub>Windows 2000</sub> 13: </replacement> 14: </thesaurus>
To find the location of this file you first need to look in the registry under: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12.0\Search\Global\Gathering Manager] Key: DefaultApplicationsPath
Once you have found the file you can simply add expansion and replacement elements with the children nodes as needed.
Don’t forget to do this to all the servers in your farm. Also you will need to restart the search service for these changes to take effect.
The following table (from the enterprise search blog) lists the elements and what they do:
| Term |
Meaning |
| thesaurus |
marks beginning (and end) of thesaurus |
| diacritics_sensitive |
Diacritics are marks, such as accents that are added to letters that change their pronunciation. For example, the acute accent over and e gives you: é. 0 – ignore diacritics 1 – respect diacritics |
| expansion |
A list of alternative forms each marked by <sub> by the sub keyword |
| sub |
One of several alternatives in an expansion |
| replacement |
Several patterns will be replaced with a substitution |
| pat |
A pattern to be replaced |
| sub |
Item to be substituted |
I’ve only scratched the surface here, for a full overview of this topic, check out the enterprise search blog
Don’t forget that SQL Server full text search (FTS) has the same capabilities in terms of a thesaurus file that supports expansion and replacement words. If your looking for information on SQL Server 2005 or 2008 you should refer to this post.
The other feature that both products support is the notion of noise words, these are words that when used add no value to the search like ‘been’, ‘before’, ‘being’, ‘both’ etc. This KB describes the process to add or remove words from this list, but simply it is modifying the contents of a file which lives in: Data\Ftdata\SharePointPortalServer\Config, it’s a simple format where each word is on it’s own line (no xml).
Have fun customising the search experience.
From an MSDN article, I’ve found this concise procedure to setup an indexing server: Enable or disable the index server role or query server role -
In Central Administration, on the Operations tab, in the Topology and Services section, click Services on server. -
On the Services on Server page: -
If the server name that appears is not the server that you want to configure, click the arrow next to the server name, click Change Server, and then click the server for which you want to enable or disable the index server role or query server role. -
In the Start services in the table below section, in the Status column for Office SharePoint Server Search, if the status is Stopped, in the Action column click Start. -
Click Office SharePoint Server Search. -
On the Configure Office SharePoint Server Search Service Settings page, in the Query and Indexing section, enable server roles for the server as appropriate for your configuration: -
To enable the index server role on the computer, select Use this server for indexing content. -
To enable the query server role on the computer, select Use this server for serving search queries. -
To save changes and return to the Services on Server page, click OK.
With little effort you can make your site a little more search friendly, it’s possible to embed some xml into your site which your browser can use in its search box. Most modern browsers support OpenSearch, which is what this xml fragment is using. The first step is to create the following xml, but replace the bits that are specific to your site: 1: <?xml version="1.0" encoding="UTF-8"?> 2: <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> 3: <ShortName>SharePoint Search</ShortName> 4: <Description>Search for SharePoint</Description> 5: <Url type="text/html" method="get" template="http://YourSite/SearchCenter/results.aspx?k={searchTerms}"/> 6: <Image width="16" height="16">http://YourSite/favicon.png</Image> 7: <InputEncoding>UTF-8</InputEncoding> 8: <SearchForm>http://YourSite/SearchCenter/</SearchForm> 9: </OpenSearchDescription>
In the master page, you will need to refer to this xml file:
1: <link rel="search" type="application/opensearchdescription+xml" href="/Style%20Library/OpenSearch.xml" title="SharePoint Search">
Finally you can drop down the search provider box in your browser to select your new search provider, there is no need to browse to the search centre again.
Little things like this can help entrench searching as the primary navigation method in an organisation.
You may have noticed in your coding adventures that some column names have had the spaces replaced with _x0020_. To understand why this is happening a little background is needed, a site column has two types of ID’s, the first is a unique name and the second is a unique ID. The ID is a GUID, however the unique name has two parts, the internal name and the display name, it is the internal name that has it’s space characters replaced with the hex value of the HTML space character: %20. Now that you know why this happens, the way to prevent the _x0020_ characters using the web UI is to firstly create the column without any spaces (this will create the ID and the unique name), then after it has been created, you can go back and rename it (this will just updated the display name).
I read with interest Arpan’s article on Finding and Growing Great SharePoint Talent which covers the topic’s of developing and keeping great SharePoint talent in a broader context. I’m mostly concerned with the developer role, so I’ll focus on that. The Interview The thing to extract here is their level of exposure to ASP.NET, depending on what role you want them to take in your SharePoint development you would at least want them to have a good grasp of ASP.NET. You really don’t want to spend time training them up on concepts that are generally related to ASP.NET development. The next thing you should look for is their attitude towards SharePoint administration, the key to becoming an awesome SharePoint developer is knowing the product, if you want a developer to just develop some web parts then having a deep understanding of the product might not be needed, but if your looking for someone to tackle a wide range of SharePoint development tasks they need a broad understanding. I mean how can you develop a personalised web part if they can’t setup a development environment properly? or how can they build a feature or solution if they have no idea how they are used. Some developers will cringe at the thought of admin / setup work, but the fact is, it’s the best way to become familiar with the product. On the Job - Day One So you’ve picked someone who is going to become your superstar SharePoint developer, where do you start now? Do you leave them alone with a stack of books and training material? or do you throw some code at them and say start typing? There is no correct answer, each developer learns in a different way, you just have to work out what that way is. It’s probably not a huge stretch to start with the familiar topics like web parts but only after the developer has in fact used the SharePoint UI. As time passes Once the developer has done some admin and bits of development, they will start to speak and understand the SharePoint vocabulary, don’t expect them to instantly know the answer to all your development questions, even the seasoned developers need time to investigate the solutions, SharePoint is such a large product it’s not an easy task to know everything, but once you digest the core concepts a lot of stuff can be inferred and it gets easier to grasp new SharePoint related ideas. You’ll probably find that your administrators look to your developers at the first sign of trouble, which is another reason why your developers should experience the SharePoint world from an administrative perspective. So in summary, I think ASP.NET skills provide the best leg up to SharePoint development combine that with someone prepared to sit and learn some administrative / setup tasks and you have the makings of a great SharePoint developer.
Scott Hanselman recently posted about the IIS 7 Rewrite module and how easy it is to setup and use, he covers the topic extensively, I just wanted to point out that this tool will also work with SharePoint. I was recently in a position where the marketing guys had decided that the URL should be the promotion name of the marketing campaign, but it was both impractical and impossible (the URL had to point to a survey) to create a page in SharePoint that would suit their needs. Since the site was running IIS 7 we used the IIS 7 Rewrite module. For example: Say the requested URL was: http://server/marketing/promo/ But we needed to post to: http://server/Lists/Marketing Survey 1/overview.aspx The first step after installing the rewrite module is to create a ‘Rule with rewrite map’: Then choose the rule action type, in this case it is a rewrite action, give it a meaningful name: Then add the mapping (right click in the panel that gets opened after the last step), the Original value is the URL you want your users to navigate to: That’s it your done, now you can browse to your URL and your users will end up in the correct location: 
Some of the major web 2.0 sites like facebook and twitter are providing REST based API’s to interact with their services, if your not familiar with Representational State Transfer (REST), then now might be the perfect time for you to come up to speed because it seems that Microsoft is planning on incorporating more REST features in .NET 4, we already have some REST support in the current generation of tools. The basic idea of REST is to use the existing infrastructure that the web already provides, we have HTTP verbs such as GET, POST, DELETE and PUT, which map pretty well to the CRUD model we deal with often. The web provides us with a simple error handling model, we all understand 404, 501 and 403 error codes (file not found, server error, unauthorised access). The final building block is the URL itself, we can create descriptive URL’s such that any user can infer it’s intent just by looking at it, while some may disagree with my summation, I think all of these elements together define what REST is. If we contrast all of this with the world of SOAP and webservices, we soon realise that the webservices/SOAP model has been over-engineered. Do we really need to re-invent error codes and the calling conventions that already sit on top of the web, do we need all this SOAP overhead, one end-point for a raft of operations? Of course there will still be a place for traditional webservices, I’m not saying they don’t work or are going away, they clearly work and will be around for a while. The webforms model of ASP.NET hasn’t really made it easy to create REST services, it was never really designed for it, however the ASP.NET framework has made developing REST’ful services a lot easier. The first feature that the MVC framework offers is the flexibility of offering a URL routing engine, so that it is possible to easily create nice URL’s, but more than that, the way MVC uses the convention of {controller}/{action}/{parameter} you end up with code that without much effort becomes very REST like. The second feature that MVC offers is the way each action on a controller returns an ActionResult, the framework provides inherited result classes such as the JsonResult and ContentResult, in effect the one action can return multiple payloads of data (in this case either a JSON view or an XML view). While not strictly fitting to the REST definition, most modern REST API’s return data in a format that is most easiest to work with which is increasing becoming JSON for AJAX/Mashable operations. With this background we can now focus our attention on SharePoint, currently there are two ways to work with the data stored: - Web services
- Object Model
I’ve already touched on what the limitations of the web services are, the SharePoint object model really only concerns us if our code is working inside SharePoint (i.e. on the same machine/s as SharePoint). The Benefits to SharePoint of a REST API - Enterprise mash-ability – the web is becoming more and more useful, one reason is that its now easier to mash together applications, look at any application that makes use of virtual earth or Google earth, flickr, twitter, live services etc. Imagine having this flexibility in your organisation, if your organisation has really embraced SharePoint then most likely it contains data that could be mashed into other systems, this REST framework is about exposing that data more easily.
- It still fits with an organisations Service Oriented Architecture (SOA) in most cases. A lot of people feel strongly that SharePoint isn’t the single source of truth for data, that may be the case, but SharePoint is a tool that does aggregate data and adds value to it (a user might create a list with BDC data with additional columns describing some business function), this framework is designed to make this data more accessible to an organisation trying to construct some form of SOA.
The URL Format A REST API that sits over the object model would provide the best of both options, we could craft a simple URL that returned the items that we are after such as: http://mashserver/Site/ -- returns all sites http://mashserver/Site/all/ -- same as above http://mashserver/Site/get/{guid-id} -- returns the selected site http://mashserver/List/ --returns all lists that the user has permissions for http://mashserver/List/forsite/{site-guid-id} -- returns all the lists for the given site id http://mashserver/ListItems/get/{list-guid-id} -- returns all the items in the list, http://mashserver/BDC/ --Return all the BDC applications http://mashserver/Permission/site/{site-guid} --Return objects representing the security ACL’s on the given object The REST API should expand across all areas of the SharePoint system. Each action should be decorated with an accepted verb type attribute which MVC provides. The Return Values We could build a HybridResult that looks at the HTTP headers to determine the accepted input and then either return JSON or XML: In fact Omar has already provided a nice starting platform. This HybridResult can also be smart enough to return a 404 if the object we are trying to serialize is null. If we get any security exceptions we can set the return status code to 403, again the MVC provides a nice mechanism to support this via it’s Error Handling attribute. Problems - Do we build our own object model, reinvent the wheel?
I think the answer is yes, we can’t serialize the SharePoint object model to our needs as a REST API, in some cases we want to return properties in a form that would be easy to use from JavaScript. We often don’t want to return the whole object graph, if you ask for all the lists, you really don’t want to have a list object returned will a collection of list items. By using the new language features in .NET 3.5 we can build extension methods that provide a neat way to convert the existing Object Model entities to those created by the MVC framework. - Security – The double hop issue
Since the API is going to make heavy use of the object model, it’s going to have the same limitations as the object model, i.e. the API web sites will need to live on the same server as SharePoint unless the double hop authentication issue is taken care of via the use of Kerberos, which is a likely event in most larger organisation. Features - Thoughts? So what do you think? Is a REST API for SharePoint something that would be useful? I’ve already started developing some proof of concept prototypes which I’ve included on the project’s Codeplex site: http://www.codeplex.com/REST4SharePoint so please leave any feedback there. What features do you think it should include? What are your thoughts? What obvious thing have I missed?
By default SharePoint will create a content source called ‘Local Office SharePoint Server sites’ such as: This will contain the starting addresses of all the sites on your SharePoint server such as: Notice how it also includes the sps3://, this is the indexing of your user profiles. My tip is to remove the sps3:// link from the default content source and add it as a new content source on it’s own. The reasons why I think this is helpful: - By default you need to crawl all your other content just to update your user profile information.
- You can schedule your profile crawls at a time that suits your active directory imports
In any case it’s worth considering breaking the profile crawl into it’s own content source.
I’ve put together a screencast that demonstrates how to configure a web application so that it can host MOSS MySites. The basic steps are: - Create the web application
- Create a site collection to be hosted in the newly created web application, make sure this is based on the ‘My Site Host Template’
- Create a managed path for the MySites
- Change the MySite settings in the SSP central admin to reflect the URL and managed path that was setup in the previous steps
- Enable self service site creation so that new MySites are created when the user clicks on the My Site link
The high quality version can be found here or the youtube version:
One of the main features of SQL Server 2008 is database backup compression. In SQL 2008 you can now specify the backup as: BACKUP DATABASE ‘WSSDatabase’ TO DISK = ‘C:\WSSDatabase.bak’ WITH COMPRESSION; Or if your GUI inclined: This will make managing your content databases a little easier, especially if your already getting heckled by your SAN Administrator or the poor guy trying to backup those 150GB backups. The second feature is Transparent Data Encryption (TDE), this allows you to specify a key that is used for data encryption for example: ALTER DATABASE ‘WSSDatabase’ SET ENCRYPTION ON; The scenario where I think this feature works nicely is this; imagine your pay-role people want to store pay details, you could firstly ensure that the site collection they use is located in a nominated content database, then you could turn on the encryption and have another secured means of protecting your data. Further information can be found here. Those two features are pretty compelling, on all our new deployments we are recommending SQL 2008.
I’ve put together a screencast that shows how to use your data from an external source such as a SQL Server database and combine it with your user profiles. The screencast shows how to: - Import the application definition to setup the BDC application
- Setup a custom import connection of type business data catalog and how to set the key between active directory account and BDC data
- Setting up custom profile property section (the example is cricket statistics)
- Setting up custom profile properties that import data from the BDC data source
- Start a full import
- Perform a search which returns the user and show the new data that is displayed in the users profile page.
A good quality version of the screen cast can be found here (4 mins). Or the low quality youtube version:
The question was asked: How do I load up an XSLT from the style library like: 1: XslCompiledTransform xslTransform = new XslCompiledTransform(); 2: xslTransform.Load("/styles/file.xslt");
When I do this I get a (401) unauthorized exception
Answer
You need to use an XmlUrlResolver and populate the credentials property like:
1: XmlUrlResolver resolver = new XmlUrlResolver(); 2: resolver.Credentials = CredentialCache.DefaultCredentials; 3: 4: XsltSettings settings = new XsltSettings(true, true); 5: transform.Load(XSLTUrl, settings, resolver);
Normally I wouldn’t recommend bulk creating a MySites, but what if you were bringing ten thousand plus users onto your portal at a single time? Imagine if they all hit the My Site link within a small time frame, would your server melt? Maybe you would like to bulk create the MySites at a time that suits you to avoid this meltdown. The following code might help you out: 1: class Program 2: { 3: static void Main(string[] args) 4: { 5: using (SPSite site = new SPSite("http://my.sharepoint.url")) 6: { 7: ServerContext context = ServerContext.GetContext(site); 8: 9: //initialize user profile manager 10: UserProfileManager profileManager = new UserProfileManager(context); 11: 12: //this just creates a mysite for everyone in the profile database 13: foreach (UserProfile profile in profileManager) 14: { 15: Console.WriteLine("Creating a Personal Site for " + profile["PreferredName"] + "..."); 16: try 17: { 18: profile.CreatePersonalSite(); 19: Console.Write("Succcess!\n"); 20: 21: } 22: catch (PersonalSiteExistsException) 23: { 24: Console.Write("Site already exists!\n"); 25: } 26: } 27: } 28: } 29: }
It simply iterates through each user profile and calls the CreatePersonalSite() method on each user profile.
Of course the other big issue to consider is the impact of the content databases, these should all be created and some thought should be given as to the potential size of these databases. If you say have 10000 users each with 100MB Quota your potentially looking at a 100GB database, which might be a little hard to work with. So you should have an idea of the total number of users, the quota size of each MySite and then setup the content database limits. These values can be viewed from the ‘Content Databases’ Option under ‘SharePoint Web Application Management’ in the Application Management section of central admin:

The above page will let you control how your sites are distributed across your databases.
The SharePoint community have a number of useful tools that can aide you in your performance testing of MOSS. The tool that comes to mind is the SharePoint 2007 Test Data Population Tool, this tool can bulk load data into SharePoint. If you click on the Releases tab you will see a release: MOSS Performance Load Tests. This is a series of web tests that can be loaded up into visual studio team system:  The visual studio load testing features can simulate multiple users over a period of time all running a defined test mix. In the above image the homepage is hit 9% of the time, whereas the general PageView test which is a broad selection of pages makes up 28% of the test mix. Your test mix will depend on how you perceive your users using the site. Visual studio has a number of options that you can tweak such as: - Browser mix – provides a way to simulate multiple browser types i.e. mobile devices, firefox, IE etc ..
- Network mix – your test network might not be the same as production, this allows you to test with network limitations in place
- Load pattern – Constant, Step or Goal based, depending on what your testing, you could hammer your server with a constant load, or you could build up in a controlled manner.
- Warm up time – You can warm the server up to avoid the initial compile times from impacting your results.
In the example above I’m using the test mix from the Microsoft document: Estimate performance and capacity requirements for portal collaboration environments, you can find other documentation for different portal types here.
MOSS 2007 has the option to use a dedicated web front end server for crawling content:  Why would you want to do this: Advantages: - Search doesn’t compete with the end users – Large environments that need to crawl constantly can cause more traffic than normal user load, you don’t really want your users to experience slow pages just because your doing an index? By using a dedicated web front end that isn’t part of the load balanced cluster, your indexing won’t impact your users as much (I say as much, because you still need to think about the impacts of the database server).
- Easy to move the WFE into the load balanced cluster – It’s a rather crude disaster recovery method, but it’s not that hard to move this box into the load balanced cluster if you really need the extra capacity or if one of your other servers fail. After all it’s just a normal web front end, but one that is reserved for the indexer.
- Perfect place to run a backup central admin – You should always try to have more than one server running central admin (on a large farm anyway), that way if your main central admin server goes down, you still have a way to manage the farm.
Disadvantages: - More hardware – The obvious disadvantage to having an extra machine is the requirement of more hardware, which also means:
- More cost – New hardware is an additional cost, but now that you have an idea of the advantages it brings, you can make a more informed decision.
Joel has some other tips such as adding a robots.txt to servers that you don’t wish to participate in the indexing process.
The next integration method that I want to discuss is the collection of data. As a web developer you’ve no doubt been asked to create a form that does X a number of times, you might even have created some tools to aide in this, it’s pretty common. So now that you’ve moved across to the dark side of MOSS development we need to break your thinking, if you want to create a form, first have a look at Microsoft InfoPath, InfoPath files can be published to MOSS running the InfoPath Forms Service. The benefits to this approach are: - Less development – I won’t go as far to say that even the worst coder on your team could use this tool, that guy can’t do anything, but for simple stuff it’s not too hard to get your head around.
- Central management of forms – You can easily keep track of your forms, how many times have you written a form that has already been developed? But seriously because each form is a content type, you can manage the metadata of a form from a single location, regardless of how many libraries you’ve added the content type to.
- Can easily use existing MOSS features - This is the biggest feature, you can easily add workflow capabilities or publish your results to a list, I bet most of your forms currently just send an email and get written to a database.
The disadvantages are: - It requires a different way of thinking - This might not be a bad thing, but some large organisations have teams of programmers who spend 90% of their time doing some sort of forms based programming. It might be a hard sell for these guys.
- Anonymous access might cause some issues – I’ve seen a few issues around anonymous access to forms, it’s by no means a show stopper, but you should think about this before you deploy.
I think the benefits far out weigh the disadvantages, in any case hopefully the next time you hear a developer say they’ll create a web part for a form, you’ll think about it a little more carefully.
I previously posted about a simple integration pattern that used the Page Viewer web part. The approach I’m going to discuss today again does not involve any custom coding, but will have a huge impact on your end users. If your goal is to make your corporate data more available this is an easy option. I’ve posted about this diagram before:  This is the Business Data Catalog (BDC). It can source data from either a database or a web service, once you have built an Application Definition File (the only development work needed), your data can then reach custom lists, profile information, search and custom solutions. The benefits to this approach are: - Reach – You’d be hard pressed to develop a feature that has more end user impact. Firstly your data can be searched, secondly your data can now be used in ways that couldn’t be foreseen. This might be a scary prospect for a lot of organisations, but this will change over time, I plan to discuss this further in another post.
- Searchability – The younger generations have grown up with the web and expect to be able to do everything via search, you’ll probably find that your search engine is the most popular MOSS feature, why not plug more data into it?
- Fits with a SOA – Yes, it’s buzzword compliant, don’t forget that the BDC in MOSS can call web services, so if your organisation has a Service Oriented Architecture, this will likely be an easy sell.
The disadvantages are: - Doesn’t work well for complex data – It really only works well if your data is simple, for example if your trying to expose a product list or the locations of your stores.
- Read Only Data – Indexing and Searching are read only operations, as simple as that. If you want your users to rate your data or tag it, then your going to need a different strategy.
I’ve been doing some thinking around how MOSS can be integrated into a large and complex organisation, an organisation that has many disparate applications which act as an information silo. Most developers who are not intimately familar with MOSS will suggestion just using the page viewer web part if the external system is web based. The page viewer web part is basically an IFrame, so MOSS is in effect a window to the external application. The benefits to this approach are: - Low Cost – The external system has already been developed, so it’s not going to cost you anything significant in terms of developer hours. This is assuming that no technical hurdles exist in simply referencing the application via a page viewer. I’ll discuss the authentication issues in the disadvantages section.
- Quick – Its quick and easy to do, if you plan to redevelop your system to be MOSS aware, this is a good interim measure.
The disadvantages are: - Systems remain isolated – You still have to maintain that existing system, this might be OK but, it’s likely to be mature and stable.
- The information is still in a silo – This continues from the last point,MOSS isn’t taking full advantage of the system, you can’t personalise the information from the system, you can’t drop a filter web part on the page and get the exact data your looking for. Sure you might be able to crawl the external site and provide it via the Search Centre, but can your users build a custom list and use this information in a way that makes sense to them?
- Authentication – How is your authentication handled, MOSS is just providing a window to the external application, your external system has to do all the heavy lifting in terms of authentication and authentication.
- Inconsistent User Experience – Your external system probably has its own navigation, style and theme that probably isn’t the same as the MOSS implementation that your trying to sell to your users. It has gives off this feeling that its all held together by sticky tape.
I think this approach is fine if you fully understand what your doing, I plan on discussing some of the other approaches in some upcoming posts.
I’ve previously posted a demo that made use of a tag suggestion web part, as you type the web part will make an ajax call which will return the tags that match the current input. The user can click on the suggestion tag and it will populate the textbox, multiple tags can be entered into the textbox.  The most interesting part of this web part is the server side call, which I’ve implemented as a HttpHandler in the Tags.ashx file: public class Handler : IHttpHandler {
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text";
I’ve made use of the System.Web.Extensions JavaScriptSerializer to render the string array of tags to JSON:
1: List<string> tagList = new List<string>(); 2: 3: //add all the tags to a collection, JSON serialize the list 4: 5: System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); 6: 7: context.Response.Write(serializer.Serialize(tagList.ToArray())); 8:
I’ve put this handler in the /_vti_bin/ directory of SharePoint which maps to the ISAPI folder under the 12 hive.
I’ve used the same code to generate the tags as I did with the tag cloud web part, so once again the generation of these tags won’t scale to large lists, this is just an example of how to implement a JQuery based Tag Suggestion web part.
The source code for this web part can be found here.
Just a quick tip, if your crawling external sources with the MOSS (or Enterprise Search), you might find that your crawl doesn’t finish or hangs, it might be worthwhile checking to see if the site you are crawling has a calendar with links: You will need to determine the URL and then add a crawl rule to exclude that path, the crawler will see an infinite number of pages (it thinks each date and next link is a separate page).
I recently had a project where I needed to bind some business objects to a SPGridView in a web part, which on its own is trivial, but I also needed to create the columns for the SPGridView on the fly, since a number of business objects could be rendered in this control. The main problem was the naming of these columns, if I just bound them directly to the Grid, I would get the ugly naming of my properties (I say it’s ugly, it’s actually pretty awesome if your a developer, but those pesky end users like pretty names). The sample grid looks like this by default: So what I came up with isn’t exactly new or innovative, I created a custom attribute called Alias: public class AliasAttribute : System.Attribute
{
protected string[] aliasName;
public AliasAttribute(params string[] alias)
{
this.aliasName = alias;
}
public string[] Alias
{
get { return aliasName; }
set { aliasName = value; }
}
}
This attribute gets applied to each property on your business object with a pretty name:
[Alias("Internet URL")]
public string InternetAddress
{
get { return internetAddress; }
set { internetAddress = value; }
}
Now the code that performs the binding looks for properties with a custom attribute and pulls out the alias as the HeaderText:
BoundField newBoundField = new BoundField();
newBoundField.HeaderText = Helpers.GetPropertyAlias(field.Trim());
newBoundField.DataField = field.Trim();
grid.Columns.Add(newBoundField);
public static string GetPropertyAlias(string propertyName)
{
//Change the next line for your business object ..
PropertyInfo[] propInfos = typeof(BusinessObject).GetProperties();
foreach (PropertyInfo propInfo in propInfos)
{
if (propInfo.Name.ToUpper().Equals(propertyName.ToUpper()))
{
object[] attribs = propInfo.GetCustomAttributes(typeof(AliasAttribute), true);
foreach (object attrib in attribs)
{
if (attrib is AliasAttribute)
{
AliasAttribute alias = (AliasAttribute)attrib;
//return the alias
return alias.Alias[0];
}
}
}
}
//if we don't find the custom attribute, just return the same value we passed in
return propertyName;
}
The end result is a nice pretty grid:
A sample project can be found here.
If you work in an organisation that has an organisation hierarchy it might be worth setting this up within MOSS, so that when you view a user’s profile you will get the following view:  This is driven off the Manager profile field, it can be setup so that it is driven off either active directory or a custom source such as a SQL database in which case the BDC will need to be used. The Colleague suggestions and notifications make use of the organisation hierarchy as well, so it is worthwhile to set it up. One thing but, regardless of your localisation settings, the heading ‘Organization Hierarchy’ will be spelt with a ‘z’, so for us Aussie’s we just have to cop the American spelling.
The SharePoint Capacity Planning tool can be found here. The executive overview can be found here. The process of creating a model is very simple: The UI is wizard based and it asks a number of questions, it's all high level stuff that shouldn't present any issues: The notes from the overview point out that the tool gives a first approximation, we’ve found that it does a good job recommending hardware, but that the topology of the recommended farms could use some extra thought. For example, if you created a single intranet site with 25000 users with a heavy collaboration usage profile, the tool will recommend 6 servers, 2 of which are SQL Servers. The tool recommends 3 web front ends and an index server. Now I’m sure this would work fine, but some questions you could ask are: Could adding a dedicated web front end that isn’t in the load balanced cluster but that is used to service the indexer add to the performance of the site, since the crawler isn’t competing with end users for resources? The tool doesn't go into the logical architecture of the SharePoint farm, it's purely hardware related. Overall I think the tool is very worthwhile, your planning should definitely include running this tool, but don’t let this tool do all the work, do a little bit of thinking for yourself.
If you run your SQL Server on a non standard port you may have experienced issues when installing SharePoint on this server. The standard psconfig utility has a field for the server name, if you give it a comma and then the port your SQL box is running on it will fail. The psconfig wizard doesn't understand the port number format. This isn't just related to SharePoint, I had to use this same trick when I was doing a Great Plains install. The trick is to create an Alias, first run 'cliconfg', you should be presented with the following UI: Select the Alias tab, then Add - you should then see the above dialog. Now you just need to enter the server alias, I normally just make this the server name, but your circumstances may be different, then unselect the 'Dynamically determine port' checkbox so you can enter the port number that your SQL Server runs on. Once the Alias is setup you can refer to the SQL Server by the server alias that you have setup here.
I found this fantastic post today that I just had to comment on: | In a SharePoint entry form the user can select multiple values from a list. This should include an “All” option so in a long list the user can select or de-select all values with one click. | The idea is to use JQuery to perform a select 'All' on all the checkboxes in a list. DannyE posted this JavaScript: $(”.ms-RadioText[title=’All’] :checkbox”).click(function(){
var otherids = (this.id).substring(0, (this.id).length-2 );
$(”input[id^=’”+otherids+”‘]”).attr( “checked” , (this.checked)?”checked”:”" );
});
It looks for all the items with the ms-RadioText class that also have a Title attribute that equals 'All', it adds a click handler that will then toggle the checked status of the checkbox, its extremely cool, way better than the latest block buster movie ...
Anyway, that's all well and good, I'm not adding much value reposting his stuff, so I'd just like to improve on his deployment recommendation.
Instead of adding a script reference to the master page and then inserting a content editor region to paste the JavaScript in, I would suggest using the JQuery Script Manager:
So the idea is to use SharePoint Designer to register the control with the JavaScript. Note here to use the ScriptTemplate you'll have to grab my latest script manager control from here.
<cc1:jQueryManager ID="JQueryManager1" runat="server">
<ScriptTemplate>
$(".ms-RadioText[title='All'] :checkbox").click(function(){
var otherids = (this.id).substring(0, (this.id).length-2 );
$("input[id^='"+otherids+"']").attr( "checked" , (this.checked)?"checked":"" );});
</ScriptTemplate>
</cc1:jQueryManager>
This will insert the JQuery Script Manager and declaratively add the JavaScript code to the page. This way you don't need to worry about the script resources or having multiple client side load functions.
How can you not love JQuery after seeing the power of the above code? it's just that awesome!
I've previously posted a demo that illustrated a tag cloud web part, I've now posted the code. First let me warn you that the method I've used to get the tag list will not scale, so please disregard the TagBuilder method, you'll need to implement that yourself. I've based this tag cloud control loosely on the tag cloud control from codeproject, I've refactored most of it, but have kept the maths behind generating a weighting. Using the control is pretty simple, you just need to measure the tags and then pass in a collection of CloudItems: //tagList is a dictionary with the tag and count
Dictionary<string, int> tagList = new Dictionary<string, int>();
foreach (string tag in tagList.Keys)
{
CloudItem cloudItem = new CloudItem();
cloudItem.Text = tag;
cloudItem.Weight = tagList[tag];
cloudItem.Href = "/SearchCenter/Pages/Results.aspx?k=tags:" + tag;
Items.Add(cloudItem);
}
There are a number of ways to perform the measurement, if you were to count the number of times a tagged resource was accessed, you would end up with a heat map of popular items. Alternatively you could simply count up the number of times a tag has been used (I think this is the most common use, I've done it this way in my demo). In any case the exact implementation details have been left up to you.
The web part that I'm presenting today is more of a building block, it doesn't do much on its own. It makes use of the JQuery Corners Plugin. To set the corner types you can modify the Corner Type setting: The above example will cause the following JavaScript to be output: jQuery('cornerDiv').corner('tr 25px');
This web part makes use of the JQuery Script Manager that I've posted about previously.
As usual the source can be found here.
A screencast of the control in action can also be found here.
I've just rolled off a project that made use of the Business Data Catalog (BDC), I thought I'd share my experiences. Firstly I'll give a quick heads up on the BDC, you've probably seen a diagram similar to the following one, basically it shows all the features in MOSS that hang off the BDC: Business Data Web Parts - These are the out of the box list web parts, they provide enough functionality out of the box to at the very least expose the user to the data. SharePoint Lists - A user can create a column of type 'Business Data' this is very handy for end users, for example they may want to create an Asset register for locations, the locations that are relevant to them maybe exposed via the BDC, this would then save the user the task of maintaining the location list. Search - Provide a consistent search experience across all the enterprise data. User Profile Importer - Use business data to enhance the profile information you have about your user base, from the example above, you could import the base location into your profile property, then web parts could be built to personalise the information that is displayed to you. Custom Solutions - Use the BDC as a data access layer. So anyway the application that I did some work on fits into the custom solution box mostly, but with a little bit of thought you can generally make it cross over a number of areas (search, profile import etc) and by doing so create a greater experience for your end users. For example, our BDC profile import process imported a whole range of properties, one in particular was a business unit code. So by creating a custom metadata property, the people search was able to become a business unit people searcher. We then build some custom web parts that visualised the data on some maps and in some other ways that were relevant to the business, we constructed links that pointed to the people search which made use of the location code, much in the same way that I created tag links in the search demo. We added the BDC data to the search centre but changed the result links to point to pages with our custom web parts, so now the search centre feeds into the custom visualisations, so no matter where the user comes from, either search or navigation they are going to get sucked into our experience. So my tips are simple: - Plan your navigation experience - does search play a part in your experience?, its such a powerful concept to ignore, more people especially the younger generation are very search centric.
- The BDC is best for read only type data - do you need to push back information? might be other easier ways to go about it.
- Try to use all the options like web parts, search and profile information to be part of your overall custom solution.
- Can users use your data in lists? Would it make the end users job easier?
I've posted a couple of articles on JQuery and it's use in SharePoint and also provided some sample web parts. Recently I was putting together these web parts into a single project and I ran into a problem. Each web part was adding the JQuery script resources to the page as if it was the only control on the page, for example the ImageCarousel and the Tag Suggestion web parts both had code that needed to be run on the JQuery load event, this problem would also crop up when multiple instances of the same web part were added to the page. What was outputted to the page was something like: $(document).ready(function(){
//function call for webpart1
}
$(document).ready(function(){
//function call for webpart2
}
$(document).ready(function(){
//function call for webpart2 instance 2
}
So I started developing a Script Manager control that I could add to the page that would output the JQuery load event just once, but with all the calls for the page inside this event. As with anything programming related these days, I found what I was looking for at http://codeplex.com/JQueryScriptManager/ I've changed a bit of the implementation, but the basic intent has remained the same. Hopefully I'll be able to get these changes updated to codeplex.
Looking at the revised code for the ImageCarousel web part:
jQueryManager jqueryManager = null;
protected override void OnInit(EventArgs e)
{
jqueryManager = jQueryManager.GetCurrent(Page);
if (jqueryManager == null)
{
jqueryManager = new jQueryManager();
Page.Controls.Add(jqueryManager);
}
Page.ClientScript.RegisterClientScriptInclude("JCarousel", Page.ClientScript.GetWebResourceUrl(this.GetType(),
"JQueryWebParts.js.jquery.jcarousel.pack.js"));
base.OnLoad(e);
}
Now inside the PageInit we check for the presence of a JQueryManager script control, if we don't find one, we add it to the controls collection. Just like the ASP.NET Ajax ScriptManager control, there can only be one per page.
Now to add code that is globally scoped which also resides in the same script block where our OnLoad event lives:
jqueryManager.RegisterScript("-- javascript code without <script> blocks");
//example:
jqueryManager.RegisterScript("var someItems = [5,4,3]");
To get javascript to run when the page is loaded (this will bundle calls into a single load event):
jqueryManager.ReadyFunctions.Add(new RegisterStartFunction("jQuery('#carousel').jcarousel();"));
Use the ReadyFunctions collection which takes a RegisterStartFunction object as a parameter.
Internally the JQuery script manager control is performing the following:
StringBuilder Start = new StringBuilder();
if (Scripts != null)
{
foreach (RegisterScriptBlock r in Scripts)
Start.Append(r.ScriptBlock + Environment.NewLine);
}
if (ReadyFunctions != null)
{
Start.Append("$(document).ready(function(){");
foreach (RegisterStartFunction r in ReadyFunctions)
Start.Append(r.FunctionName + Environment.NewLine);
Start.Append("});\n\n");
}
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "JQuery", Start.ToString(), true);
The source code can be downloaded here. This includes the updated JQuery Script Manager and the ImageCarousel web part that I've previously posted about.
I think this approach will provide a nice platform to continue building web parts based on JQuery Plugins, have fun.
Continuing on my theme to bring more JQuery plugins into SharePoint, I present a sample application that makes use of the search features of MOSS and some custom web parts that provide Ajax tag suggestion and a tag cloud web part that works over all SharePoint document libraries with a little configuration of the search engine. Here is a screen cast that demonstrates the concept: Alternatively you can download a higher quality video here (7mb). I will blog more about how I built this application in the coming days and I'll also release the source code to it along with a more detailed description. If I can convey any message about this application it would be that the search experience is an integral part of the overall application and it can really add some value. For example in the tagging demo I've created a metadata property mapping called Tags, this property is used to crawl the Tag column of the document library. The search cloud web part renders a link to the search centre with the selected tag embedded in the search query, so for no code I've managed to display all the documents that are tagged with the selected tag. The Ajax tag suggestion web part is based on the great Tag Suggestion plugin, it uses a IHttpHandler to iterate over all of the lists to create a collection of matching tags which are returned as JSON, now this approach won't scale, but we can work on that in the future, hopefully we can also leverage the search engine for that as well. For now, hopefully this demo will give you some ideas to employ the search engine inside your MOSS development.
Recently I posted about using JQuery in SharePoint web parts in this post I wrapped up the JCarousel plugin. Instead of requiring the JCarousel.js file to be included into the master page, I instead made the web part inject the HTML to cause the JavaScript file to be loaded. The first step was to include the packed version of jquery.jcarousel.pack.js into my project: Notice the Embedded Resource setting for the build action. Now if we add the following code to our assembly.cs file: [assembly: System.Web.UI.WebResource("SPImageCarousel.jquery.jcarousel.pack.js", "text/javascript")]
The above line now makes our embedded resource available for use in our web part which is done by the following code:
protected override void OnInit(EventArgs e)
{
Page.ClientScript.RegisterClientScriptInclude("JCarousel", Page.ClientScript.GetWebResourceUrl(this.GetType(), "SPImageCarousel.jquery.jcarousel.pack.js"));
base.OnLoad(e);
}
This code will ensure that the script is only added to the page once since the key "JCarousel" is the first parameter, remember that more than one web part can be added to a page at the same time, so we need to consider this as we develop our web parts. OnInit is the perfect event to call RegisterClientScriptInclude it's nice and early in the page lifecycle.
So hopefully you'll find this a nice and easy way to include your JavaScript resources, it does have the disadvantage of requiring a recompile to include a later version of the JavaScript file, so your mileage may vary if this is a concern.
Better still if your using .NET 3.5 Service Pack 1, you could make use of the CompsiteScript element of the script manager to combine the javascript files so the browser makes fewer calls to the server.
I've mentioned before that I'm partial to JQuery, I consider it to be the Jessica Alba of JavaScript. With all the work that has been done creating awesome JQuery based UI components, it seems like such a shame that us SharePoint developers aren't making more use of them. So I present to you my first JQuery based web part, the Image Carousel: Based on JCarousel, this web part presents the contents of a picture library in a carousel that can be scrolled by the navigation buttons. The web part simply imports the jcarousel.js file, the web part then injects a JavaScript array of the images that reside in the selected picture library along with some javascript that initialises the carousel. The code to find the SharePoint picture library list is trivial: SPSite site = SPContext.GetContext(HttpContext.Current).Site;
SPWeb web = site.OpenWeb();
foreach (SPList list in web.Lists)
{
if (list.BaseTemplate == SPListTemplateType.PictureLibrary)
{
//grab the images from this list
jsArray.Append(ProcessList(list));
}
}
The web part makes use of custom toolpart for the selection of the picture list:
I think this provides a good template and starting point to start putting more of those JQuery plugins to use inside SharePoint
A small screencast of the control in action can be found here.
The end effect is some exceptional functionality for little development effort, which is really what JQuery is all about.
Some of the things that you need to think about when integrating:
- Make sure that the injected div tag that will be used at the host has a unique id, remember that more than one webpart can be added to a page, if you hard code the html to have specific ID's, you will run into problems.
- Make sure the JavaScript gets injected into the right part of the page, also just like the above point, ensure that each injected bits of code have a unique name, so that multiple webparts on the same page don't cause problems.
- Use the packed version of the component, SharePoint is bloated enough already, use the smallest possible footprint you can.
The source code can be found here.
In my first post dissecting the portal connection code, I showed the following code: PortalConfig portalConfig =
(PortalConfig)curSite.WebApplication.Farm.GetObject
(
new Guid(curSite.WebApplication.Properties[PROP_KEY].ToString())
);
Notice the line: curSite.WebApplication.Properties[PROP_KEY].ToString()
The properties collection is scoped at the web application level. So if someone was to build a tool that called the Clear() method on this collection it would cause issues inside the Portal Connection tool.
So my point is to use the web application properties with other applications in mind, only clear your own state.
If your profile properties includes the organisation hierarchy, you may have noticed that when an AD account is deleted or deactivated the manager of that person is sent an email notifying the manager that the site is scheduled for deletion and that they now have permissions to the mysite so they can clean it up. Of course the MySite will only be deleted if the Automatic Site Deletion is enabled. Be aware that if the AD account is deleted and Automatic Site Deletion is enabled, the manager won't have access because the site will be deleted.  Lets say you work in an organisation that wants to suppress this email (I know it sounds silly), how would you go about that? The first thing to understand is the process that caused that email to be sent in the first place. The first step is the profile import will detect if the AD account has been deleted or disabled and will change the bDeleted flag to 1 in the UserProfile_Full table of the SSP database. The next step of the process is handled by the My Site Cleanup Job:  If you disable this job then you will prevent the notification email being sent. However you will still need to have a process in place to clean out the mysites and the profiles. The crawler will still find the profiles and index them (so your people searches will include outdated people). The MySites will still be around taking up disk space. One potential solution is to call the profile stored procedure manually: DECLARE @UserID uniqueidentifier
DECLARE @NTName nvarchar(400)
DECLARE @SID varbinary(512)
DECLARE userCursor CURSOR FOR
SELECT Userid, NTName, SID from userprofile _full where bdeleted = 1
OPEN userCursor
FETCH NEXT FROM userCursor INTO @UserID, @NTName, @SID
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC profile_RemoveUser @UserID, @NTName, @SID
FETCH NEXT FROM userCursor INTO @UserID, @NTName, @SID
END
CLOSE userCursor
DEALLOCATE userCursor
I've had the chance to see two different approaches to implementing MOSS search, the first is the standard of the box experience and the second is by making use of all the features that MOSS offers. Once you understand what MOSS search can do, there is no reason why you shouldn't be making full use of the product. I found this fantastic article called Plan the end-user search experience, which outlines in great detail all the options the search products provide. I thought that I would list a few small things that give big bang for the effort that is needed to get them running. 1. Best Bets Number 4 on the above image is the best bet. A best bet is predefined link that is shown for certain search terms. It doesn't have to be a link to your MOSS site, it can point to external resources. 2. Synonyms Synonyms should be configured with words that are commonly used in your organisation, in the example above 'oof' is used for 'out of office', so both search items 'oof' and 'out of office' would show the same best bet. 3. Search Results Display The layout options are not mentioned in the referenced article, I do think it is a good idea to build your XSLT to make your results look like Google. So align your results to the left, get rid of the horizontal line and maybe think about getting rid of the file type icon. If anything I just want to bring the search experience to your attention, it's worth spending some time to become familiar with the various options that exist.
In a previous post I introduced the Automatic Portal Connection tool, which can be used to automatically setup the portal connection on new and existing sites. I thought I would take a peek at how I've made this happen: Firstly I'd like to introduce the SPPersistedObject: The description MSDN uses is: | The SPPersistedObject class provides a base class for all administration objects. It serializes all fields marked with the Persisted attribute to XML and writes the XML blob to the configuration database. The SPPersistedObject class contains code to serialize all its members that are base types, other persisted objects, and collections of persisted objects. Configuration data that is stored in persisted objects is automatically made available to every process on every server in the farm. | The Portal Connection tool has a class called PortalConfig which derives from SPPersistedObject this class holds the configuration information such as the portal name and portal URL: /// <summary>
/// Simple persisted object that stores the portal connection name and the portal connection url
/// </summary>
public class PortalConfig : SPPersistedObject
{
[Persisted]
private string portalName = string.Empty;
public string PortalName
{
get { return portalName; }
set { portalName = value; }
}
[Persisted]
private string portalUrl = string.Empty;
public string PortalUrl
{
get { return portalUrl; }
set { portalUrl = value; }
}
public PortalConfig()
{
}
public PortalConfig(string name, SPPersistedObject parent, Guid guid)
: base(name, parent, guid)
{
}
}
SPPersistedObject is a very handy class, especially for configuration type scenario that is used in this solution, a method that makes use of this object is:
/// <summary>
/// Simple method to persist our settings to the site
/// </summary>
/// <param name="curWeb"></param>
/// <param name="curSite"></param>
public static void ApplyPortalConnectionSettings(SPWeb curWeb, SPSite curSite)
{
PortalConfig portalConfig = (PortalConfig)curSite.WebApplication.Farm.GetObject(
new Guid(curSite.WebApplication.Properties[PROP_KEY].ToString())
);
if (curWeb.IsRootWeb)
{
curSite.PortalName = portalConfig.PortalName;
curSite.PortalUrl = portalConfig.PortalUrl;
}
curWeb.Update();
}
This object is filled out and persisted by the central admin page, then a feature staple gets this information when the site is created and applies it, but I'll expand on this in future posts.
The portal connection is pretty useful, say you have a web application for team sites and have self service site creation turned on you would probably like the portal connection to be set to your portal homepage. Just as a refresher the portal connection is the link at the top of your site: I've put together a tool that will give you the ability to set the portal connection for new sites and also for newly created sites. Once the solution has been deployed and the features activated, from central admin you will see a new link: Following this link you will be presented with the following page: From here you can select the web application to work with and you can set the portal name and URL. By selecting the 'Apply To Existing Sites' checkbox, you will set portal connections to all the sites on that web application. The SharePoint solution can be downloaded here, I'll be releasing the code soon, I want to release it to Codeplex, I just need to set it up first. Here is a small screencast showing the tool in use Edit: I've now setup the Codeplex project: http://codeplex.com/SPPortalConnection
If your experiencing long initial page load times and your servers are configured so they can't connect out to the internet, you can reduce this time by either allowing the servers access to crl.microsoft.com or by deselecting the 'Check for publisher's certificate revocation' option from within Internet Explorer. The Internet Explorer option can be found under the Tools menu, then Internet Options: Another option is to add a host file entry so that crl.microsoft.com points to the localhost (127.0.0.1)
There is so much great content on MSDN, but often it's hidden away, so I like to blog about these finds so I have a chance to find them in the future. Today's find is the Design Logical Architecture for Collaboration Sites. In particular I like the advice surrounding team sites: | Design guidance for team sites includes the following recommendations, each of which is elaborated in the following sections: -
Host team sites in a dedicated Web application. -
Apply Web application general settings, such as quota and life-cycle management settings at the Web application level to manage growth of team sites and to keep content current. -
Design content database settings for appropriate storage and scale and to ensure that you can back up and restore databases of the designed size. -
Automatically delete sites that are not used. -
Use paths to organize team site URLs. -
Plan for appropriate policies and permissions. | Some of my notes: - Hosting the team sites in a dedicated web app - This gives you the power to apply web application policy (this goes for the last point as well), what we normally do is have a zone like admin.yourteamsiteurl, then we use policy so that our administrators have full access over the web application. This means they can go into any team site if needed.
- In large organisations keeping an eye on the growth of the team sites is important, you need to know how large the content database is and when to expand out to more databases. Make sure you setup site level warnings, so your administrators are on top of the situation.
- Ensure that the procedures for notifying users of inactive sites is occurring, you really want to be able to claim that space back.
- A nice path is helpful for your end users, we normally opt for team.yoursiteurl/sites/<teamsitename> (the default setting)
This article also feeds into another great find Logical Architecture Sample Design: Collaboration Sites
I was reading this MS KB article 'How to point to a custom 404 error page in Windows SharePoint Services 3.0 or in MOSS'. I thought I'd throw together a simple administrative page to pull it all together. I've packaged this up in this solution file. Once you active the 'Manage Custom 404 Page' feature, you will get a new menu item in the Application Management page in Central Admin: From here you get a simple page that you can type the 404 page name for the selected web application: The code is super simple: using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Portal.WebControls;
using Microsoft.SharePoint.Administration;
using System.Web.UI.WebControls;
namespace SP404Handler
{
public class SP404HandlerPage : LayoutsPageBase
{
protected WebApplicationSelector lstWebApps;
protected PageLevelError pageLevelError;
protected Button ButtonDoneOK;
protected TextBox txt404PageName;
protected Microsoft.SharePoint.WebControls.InputFormSection I404Section;
protected void lstWebApps_OnContextChange(object sender, EventArgs e)
{
try
{
SPWebApplication webApp = lstWebApps.CurrentItem;
BindData(webApp);
}
catch (Exception err)
{
pageLevelError.ErrorText = err.Message;
}
}
protected void DoneButtonOkClick(object sender, EventArgs e)
{
SPWebApplication webApp = lstWebApps.CurrentItem;
try
{
webApp.FileNotFoundPage = txt404PageName.Text;
webApp.Update();
}
catch (Exception ex)
{
pageLevelError.ErrorText = ex.Message;
}
}
private void BindData(SPWebApplication webApp)
{
txt404PageName.Text = webApp.FileNotFoundPage;
}
}
}
The source can be downloaded from here, or a complied solution from here.
I read with interest the recent example solution architecture article posted on Technet. It goes through the logical architecture of a School MOSS implementation. They have some good recommendations on hosting hardware and have some interesting comments on the way they handle media hosting. Of Course being good consultants they provide nice visio overlays of the logical architecture: However I was surprised to see the following: | The solution is composed of two sets of sites: Each set of sites is grouped in a separate application pool. | Basically they are saying that staff sites and student sites are hosted in the same web application. I hope staff aren't going to put sensitive information into their sites, because it would only take one human error assigning permissions to give students access to the site. However if the students and staff are in separate web applications you could use web application policy to block students, so even if they were accidentally given access by a human, they would be blocked. I'm sure this has been thought about by these guys because further on in the article we find: | You want to use zone policies to enforce permissions at the Web application level. For example, you can create a policy to deny write access to all unauthenticated users who view content on the public-facing sites. | This is a classic example of where web application policy is so helpful and why it really pays to have a think about the implications of your design.
I came across this white paper titled 'Office SharePoint Server Web Browser Support', it covers the how SharePoint supports level 1 and 2 browsers, but the authors have done extensive testing on the most common features in SharePoint and provided a matrix of what does and doesn't work. They even cover some work around's like using the Telerik RAD editor for SharePoint. I'm blogging about this as much for my own memory as anything else, you always come across these great resources and can never find them again.
I came across an interesting issue the other day with a Virtual Earth web part that we have developed. If you scrolled down the page and hovered over a pushpin, in IE the info window would be displayed higher than the pushpin, i.e. they weren't aligned properly. I found the answer to my problem, the master page that the site was using did not have the following: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
The missing doctype tag was putting IE in quirks mode and this caused the scroll behavior mentioned above. Heather Solomon has a discussion on Sharepoint master pages and the missing doctype. Unfortunately for us the master page couldn't easily be changed without making changes to a number of CSS classes.
I guess my point is that your sharepoint designer should know about this sort of stuff and design from the outset with the doctype tag.
I'm sure you've done work in an organisation that wants to exert its control over users, one of the common things that I've seen is the disabling of menu items like the 'delete this web site' option. The last thing you want to do is edit the pages on the file system, this will just cause issues when you apply a service pack or if you bring a new WFE online because each server will need the same hack. Instead you should build a solution that makes use of the HideCustomAction definition. For the above delete site link, the Elements.xml file should look like: <?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<HideCustomAction Location="Microsoft.SharePoint.SiteSettings"
GroupId="SiteAdministration" HideActionId="DeleteWeb"
Id="HideDeleteWeb" />
</Elements>
From here it's just a matter of referencing the above elements.xml in your feature.xml file and building the solution in the normal way.
To find out what the ActionId, GroupId and Location you can refer to this MSDN page.
Now you can safely change the menu items, but be aware that the files still exist on the file system, so in the example above if a user still knew the location of the delete web page they could still delete their site.
Depending on your hardware if your crawling your own content, or if you are crawling external content you might like to know what the three settings for the crawler performance settings really mean. I ran across this article Change the Indexer Performance Settings which provides the following table: | Indexer Performance setting | Total threads available for crawling | Maximum threads available for crawling any particular host | | Reduced | Number of processors on the index server | Number of processors on the index server | | Partially reduced | 4 times the number of processors on the index server | Number of processors on the index server plus 4 | | Maximum | 16 times the number of processors on the index server | Number of processors on the index server plus 4 |
I had the task of writing a web part that had a lot of UI code and one of the requirements was that a designer could change the look and layout as a toolpart setting. The smartpart stuff is OK, but it doesn't really provide the kind of flexibility that I was after. I came up with a neat solution that I think is pretty powerful. We've all seen the example webpart code that has lots of layout controls like tables defined in the CreateChildControls method, it's awful to write and its not easy to change. What we really need is a way to define a template that is provided by an external source and then to hookup our code to perform operations on it. Lets have a look at some sample code: using System;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.WebControls;
namespace Template
{
public class Template : System.Web.UI.WebControls.WebParts.WebPart
{
//string with UI code to be added to the page
protected string templateHTML = string.Empty;
//Control IDs, the template must use these ID's
private string searchButtonID = "searchImgBtn";
private string allTextID = "allTxt";
//controls that will be found from the template
protected TextBox allTxt = null;
protected ImageButton searchBtn = null;
public Template()
{
}
protected override void CreateChildControls()
{
if (templateHTML != string.Empty)
{
try
{
//create a control that has been parsed by asp.net
Control template = Page.ParseControl(templateHTML);
//add it to the page
this.Controls.Add(template);
//search for the known controls
allTxt = template.FindControl(allTextID) as TextBox;
searchBtn = template.FindControl(searchButtonID) as ImageButton;
//hook up any events
if (searchBtn != null)
{
searchBtn.Click += new ImageClickEventHandler(btnSearch_Click);
}
}
catch (Exception err)
{
this.Controls.Add(
new LiteralControl(string.Format("Error applying template: {0}", err.Message))
);
}
}
else
{
this.Controls.Add(new LiteralControl("Please enter a template in the toolpart settings"));
}
base.CreateChildControls();
}
void btnSearch_Click(object sender, ImageClickEventArgs e)
{
//if the template included the textbox, show its results
if (allTxt != null)
{
this.Controls.Add(new LiteralControl("Searched On: " + allTxt.Text));
}
}
[System.Web.UI.WebControls.WebParts.WebBrowsable(true),
System.Web.UI.WebControls.WebParts.WebDisplayName("Layout Template"),
System.Web.UI.WebControls.WebParts.WebDescription("Template that is used for layout"),
System.Web.UI.WebControls.WebParts.Personalizable(
System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
System.ComponentModel.Category("Template Settings"),
System.ComponentModel.DefaultValue("")
]
public string TemplateHTML
{
get
{return templateHTML;}
set
{templateHTML = value;}
}
}
}
The template that is set with the toolpart should look like this:
<table>
<tr>
<td>Search On:</td>
<td><asp:TextBox ID="allTxt" runat="server" />
</tr>
<tr>
<td colspan="2"><asp:ImageButton runat="server" ID="searchImgBtn" ImageUrl="Search.gif"/>
</tr>
<table>
Notice how the control IDs match the values in the code, that is important, because we use FindControl to bind the code to the UI.
So what we end up with is a flexible way to define our UI code in a way that most of us are used to. I've used this in a number of places now and haven't seen any issues, drop me a line if you start using this approach.
I've written a Sharepoint feature that will modify the web.config file and add the entries needed to enable the ASP.NET AJAX Control Toolkit. I've made the feature scoped to the web application level, so to activate the feature (which will write to the web.config file) go to Application Management in central admin, then to Manage Web application features: Next step is to activate the feature: I was thinking that I would just provide the source code and that you could include it in your solution project as this functionality doesn't do much as a stand alone feature. To use this feature you will need the source, I'm thinking about uploading to CodePlex, I'm just not sure if one code file is enough to warrant this. To add this feature to your existing project you should include the following information in your feature.xml: <?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
Id="YOUR GUID ID"
Scope="WebApplication"
Title="ASP.NET Ajax web.config update tool"
Description="Adds web.config entries to enable ASP.NET Ajax"
ReceiverAssembly="Full name and version of your assembly"
ReceiverClass="Features.AjaxWebConfigInstaller"
Hidden="False"
>
</Feature>
Excel Services offers a number of Business Intelligence features such as: - Workbooks can be connected to enterprise data sources
- The logic contained in workbooks can be reused in server based code, i.e. don’t have to rewrite code specifically for the server
- Provides real time analysis of data
- Full features UI experience, there is no loss of fidelity when moving to the server.
- Sorting, Filtering and drill down all work the same, no loss of functionality.
It’s worth your time investigating the features offered by Excel Calculation Services
I haven’t had a featured diagram in a little while so here goes: .gif) This doesn’t have all the pretty colours of my last ones. But this diagram does offer a great insight into how Microsoft recommend integrating the MOSS BDC and InfoPath forms service.
Continuing on with my last post on great diagrams, today I bring you the SharePoint Capabilities Diagram: .gif) These capabilities are: Collaboration and social computing Portals Enterprise Search Enterprise content management Business process and forms Business intelligence
I found this resource which lists the Supported Excel types and UDF arguments: .gif) 1. Types are in the System namespace. These are the only supported argument types. 2. The term "All numeric" refers to the following types: Double, Single, Int32, UInt32, Int16, UInt16, Byte, Sbyte. Specifically, Int64 and UInt64 are not supported types. 3. Date/time in Excel is a Double internally, but the representation (algorithm to encode date/time as Double) is different from the .NET Framework representation, and conversion is necessary. Therefore, UDF arguments support DateTime as an explicit type, and Excel Services convert any Excel Double into a .NET DateTime when the argument is of type DateTime (assuming that the Excel Double is actually a date/time value). 4. Scalar object arguments are not supported; only one-dimensional or two-dimensional object arrays are supported (see the "Arrays and Ranges" later in this article). This table row defines only how each cell in the range that is passed into the array is handled.
Like me you might sometimes struggle to describe the breath of the Microsoft stack across all the various products. I must give credit to the Microsoft people who come up with these great looking diagrams: .gif)
Almost all SharePoint developers will know about the U2U CAML Query tool, I just thought I’d throw a little link love their way. If you live under a rock, this tool provides a nice GUI for you to generate those nasty CAML queries: This is a fantastic tool, I have no doubt that it will save you plenty of time. Just remember to remove the <Query> opening and closing tags before you use it in your code.
BDC associations allow you to create master / child type relationships with the Business Data List Web Part and the Business Data Related List Web Part. From the MSDN article, the following points should be followed: - The entity that defines the association method must be below all of the entities to which it relates within the XML metadata file.
- The association method must have input parameters that map to the identifiers of all the source entities.
- The return parameter of the association method must include the identifiers of the destination entity.
I would recommend watching the associated 10 minute video from the MSDN article as well.
I’ve previously posted about a great resource for the new MOSS developer. Well here is another great resource to help the developer get their mind around the workflow’s in SharePoint. The MSDN article is: Developer Introduction to Workflows for Windows SharePoint Services 3.0 and SharePoint Server 2007 This article covers the following topics: Introduction to workflows - The workflow architecture
- Workflow Types
- Workflow composition
- Workflow mark-up
- Workflow’s that are specific to SharePoint
- Authoring SharePoint workflows with both SharePoint Designer and Visual Studio, with topics that draw the comparisons of each product.
Again its another meaty document, I think my first resource still stands as the document for the newbie to read, if they come back for day two, maybe throw them this link.
Today I was asked if it was possible to have a different icon display in the search results if the returned document was a PDF. The answer was yes. Here’s how: - Save the image to the <12 hive> \Template\Images\ directory
- Edit the following xml file: <12 hive>\Template\Xml\DOCICON.XML
- Add the following line: <Mapping Key="pdf" Value="pdf.gif"/>
*Note the <12 hive> is normally located at: C:\Program Files\Common Files\Microsoft Shared\Web server extensions\12
I’m sure everyone has seen the famous ‘hamburger diagram’ (if you look closely it kind of forms the share of a hamburger) The diagram shows the end-to-end product offering from Microsoft. The diagram points out some MOSS specific technologies such as Excel Workbooks, but it doesn’t highlight and BDC type interactions which I’m not sure how they would be modelled on this diagram since the BDC can return data straight from the data sources at the bottom, if they did include the BDC the diagram may not look like a hamburger anymore :)
Up until now I haven’t found a good resource to point a new developer who knows .NET but is new to SharePoint. Well as what usually happens, I stumbled across this article: Introduction to SharePoint Products and Technologies for the Professional .NET Developer - It covers everything a newbie needs to know:
- Benefits of SharePoint -
- Introduction to all the SharePoint components
- Using Visual Studio 2008 to build SharePoint solutions
- A comprehensive glossary of common terms
- A getting started guide
So I would add this to the list of must read items for that new starter, they have to start drinking from the fire hose at some stage!
I was reading this excellent Excel Services resource: Excel Services Technical Overview. It covers: - What Excel Services is
- Managing Excel Workbooks in MOSS
- Business Intelligence Reporting and Dashboards
- Extending Excel Services
The particular bit of information that I was seeking was this diagram that outlines the core components: .gif)
Continuing on from Part 1 and Part 2 where I discussed Zones, Authentication providers and Policy, this time I would like to discuss Site Collections. A site collection is a container, it forms the basis of an information architecture where you can create sub sites to build out your information architecture. Windows Sharepoint Services (WSS) allows the user to create one site collection, that is all of your content will be housed in a single site collection. MOSS takes a different approach and allows you to create as many site collections as you need, if you turn on self service site creation for team sites, then every site will be a site collection. Even the My Sites are in fact a site collection. Using managed paths, you can create site collections that form parts of your information architecture. So what are the benefits of a site collection? The first is distributed administration, each site collection can have different administrators, the other big features are a separate recycle bin and the ability to enforce a quota (as well as the features not covered here). Each site collection is an isolated collection of sites, you can't use the content query web part to roll up content across site collections (although you could use RSS feeds to do this). This might sound like a bad thing, but lets consider it with an example. From Part 1 we put forward a scenario where we have staff members and external people accessing a portal, both of these groups need to view different information depending on who they are. Lets assume we had one single site collection, without item level security (which isn't an out of the box feature) all users could see information they shouldn't. Or assume we did have item level security, it would only take a simple mistake to assign the wrong permissions for information to leak. It might sound like a good idea to have a single site collection, but after you think about it a little more it becomes obvious that it doesn't work when you get past a simple implementation (like what WSS is designed for). Looking at the reference diagram from Part 1, we see that Microsoft has indeed separated the partner content and internal content into separate site collections.
I've recorded a screencast that is a complement to Part 1 - Zones and Authentication Providers, in this screencast I show how to setup a custom zone to use forms based authentication by extending the existing web application onto a new zone and configuring that zone to use forms based authentication.
In my last post I covered a little bit about Zones and Authentication providers, the next chunk of knowledge that I would like to instill is Web Applications and Web Application Policy (or Zone Policy). A Web Application is an IIS website with a unique domain name, looking at our all encompassing diagram from Part 1 the web applications are the shaded grey areas (which are labeled as Application Pools). A Web Application can and should be given different application pool accounts to help secure the system. In the sample application the Intranet site is hosted in a different Web Application than the Team and MySites. To recap, a Web Application is used to isolate content, isolate users so that we can enforce permissions. There are other benefits as well, but they don't really concern us at this stage in the planning process. That leads into the next chunk of information: Zone Policy or web application policy. The concept is that Zone Policy enforces permissions at the web application level, I might have understated that, so here it is again in different words: Zone Policy will override all other security settings. Now that we have these concepts covered, if we look back at out requirements from Part 1, it starts to become clear what we need to start thinking about: - Members of golf clubs, should use a web application with a Zone Policy that prevents write operations.
Just to touch on this point again and to stress the concept, no matter what a site collection administrator does, whatever human error is made in assigning permissions, a golf club member will never be able to write to this site. Lets play devils advocate for a second and assume we took the approach of just using the permissions of the site collection, how do you prevent a user accidentally assigning permissions to someone who should not have them? By now your saying to yourself: Zone Policy. I'd also like to share a practice that our team uses when we manage a MOSS instance, we use Zone Policy to prevent our Farm administrators from making silly mistakes. We setup a zone like admin.domainname and assign permissions via Zone Policy to the admins, rather than making the farm admin group the site collection administrator. We do this so that the farm admins are forced to think about what they are doing, rather than just blindly browsing as an admin. I'm working on getting some screencasts up to help drive home the points that I'm making, so keep an eye out for them. Next time we will look at Site Collections.
I thought I might create a screen cast for a bit of fun, so this screen cast shows how you can stop IE from prompting you to enter your windows login credentials:
Basically you need to ensure that your site is in the Local Intranet zone, IE will automatically forward your current login credentials on to sites in this zone.
|