Welcome to MSDN Blogs Sign in | Join | Help

Using SQL Management Studio as a domain user from a machine that is not on the domain in Windows Server 2008 R2

My main development machine is not domain joined, but I often need to use it to access domain resources.  Usually this isn’t an issue as I have the option to enter my credentials through some type of login box.  In SQL Management Studio this option doesn’t exist if you are using Windows Authentication, you will always default to the user you are currently logged on as with no option to change.  The only way I can see to get around this is to use the command line to specify the following command:

runas /noprofile /netonly /user:<domain>\<username> <program>

A typical usage of this would look like:

runas /noprofile /netonly /user:CXDMN\gaffeyj "C:\Program Files (x86)\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\SqlWb.exe"

 

I assume this would work in any other apps but this is the only one I’ve tried so far.  Hope this helps someone avoid searching for this answer.

Posted by joshuag | 0 Comments

Crawling Large Lists in SharePoint 2007

There is a lot of information out there on working with large lists in SharePoint 2007.  There is some good info here and here and Steve Peschka has a great whitepaper on working with large lists in SharePoint 2007 that has been out for a while now.  One thing that I haven’t seen a lot of information on is crawling large lists.  Steve’s whitepaper has a quick reference in his whitepaper saying “If the indexer is timing out when crawling large lists, you can increase the time-out value”.  We had been experiencing issues crawling a large pages library of over 4,000 items.  We were getting the error “The item may be too large or corrupt.”  Increasing the time out  seemed to help, but many times the indexer would still not make it through the whole library, we were still looking for a better solution.  After working with Microsoft Support we were told to modify three registry values:

 

Key Default Value New Value
HKLM\SOFTWARE\Microsoft\Office Server\12\Search\Global\GatheringManager\DedicatedFilterProcessMemoryQuota 104,857,600 209,715,200
HKLM\SOFTWARE\Microsoft\Office Server\12\Search\Global\GatheringManager\FilterProcessMemoryQuota 104,857,600 209,715,200
HKLM\SOFTWARE\Microsoft\Office Server\12\Search\Global\GatheringManager\FolderHighPriority 50 500


Modifying these registry values has definitely seemed to do the trick for us so far.  This was for x64 machines with a lot of RAM, so this might not be a good idea if you are still on x86 and are RAM constrained.  I’d like to hear your results, did this work for you?  What are your experiences crawling large pages libraries in SharePoint 2007?

Posted by joshuag | 0 Comments

SPWeb.GetFile Hangs when retrieving a publishing page

I’ve been working on a utility for the past few days to manipulate some data on a SharePoint server.  This code got the “works on my machine” stamp of approval, but every time we ran it against a backup of production data it would hang after processing a certain number of pages with no errors to be found anywhere.  Since this was a backup of production data I tried deleting the file it was hanging on to see if that specific file was the issue…no luck.  After a few hours of trying to track down the problem I eventually switched from using SPWeb.GetFile to SPWeb.GetFileOrFolderObject.  The only impact this had on my code was I then had to cast the object type returned from GetFileOrFolderObject to SPFile.  Magically the hang disappeared.  I’m throwing this one in the SharePoint believe it or not bucket and moving on, in case you run into the same issue I’ve shown the difference in code below.

Here is the original code

   1: SPFile article = web.GetFile(articleUrl);
   2: articleGuid = article.UniqueId;

And the version that worked

   1: object spObject = web.GetFileOrFolderObject(articleUrl);
   2: articleGuid = ((SPFile)spObject).UniqueId;

I’ve shortened these for clarity, obviously you should be checking if you actually got a SPFile object back, but you get the idea.
Posted by joshuag | 0 Comments

I’ve got my SharePoint development environment setup, now what?

There are a bunch of posts out there on how to get your SharePoint development environment up and running.  A quick search on “Setting up your SharePoint development environment” will yield plenty of results to skim through to fit your needs.  However, not many of these articles focus on tips and tricks to make day to day development in SharePoint easier.  I’ve compiled some useful tips over the past few years that I wanted to share.

 

Software

SP Dispose Check – This is a tool release by Microsoft to check for un-disposed SPSite and SPWeb objects.  You should run this on a regular basis, and always before deployment to production.

SharePoint Designer – A lot of people overlook SharePoint designer claiming it’s not for “real developers”, but it can be invaluable for quickly manipulating existing SharePoint sites.  Real developers know how to use the tools available to them, and this is just another tool.

Red Gate .NET Reflector – This is great for digging into SharePoint assemblies when you want to get intimate with how something works.

U2U Caml Query Builder – If you are developing for SharePoint you need this, no exceptions.  It will make writing CAML queries much, much easier

Fiddler – Great tool for HTTP Request/Response debugging.  This comes in handy when debugging AJAX web service calls.  Firebug also works good here.

Firefox and Firebug – Firebug is an extremely useful tool for HTML, CSS and Javascript.  If you are doing SharePoint development you are inherently going to be working with these things a lot, hands down Firebug is the best tool for this.

IE Developer Toolbar – When you need to troubleshoot rendering issues in IE, this tool comes in handy.  IE8 has these built in, but anything before that you will need to download the add on.

WSPBuilder – Absolutely invaluable tool for building solution packages, otherwise known as .wsp’s.

 

OS Modifications

Environment Variables

First thing you should do is add stsadm to your “Path” variable.  This will allow you to execute stsadm from anywhere in your command prompt.  You can do this by adding the following to your “Path” variable in environment variables.  Don’t delete what is already there, just add to the end.

c:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\BIN;

I would also add SPDispose check if you installed above (I highly recommend it).

C:\Program Files\Microsoft\SharePoint Dispose Check\;

I also like to add my bin directory as a new variable to make copying assemblies easier, this works well for team development when not everyone has the same VM as you can all use the same post build steps.

image

 

If you are looking to squeeze every bit of performance out of your VM you can stop the following services:

Windows SharePoint Timer Service – this runs scheduled jobs for SharePoint.  If you shut this off some things may not function as you would expect.  For example, you need to manually execute .wsp deployments.  You can manually run the timer service by calling stsadm –o execadmsvcjobs.  This can consume quite a bit of resources, I have always developed with mine off, but be warned, you should understand what this does.

Windows SharePoint Services Search – If you don’t need search and aren’t developing any components using search on your VM you can turn this service off.

Office SharePoint Server Search – Same as above.

Quick Access

In my quick launch bar I like to add:

A shortcut to the 12 hive and virtual directory root

  • "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12"
  • C:\inetpub\wwwroot\wss\VirtualDirectories\80

A shortcut to recycle the app pool

  • Windows 2008
    %windir%\System32\inetsrv\appcmd.exe recycle apppool "SharePoint - 80"
  • Windows 2003
    cscript %windir%\system32\iisapp.vbs /a "SharePointDefaultAppPool" /r

Visual Studio

Add Guid Generator to Tools menu – this used to be there by default, not sure why they removed it, but you really need it for SharePoint development

Tools / External Tools / Add - c:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\guidgen.exe

 

That’s all I can think of for now.  What tools do you like to use?  What did I miss and more importantly what am I missing out on?

Posted by joshuag | 2 Comments

Using custom web services in SharePoint

If you are developing custom SharePoint applications there is a good chance that you are going to need some custom web services at some point, especially if you are making use of AJAX and JSON.  There is a great walkthrough over on MSDN on how to do this, but if you just need a web service to enable AJAX on a page it is a bit of overkill, especially since there is no way to automate deployment on some of the steps they detail.  I also recently saw a presentation where the presenter put his custom web service in a separate web application, I’m going to show that this really isn’t necessary, and you can fully automate your web service deployments through solution packages.  In the long run this method of deployment will make your life much easier.

 

I use WSPBuilder for all my SharePoint development.  I know there are other tools out there but I’ve been using WSPBuilder since 2007 and it hasn’t failed me yet (Thanks Carsten!).  Why fix it if it ain’t broke?  For this reason I always structure my SharePoint projects in the WSPBuilder method, which mirrors the SharePoint 12 Hive.

The first thing you want to do is create a new class library and build out your folder structure.  You can use “wspbuilder – createfolders” to do this, but it will give you a bunch of folders you won’t need for this.  I opted to create mine by hand. 

You should then add a folder under “LAYOUTS” to separate your custom code from SharePoint out of the box code.

In the “LAYOUTS” folder you can add your .asmx file.  Unfortunately Visual Studio won’t let you add web services to a class library project, so I usually just add a text file, rename the extension, and add the appropriate markup inside the file.

You’ll also want to add the code behind file.  For the purposes of this sample I’ve kept them both in the same assembly, but they don’t have to be.  Here is what my project looks like so far.

image

Inside ServiceSample.asmx I have the following single line which tells the web service which code behind assembly and class to use:

<%@ WebService Language="C#" Class="CustomWebServiceSample.ServiceSample,CustomWebServiceSample,Version=1.0.0.0,Culture=neutral" %>

Inside ServiceSample.cs I have the following code:

public class ServiceSample : System.Web.Services.WebService
    {
        [WebMethod]
        public string GetServerName()
        {
            return HttpContext.Current.Server.MachineName; 
        }
        [WebMethod]
        public string GetCurrentSiteUrl()
        {
            return SPContext.Current.Web.Url.ToString();
        }
        
    }

  

The important thing to note here is that your SPContext will still be valid, as long as you enter the web service through the appropriate URL.  My development machine is named wssdev, so if I go to the web service through http://wssdev, I’ll get the context for the root site collection.  If I go to the service through http://wssdev/sites/<sitename>, I’ll get the context for that site collection. 

I have a post build script that copies my assembly and .asmx file to the appropriate location, if you package this using wspbuilder you won’t have to do this step, but it helps during development.  Here is the script:

@SET TEMPLATEDIR=c:\program files\common files\microsoft shared\web server extensions\12
xcopy /e /y bin\debug\CustomWebServiceSample.dll %BINDIR%
xcopy /e /y /q 12\* "%TEMPLATEDIR%"

Compile, build/deploy your wsp, or run the post build script, and you should be ready to go.

image

 

It really is that simple to deploy your custom SharePoint web services.  The important take aways here is that this works with standard solution package deployment and is very simple as opposed to some of the much more complex ways I have seen people doing this.

Posted by joshuag | 3 Comments
Filed under:

Why cheap developers will cost you

I’m going to take a break from the normal techno-speak ASP.NET and SharePoint for a minute because I was recently reading something that really struck a chord with me as I’ve been experiencing a lot of it lately.  I was reading this article on The Daily WTF today, mainly because I had just submitted an item for their Code Snippet of the Date (CodeSOD) section.  For those of you who don’t know The Daily WTF and CodeSOD contain articles about real issues developers and IT professionals encounter on a daily basis.  I was reading this article on “Spaced Out” and I came across a comment by someone named Captain Kirk

As a customer your choices are:

1. Hire an experienced competent developer who understands the library functions, knows how to write properly structured code with appropriate variable scope and data types, or

2. Hire someone who will produce what appears to be exactly the same output.

As a customer who doesn't happen to be a developer himself, the above two options translate as:

1. Hire an expensive expensive expensive who expensive the expensive expensive, knows how to write expensive expensive code with expensive expensive expensive and expensive expensive, or

2. Save a bunch of money and get exactly the same thing.

At this time I thought to myself, been there, done that.  I kept reading and came across this comment

Cpt Kirk: who are you? Why don't you put this in a blog and post a link to it so we can all share it? Seriously, the sarcasm & wit may have made my day, but the point is actually very insightful...

That second comment was enough to make me write this post.

Captain Kirk’s comment was sarcastic in nature, but he was echoing the sentiment of the mistake I’ve seen many of my customers make time and time again.  A project I was recently on made the same mistake, hence why I was at The Daily WTF in the first place :).  My goal here is to illustrate why you should care about code quality, and how the upfront cost never really translates to the bottom line.

Software never ends at version 1.0

Anyone who has ever designed, developed or used any piece of software knows that software is never done at version 1.0.  There will always be another version.  Users are inherently unhappy with any software that is delivered to them (including myself) and good software just takes longer for users to be unhappy about.  A sign of a good developer is his ability to isolate pieces of functionality into neat and tiny boxes.  Low quality code ALWAYS is the opposite of this.  Imagine you are in a large department store, like Macy’s, and you want to buy a white t-shirt.  The only problem is, this “department” store has no departments.  Everything is just thrown together all over the place.  Some on racks, some in boxes, etc.  How much longer would it take you to find that white t-shirt?  The same issue applies to low quality code.  Anytime you need to make a bug fix or feature change/addition it’s going to take at least five times as long because the code is such a mess.  Something that seems like it should take 1 hour to fix could end up taking 2 weeks.  This same issue actually applies during development as well because we all know there are always bugs and changes before you even launch the first version.

Inconsistencies

Good developers also do another thing that bad developers don’t, they know how to write re-useable, centralized code.  This means that with bad developers you will get inconsistent behavior across your application because the same thing is done in different ways throughout the application.  Have you ever come across a sink where the hot water was on the right and the cold water was on the left?  This probably bothered you because the standard is to have the hot on the left, cold on the right.  When you are writing a custom application this inconsistency translates into bugs, and to compound the problem, when you fix these bugs you usually have to update the code in many places instead of just one.  So in many cases you can introduce more bugs by fixing the original ones.  In turn this results in longer development and test cycles.

Performance

This is a big one because most customers I come across don’t give this a thought.  They think that developers just build applications and they are going to work and respond well.  Good developers think about performance all the time, they don’t obsess over it, but it’s always in their mind.  Bad developers don’t even flirt with the topic, if they even understand the performance implications of what they are doing.  This means that unless your application is very small and for a very small set of users, you are going to spend a good amount of time at the end of your project doing performance tuning that you probably didn’t plan for.

Maintenance and Operations

Bad developers write the kind of code that your IT department loathes.  This is the application that is always breaking and causing them heartache.  In the long run this application is going to cost you a whole lot more to support and maintain then a well written one would.  Your users will have disdain for it as well, is that the kind of legacy you want to leave behind?

Get what you want

In the end the bad developers will probably satisfy the requirements, and they may or may not end up delivering the first version for cheaper than the good developers would have.  The reality is, you aren’t going to get what you want, they never are going to deliver more than expected and give you something that really blows people away.  Do you want something that just makes the grade, or do you want something that was better than you expected?  If you have a health problem and are looking for a doctor, do you look for the cheapest one that does what you need?  Or do you look for the one that will do the best job?

 

Writing this may not have any impact, but it did make me feel a little better :).  If I save just one project manager from this mistake then it was worth it.  Happy coding!

Posted by joshuag | 13 Comments
Filed under:

DataFormWebPart Parameters and ParameterBindings

Since my post on Easily Making the DataFormWebPart Reuseable, I’ve had a couple questions on the parameters and how to make use of them.  I’m going to attempt to document some of the more useful parameters, but hopefully provide a framework for you to be able to figure out how to do anything you may need with the DFWP.

The first thing you need inside your DFWP is a data source.  This goes inside the <DataSources> node in the DFWP markup.  You’ll notice I put some “DataFormParameters” inside the <SelectParameters> node.

<SharePoint:SPDataSource runat="server" 
		DataSourceMode="List" 
		UseInternalName="true" 

selectcommand="<View><Query><Where><Eq><FieldRef Name='Meal'/>

<Value Type='Choice'>{MealQs}</Value></Eq></Where></Query></View>"

id="Cafeteria_x0020_Menu1"><SelectParameters>

<WebPartPages:DataFormParameter

			Name="ListName" 
			ParameterKey="ListName" 
			PropertyName="ParameterValues" 

DefaultValue="Cafeteria Menu"/>

<WebPartPages:dataformparameter

			runat="server" 
			Name="Meal" 
			ParameterKey="Meal" 
			PropertyName="ParameterValues" 

DefaultValue="Breakfast"/>

<WebPartPages:dataformparameter

			runat="server" 
			Name="MealQs" 
			ParameterKey="MealQs" 
			PropertyName="ParameterValues" 
			DefaultValue="Breakfast"/>
		</SelectParameters>
		</SharePoint:SPDataSource>

In this example I’m using ListName, this allows me to reference the list by name instead of GUID, making this solution re-useable on other SharePoint sites since every time a list is created it get’s a new GUID.

I’ve also added two parameters to pickup the meal I’m trying to display.  One of these parameters comes from a drop down list on the page, the other comes from a query string variable.  More on this later.

If you want to limit the number of rows in a query you can include the “MaximumRows” parameter in your <SelectParameters> section.

<asp:Parameter Name="MaximumRows" DefaultValue="500"/>

By default, the DFWP will query the current sub site. If you are looking to query items from a list in another sub site you can include the “WebUrl” parameter.  Possible options here are “{sitecollectionroot}” or the path to the actual web “/services/cafeteria”.

<asp:Parameter Name="WebUrl" DefaultValue="{sitecollectionroot}"/>

Next is the <ParameterBindings> section.  This is where you can bind variables in your query to controls or the querystring.

<ParameterBinding Name="UserID" Location="CAMLVariable" DefaultValue="CurrentUserName"/>
<ParameterBinding Name="Today" Location="CAMLVariable" DefaultValue="CurrentDate"/>
<ParameterBinding Name="Meal" Location="Control(ddlMeal)" DefaultValue="Breakfast"/>
<ParameterBinding Name="MealQs" Location="QueryString(m)" DefaultValue="Breakfast"/>

“UserID” and “Today” are two variables inserted automatically by SharePoint.  I’m not sure what else is available here, if anyone has any more info on that I’d love to know.

“Meal” and “MealQs” are two that I created to bind to the query above.  You’ll notice in the “selectcommand” that I’m referencing “{MealQs}”.  Here is where I declare that “MealQs” is a value from the querystring variable m.  So adding “?m=breakfast” onto the end of my URL will give me only breakfast items.

“Meal” does the same thing as MealQs except it is bound to a drop down list I created called “ddlMeal”.

In summary, to bind to a control the syntax is “Control(id of the control)”.  To bind to a querystring variable the syntax is “QueryString(variable name)”.  Once you do this step you are free to use these in your “selectcommand” in your SPDataSource.

Hope that was helpful.  If there are any other topics or questions you want to know about concerning the DataFormWebPart let me know, if there is interest in something I’ll write up a post about it.

Posted by joshuag | 5 Comments

Changing the SharePoint Welcome Page through code

Sometimes you will have a feature that you activate on a sub site where you want to change the “Welcome Page” or landing page to be a custom one that you provision through your feature.  I was doing this the other day and realized that it isn’t blatantly obvious through Intellisense because it is called “DefaultPage”.  Here is a sample on how you would change default.aspx to be a file home.aspx that you have just created.

 

PublishingWeb pubWeb = PublishingWeb.GetPublishingWeb(rootWeb);
SPFile homePageFile = rootWeb.GetFile("Pages/Home.aspx");

pubWeb.DefaultPage = homePageFile;

Hope that comes in handy at some point.

Posted by joshuag | 1 Comments

Custom Date Formats in SharePoint XSL

There are quite a few posts out there on this topic, but I’m yet to find one comprehensive post that walks through this beginning to end and actually works.  Let’s give it a go.

A very common scenario for SharePoint publishing sites is to customize the look to suit the customers needs.  Usually this is done with a Content Query Web Part and some custom XSL.  When doing this very often you need to display a date.  You will quickly notice that just displaying the date that SharePoint gives you is not going to be sufficient.  If you just did the standard

<xsl:value-of select="@ArticleStartDate"/>

You get back a pretty nasty looking result

2009-03-23 00:00:00

 

However if you use the “FormatDate” function, you can make this look a lot better.

<xsl:value-of select="ddwrt:FormatDate(@ArticleStartDate, 2057, 3)"/>

Results in this

23 March 2009

All you need to do to make sure the “FormatDate” function is available in your custom XSL files is to make sure you reference the ddwrt namespace.

xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime

Once this has been added to the rest of your namespace declarations at the top of your <xsl:stylesheet> tag, you should be able to use the “FormatDate” function anywhere you like.  Here is sample of what a full XSL file would look like that does this.

<xsl:stylesheet 
  version="1.0" 
  exclude-result-prefixes="x d xsl msxsl cmswrt"
  xmlns:x="http://www.w3.org/2001/XMLSchema" 
  xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" 
  xmlns:cmswrt="http://schemas.microsoft.com/WebParts/v3/Publishing/runtime"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
    
    <xsl:template name="Default" match="*" mode="itemstyle">
	<xsl:value-of select="ddwrt:FormatDate(@ArticleStartDate, 2057, 3)"/>			
    </xsl:template>
   
</xsl:stylesheet>

Here are the details on the different formats you can get by changing the parameters.  March 23 12:00 AM was used as input for all outputs.

Output Locale Format
3/23/2009 1033 1
3/23/2009 12:00 AM 1033 2
Monday, March 23 2009 1033 3
12:00 AM 1033 4
Monday, March 23, 2009 12:00 AM 1033 7
3/23/2009 12:00:00 AM 1033 13
Monday, March 23, 2009 12:00:00 AM 1033 15
23/03/2009 2057 1
3/23/2009 12:00 AM 2057 2
23 March 2009 2057 3
00:00 2057 4
23/03/2009 00:00 2057 5
23 March 2009 00:00 2057 7
00:00:00 2057 12
23/03/2009 00:00:00 2057 13
23 March 2009 00:00:00 2057 15

You can also get a list of all the available locale’s here.

Posted by joshuag | 2 Comments

Using Resource Files (.resx) when developing SharePoint solutions

In the global economy that we live in today, we can no longer assume that everyone using our applications will be speaking English.  Currently, it is pretty easy to build a Windows or Web .NET application with support for multiple languages by making use of resources.  In SharePoint this can be a bit more difficult, and I have yet to see someone lay out the details on exactly how to do this, so here we go…

The first thing you will need to do is create your new resource file.  You can do this in Visual Studio by selecting “Add new item/Resources file”.  The resource file should get deployed to the 12\Resources directory.  In my case I created one called “helloResources”.

The first place you will probably want to use your resources files\ is in your feature.xml file.  In this case you need to specify one additional line in your feature.xml which is:

DefaultResourceFile="helloResources"

Once you have done this you can specify your feature title and description to be pulled from the resources file instead of hardcoded into your feature.xml file.

Title="$Resources:helloResources,FeatureTitle;"

Description="$Resources:helloResources,FeatureDesc;"

The next place you might want to use your resources is in your feature receiver.  For the sake of example I created a feature receiver that creates a list called “Localized Name” and adds an item to the list with the title “My localized list item”.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPSite currentSite = properties.Feature.Parent as SPSite;
            SPWeb currentWeb = currentSite.RootWeb;
            uint language = currentWeb != null ? currentWeb.Language : 1033;
            SPList list = null;

string listName = SPUtility.GetLocalizedString(

"$Resources:helloResources,LocalizedListName", "helloResources", language);

string listDesc = SPUtility.GetLocalizedString(

"$Resources:helloResources,LocalizedListDesc;", "helloResources", language);


Guid listId = currentSite.RootWeb.Lists.Add(listName,

listDesc, SPListTemplateType.GenericList);


            list = currentSite.RootWeb.Lists[listId];
            list.ContentTypesEnabled = false;
            list.OnQuickLaunch = true;
            list.EnableAttachments = false;
            list.EnableVersioning = false;
            list.NoCrawl = true;
            list.Update();

            SPListItemCollection listItems = currentWeb.Lists[listName].Items;
            SPListItem item = listItems.Add();

item["Title"] = SPUtility.GetLocalizedString(

"$Resources:helloResources,LocalizedListItem", "helloResources", language);

            item.Update();
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPSite currentSite = properties.Feature.Parent as SPSite;
            SPWeb currentWeb = currentSite.RootWeb;
            uint language = currentWeb != null ? currentWeb.Language : 1033;

string listName = SPUtility.GetLocalizedString(

"$Resources:helloResources,LocalizedListName", "helloResources", language);


            SPList list = currentWeb.Lists[listName];
            list.Delete();

        }

All of these strings are stored in the resources file of course :).  Just for reference, here is a screenshot of my resource file.

image

And the feature…

image

 

 

And the list…

image

 

You can follow the same code example for web parts or any other controls in your SharePoint application.  I used almost the exact code snipped from my feature receiver to create a web part.

public class Hello : System.Web.UI.WebControls.WebParts.WebPart
    {
        uint language = SPContext.Current.Web != null ? SPContext.Current.Web.Language : 1033;

        public Hello()
        {

        }

        protected override void CreateChildControls()
        {
            Literal literal = new Literal();

literal.Text = SPUtility.GetLocalizedString(

"$Resources:helloResources,WebpartText", "helloResources", language);

            this.Controls.Add(literal);
            base.CreateChildControls();
        }
    }

image

That’s pretty much all it takes to create your SharePoint solutions in a localizable way.  Hope that was helpful.

Posted by joshuag | 4 Comments
Filed under:

Easily Making the Data Form Web Part reusable

There are plenty of examples out there of the many capabilities of the so called "Swiss Army" Web Part, the Data Form Web Part.  Although a lot of developers know about the capabilities, many of them dismiss this Web Part as a one-off, not a versatile re-useable Web Part.  I ran into such a situation recently on a project where one of the developers wanted to write a custom Web Part to display some data that was easily done by the Data Form Web Part.  His reason for writing a custom Web Part was because the requirement was for this Web Part to be re-usable across site collections.  The goal of this post is to show how to easily transform a one-off Data Form Web Part into a re-useable web part, with no code.  Let's get started...

The first thing you will want to do is build your Data Form Web Part.  If you are simply accessing an external data source such as a web service then you can skip the next step.  You can see an example of using the Flickr web services here.  There are plenty more examples of how to use the Data Form Web Part over on the SharePoint Designer Team Blog.

If your data source is SharePoint content such as a list or library you will need to make sure that you update your SPDataSource to be Site Collection agnostic.  The most important part of this is removing the "ListID" paramter from the paramter list.  Here is an example of what was generated by SharePoint designer and what it looked like after I modified it.

SharePoint Designer

<WebPartPages:DataFormWebPart runat="server" IsIncluded="True" FrameType="None" NoDefaultStyle="TRUE" ViewFlag="0" Title="Important Phone Numbers" ListName="{1EC8059A-4D81-4AD9-88D2-56B47F6BDCF2}" Default="FALSE" 
DisplayName="Important Phone Numbers" __markuptype="vsattributemarkup" __WebPartId="{69639AAA-B062-42E5-9C34-11700E80E785}" id="g_69639aaa_b062_42e5_9c34_11700e80e785">
    <DataSources>
        <SharePoint:SPDataSource 
            runat="server" 
            DataSourceMode="List" 
            UseInternalName="true" 
            selectcommand="&lt;View&gt;&lt;/View&gt;" id="Important_x0020_Phone_x0020_Numbers1">
            <SelectParameters>
                <WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="1EC8059A-4D81-4AD9-88D2-56B47F6BDCF2"/>
            </SelectParameters>
            <DeleteParameters>
                <WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="1EC8059A-4D81-4AD9-88D2-56B47F6BDCF2"/>
            </DeleteParameters>
            <UpdateParameters>
                <WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="1EC8059A-4D81-4AD9-88D2-56B47F6BDCF2"/>
            </UpdateParameters>
            <InsertParameters>
                <WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="1EC8059A-4D81-4AD9-88D2-56B47F6BDCF2"/>
            </InsertParameters>
        </SharePoint:SPDataSource>
    </DataSources>

After Modifications

<WebPartPages:DataFormWebPart runat="server" IsIncluded="True" FrameType="None" NoDefaultStyle="TRUE" ViewFlag="0" Title="Important Phone Numbers" ListName="{1EC8059A-4D81-4AD9-88D2-56B47F6BDCF2}" Default="FALSE" 
DisplayName="Important Phone Numbers" __markuptype="vsattributemarkup" __WebPartId="{69639AAA-B062-42E5-9C34-11700E80E785}" id="g_69639aaa_b062_42e5_9c34_11700e80e785">
    <DataSources>
        <SharePoint:SPDataSource 
            runat="server" 
            DataSourceMode="List" 
            UseInternalName="true" 
            selectcommand="&lt;View&gt;&lt;/View&gt;" id="Important_x0020_Phone_x0020_Numbers1">
            <SelectParameters>
                <WebPartPages:DataFormParameter Name="ListName" ParameterKey="ListName" PropertyName="ParameterValues" DefaultValue="Important Phone Numbers" />
                <asp:Parameter runat="server" Name="WebUrl" DefaultValue="{sitecollectionroot}"/>
                <asp:Parameter Name="MaximumRows" DefaultValue="2"/>
            </SelectParameters>
        </SharePoint:SPDataSource>
    </DataSources>
    
Since we are just displaying data I removed the Insert, Update, and Delete parameter collections.  The important modification I made to the "SelectParameters" parameter collection was to change the "ListID" parameter to "ListName".  
This will get the list by name instead of GUID, which is important if you are going to reuse this Web Part in another Site Collection.  At this point you can continue updating your DFWP by modifying the XSL or adding some parameters from the querystring
or other controls on the page, there are plenty examples out there of how to do this, I won't go into detail here.
 
Next we need to create a reusable web part out of this DFWP.  To do this we will place the DFWP in a Web Part Zone and export through the standard SharePoint browser interface.
Here is an example of my markup:
<WebPartPages:WebPartZone 
    runat="server" 
    AllowPersonalization="false" 
    ID="TopZone" 
    Title="Top Zone" 
    Orientation="Horizontal">
<ZoneTemplate>

<WebPartPages:DataFormWebPart runat="server" IsIncluded="True" FrameType="None" NoDefaultStyle="TRUE" ViewFlag="0" Title="Important Phone Numbers" ListName="{1EC8059A-4D81-4AD9-88D2-56B47F6BDCF2}" Default="FALSE" DisplayName="Important Phone Numbers" __markuptype="vsattributemarkup" __WebPartId="{69639AAA-B062-42E5-9C34-11700E80E785}" id="g_69639aaa_b062_42e5_9c34_11700e80e785" partorder="1">

....[omitted for brevity]

</WebPartPages:DataFormWebPart>

</ZoneTemplate>
</WebPartPages:WebPartZone>
 
Now when I go to my page I can edit the page, and select export.  
image 
This will produce a .webpart file that I can then package in my features or use in other web part galleries to create an easily reusable DFWP.
 
 
 
Posted by joshuag | 5 Comments

Getting the Current User Name or ID in SharePoint using SharePoint Designer (no code)

A colleague sparked my interest in this today by asking me if getting the current user ID was possible without writing code.  This would be a very trivial task by creating a custom web part but with that comes deployment, maintenance, support, etc.  He had a simple requirement to pass the UserID in the query string and thought it would be overkill to do this using code, and I agreed.  This put me on a mission to find out if this was possible using SharePoint designer.  The usual Internet searches didn't return anything useful so once I figured it out I decided that this needed to be posted somewhere.  So here is how you do it:

 

Using SharePoint designer open up the Data Source Library Pane

Hover over any library or list, using the drop down select "Show Data"

image

Next you will want to have SharePoint Designer insert a DataFormWebPart for you.  These last two steps aren't really necessary, you could write all the markup by hand, but I find it is usually easier to have SPD do this for you, and then go back and clean it up, than it is to remember everything that you need and start from scratch.  For this part I selected the Title field and used a Multiple Item View Form, you can select different fields/views as your situation calls for.

image

Now we can get to what this post was really about, getting the UserName and/or the User ID.  To get the UserName all you need to do is add an XSL parameter.  A little known fact here is that any parameter that is found in the parameter bindings section of the data source element can also be passed to the XSL using an XSL parameter.  To be clear about what I added refer to the screen shot below, I have removed all the irrelevant XSL to make this easier to understand.

image 

The above will show you the what SharePoint display's the user name as.  This was a step in the right direction, but I still needed to actual user ID.  To get this I decided to go the server variable route.  Here is the documentation straight from MSDN on the three variables that are probably of interested for this situation.

AUTH_USER

The name of the user as it is derived from the authorization header sent by the client, before the user name is mapped to a Windows account. This variable is no different from REMOTE_USER. If you have an authentication filter installed on your Web server that maps incoming users to accounts, use LOGON_USER to view the mapped user name.

LOGON_USER

The Windows account that the user is impersonating while connected to your Web server. Use REMOTE_USER, UNMAPPED_REMOTE_USER, or AUTH_USER to view the raw user name that is contained in the request header. The only time LOGON_USER holds a different value than these other variables is if you have an authentication filter installed.

REMOTE_USER

The name of the user as it is derived from the authorization header sent by the client, before the user name is mapped to a Windows account. If you have an authentication filter installed on your Web server that maps incoming users to accounts, use LOGON_USER to view the mapped user name.

I decided to use the LOGON_USER variable, but note that you can use any server variable here, your not limited by SPD in any way.  To do this I had to add the server variable to the ParameterBindings as well as the XSL.  The end result of my DataFormWebPart is below.

<WebPartPages:DataFormWebPart runat="server" 
        IsIncluded="True" 
        FrameType="None" 
        NoDefaultStyle="TRUE" 
        ViewFlag="0" 
        Title="Shared Documents" 
        ListName="{EF49CFE7-C6C4-42D6-8FCB-B10D6CDC0A35}" 
        Default="FALSE" 
        DisplayName="Shared Documents" 
        __markuptype="vsattributemarkup" _
        _WebPartId="{E39B973F-F567-4AE6-9913-36710D1ADA1A}" 
        id="g_e39b973f_f567_4ae6_9913_36710d1ada1a" 
        __AllowXSLTEditing="true" 
        WebPart="true" 
        Height="" 
        Width="" __WebPartId="{633D5B43-5675-4363-8F69-9BA87A3776ED}">
    <DataSources>
        <SharePoint:SPDataSource runat="server" 
            DataSourceMode="List" 
            UseInternalName="true" 
            selectcommand="&lt;View&gt;&lt;/View&gt;" 
            id="Shared_x0020_Documents1"><SelectParameters><WebPartPages:DataFormParameter 
            Name="ListID" 
            ParameterKey="ListID" 
            PropertyName="ParameterValues" 
            DefaultValue="EF49CFE7-C6C4-42D6-8FCB-B10D6CDC0A35"/>
        </SelectParameters></SharePoint:SPDataSource>
    </DataSources>
    <ParameterBindings>
    <ParameterBinding Name="ListID" Location="None" DefaultValue="EF49CFE7-C6C4-42D6-8FCB-B10D6CDC0A35"/>
    <ParameterBinding Name="dvt_apos" Location="Postback;Connection"/>
    <ParameterBinding Name="UserID" Location="CAMLVariable" DefaultValue="CurrentUserName"/>
    <ParameterBinding Name="Today" Location="CAMLVariable" DefaultValue="CurrentDate"/>
    <ParameterBinding Name="LogonUser" Location="ServerVariable(LOGON_USER)"/>
</ParameterBindings>
    <datafields>@FileLeafRef,Name (for use in forms);@Title,Title;@Approval,Approval;
    @CollectS,Collect Signatures;@CollectF,Collect Feedback;@ID,ID;@ContentType,Content Type;
    @Created,Created;@Author,Created By;@Modified,Modified;@Editor,Modified By;
    @_CopySource,Copy Source;@CheckoutUser,Checked Out To;@_CheckinComment,Check In Comment;
    @CheckedOutTitle,Checked Out To;@CheckedOutUserId,ID of the User who has the item Checked Out;
    @FileDirRef,Path;@FSObjType,Item Type;@HTML_x0020_File_x0020_Type,HTML File Type;
    @File_x0020_Type,File Type;@IsCheckedoutToLocal,Is Checked out to local;@_SourceUrl,Source Url;
    @_HasCopyDestinations,Has Copy Destinations;@ContentTypeId,Content Type ID;@_ModerationStatus,Approval Status;
    @_UIVersion,UI Version;@Created_x0020_Date,Created;@FileRef,URL Path;@File_x0020_Size,File Size;
    @_UIVersionString,Version;@ParentVersionString,Source Version (Converted Document);
    @ParentLeafName,Source Name (Converted Document);@TemplateUrl,Template Link;</datafields>
    <XSL>
<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" 
                xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" 
                version="1.0" 
                exclude-result-prefixes="xsl msxsl ddwrt" 
                xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" 
                xmlns:asp="http://schemas.microsoft.com/ASPNET/20" 
                xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
                xmlns:SharePoint="Microsoft.SharePoint.WebControls" 
                xmlns:ddwrt2="urn:frontpage:internal">
    <xsl:output method="html" indent="no"/>
    <xsl:decimal-format NaN=""/>
    <xsl:param name="dvt_apos">'</xsl:param>
    <xsl:param name="UserID"/>
    <xsl:param name="LogonUser"/>
    <xsl:variable name="dvt_1_automode">0</xsl:variable>
    <xsl:template match="/">
    
    <xsl:value-of select="$UserID"/><br/>
    <xsl:value-of select="$LogonUser"/>
    
    </xsl:template>
    </xsl:stylesheet>    </XSL>
</WebPartPages:DataFormWebPart>
And the screen shot...
image  

 

And that does it, you can now show user name or user ID without using any code.  An important side lesson of this is the fact that you can use anything in the parameter binding session in your XSL as well. 

 

Hope this helps.

Posted by joshuag | 21 Comments

FileNotFoundException: The site with the id {GUID} could not be found.

This was an interesting problem that I ran into today that really stumped me for a while.  The scenario was this, we had a flash file (swf) sitting in a document library.  This flash file was being served up on the home page of the site.  All of a sudden, flash file disappears.  I took a look in one of my favorite tools, Fiddler, and found that it was getting a 404 on the flash file.  I then navigated to the file manually and tried to open it.  When I clicked on the link in the document library I got an error stating "System.IO.FileNotFoundException The site with the id {some guid} could not be found."  It was basically like this file didn't exist, but there it was in the document library staring me in the face.  I tried the usual tactics, download a copy, open it up to verify the file is alright, delete the file in the doc library, upload the file again...same result.  I tried renaming it once I uploaded it and amazingly this worked.  This led me to believe that it was something to do with the BLOB cache.  For those of you who don't know the BLOB cache is a disk based cache used for storing images, css, javascript, etc.  By default this setting is off, but we had turned it on, as I think most people should.  This allows IE to cache these objects which is a very good thing for publishing sites.  It also reduces trips to the DB because the files are available on the hard disk directly on the WFE.  This led me to try resetting the cache.  You can do this through Site Actions/Site Settings/Modify All Site Settings/Site Collection Object Cache.  I checked all three checkboxes here and clicked OK and voila, flash file is working again.  My theory is that somehow the disk cache got out of sync and SharePoint was looking for a file on the disk that wasn't there, but was in the DB.  If anyone else has run into this issue I'd be curious to hear your experiences.

Posted by joshuag | 11 Comments

Add SharePoint lookup column declaratively through CAML XML

UPDATE:  In the comments I’ve seen some people saying that this doesn’t work for them.  One thing that I discovered recently is that, in order for this to work, the URL specified in your schema.xml file MUST MATCH the List attribute on all your Field references in field.xml and schema.xml.  If these don’t match, it will not work.  Hope that helps the folks that were having trouble.

 

I've seen a lot of statements recently that say the only way to add a lookup column through a feature is through code.  There are even some interesting solutions around this using Feature Receivers out on codeplex here and here.  When I first started hearing these statements I didn't believe it, so I decided to try to build a feature using only CAML that will create a list with a lookup column.  As it turns out this isn't really that difficult, but it did take some digging.  When you are doing this there are two place that you have to be concerned about.  The first is when defining the column as a site column.  When you define content types and custom list schemas, you should first start with your site columns.  I usually place these in a file called fields.xml, this is also what you will see if you take a look through the out of the box list schemas.  Here is an example of one of my site columns of the Lookup type

<Field ID="{2FF1B484-6D70-449c-8E5C-904E4D5971E1}" Name="Leader" Group="My Custom Columns" Type="Lookup" DisplayName="Leader" List="Lists/Leaders" ShowField="Title" PrependId="TRUE"/>

There are three properties here that are not found on a standard site column definition.  The first is "List", this, as you might have guessed, points to the URL of the list in the site.  In this example I know I am looking for the Title column in a list that is found under Lists/Leaders.  The next property to pay attention to is "ShowField", you guessed it, this is the field that you want to show in your lookup column.  The last property is "PrependId".  I'm not 100% sure on this but I think this has something to do with whether the lookup column returns the id in front of the value.  The out of the box columns set this to "TRUE" and I do like to get back my item ID with my value so I set it to TRUE as well.  Disclaimer: I never tested what setting it to "FALSE" does.

The next thing you will want to do is add this column to a custom list schema that you define.  If you aren't familiar with custom list schemas Ted Pattison's book Inside WSS 3.0 is a great place to get familiar with this.  Quickly summarized, whenever you create a custom list, there must be a schema.xml file in a folder that is the same name as the list name.  That sounds kind of confusing, but describing how to build custom list schemas is out of the scope of this post.  Ted's book explains this really well however, and if you are a SharePoint 2007 developer and haven't read this yet, go buy it.  Anyway, in the custom list schema one of the first sections is called "Fields".  In this section, if I wanted to add my custom lookup site column to my custom list I would use the following line of CAML.

 

<Field ID="{2FF1B484-6D70-449c-8E5C-904E4D5971E1}" Name="Leader" Type="Lookup" DisplayName="Leader" List="Lists/Leaders" ShowField="Title"/>

Again, if you haven't worked with this stuff before this might seem kind of redundant.  Truthfully it is, but that is just how custom list schemas work, so get used to it.  Really, that is all there is to it, everything else you want to create (content types, list templates, list instances) is all the same for lookup columns as it is a standard text column.

Hope that helps to clear up some confusion around lookup columns.

Posted by joshuag | 22 Comments

Using XSLT in your SharePoint web part

I've been doing a lot of content management work with SharePoint lately, which means a lot of the same functionality but always a different look and feel.  XSLT is one of the main pillars of SharePoint development, and if you haven't been using it or haven't been using it extensively than you are doing yourself a huge disservice.  Every time I create a SharePoint web part or control I always make the presentation use an XSL style sheet and allow that style sheet to be configured through a property.  This is very similar to how the Content Query WebPart works, just most people end up using the default styles that come out of the box.  This has saved me many times from redeveloping a web part or control just because the HTML or styling isn't just how the customer wants it.  It is also a great way to get HTML markup out of your code, which just makes the code extremely hard to read.  I'm going to show you how to use XSLT in all of your web parts from now on with a few simple examples.  Here it goes....

 

The first example is when you are using a response from a datasource that returns XML already.  The example I'm going to use is a Yahoo RSS feed.  I know that for this you could simply use the RSS web part and modify that XSL but this is for demonstration purposes.  You might have a XML Document for a data source.  The first thing we need to do is get the XML.  I did this as an Async method so it might be a little different if you aren't using Async methods.

 

IAsyncResult BeginGetIndustryNews(Object sender, EventArgs e, AsyncCallback cb, object state)
    {
        industrynewsReq = (HttpWebRequest)WebRequest.Create(industryNewsUrl);

        industrynewsReq.Method = "GET";

        industrynewsReq.Proxy = System.Net.GlobalProxySelection.GetEmptyWebProxy();

        industryNewsResult = industrynewsReq.BeginGetResponse(cb, industrynewsReq);

        return industryNewsResult;
    }

    void EndGetIndustryNews(IAsyncResult asyncResult)

    {

        if (industryNewsResult != null)

        {

            WebResponse response2 = (WebResponse)industrynewsReq.EndGetResponse(asyncResult);

            Stream streamResponse2 = response2.GetResponseStream();

            industryNewsHtml.Text = GetTransformResults(industryNewsXsl, streamResponse2);

            response2.Close();

            streamResponse2.Close();

        }

    }

private string GetTransformResults(string xslLink, Stream responseStream)

    {

        XmlUrlResolver resolver = new XmlUrlResolver();

        resolver.Credentials = CredentialCache.DefaultCredentials;

        XmlTextReader stylesheet = new XmlTextReader(SPContext.Current.Site.Url + xslLink);

        stylesheet.XmlResolver = resolver;

        XslCompiledTransform transform = new XslCompiledTransform();

        transform.Load(stylesheet);

        StringBuilder sb = new StringBuilder();

        TextWriter writer = new StringWriter(sb);

        using (XmlReader reader = new XmlTextReader(responseStream))

        {

            XmlTextWriter results = new XmlTextWriter(writer);

            transform.Transform(reader, results);

            reader.Close();

        }

        return sb.ToString();

    }

If you haven't figured it out already the important method here is the "GetTransformResults".  This is the method that takes the input stream and applies the XSL to it.  In this method I create an XmlUrlResolver in order to use the current users credentials when accessing the XSL stylesheet.  I then use a XmlTextReader to get the stylesheet.  I then pass the XslCompiledTransform the XmlTextReader of the style sheet and call the transform method, writing the results to a StringBuilder.  When I return this StringBuilder to the calling method what I am returning is a string of HTML that was created by transforming the XML data with the XSL transform.  When I get the result in the EndGetIndustryNews method I set the Text property of the IndustryNewsHTML control, which is simply a literal control on the page.  That's it!  You now have a reusable control that you can change the look and feel of very easily without changing any code.  In case you are wondering industryNewsXsl is a property on this control that can be set to any Xsl stylesheet in the site collection.  To keep things consistent I usually place mine in the /style library/xsl style sheets folder.

This is all great but what if you have an object that contains the data that you want to transform?  Surprisingly this is even easier.  Simply use the built in XmlSerializer to get the XML representation of your object like so.

System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Newsletter));

MemoryStream memoryStream = new MemoryStream();
serializer.Serialize(memoryStream, newsletter);
memoryStream.Position = 0;

newsHtml.Text = GetTransformResults(newsXsl, memoryStream);

In this case you might have a little trouble if you don't know exactly what the XML looks like that is coming out of the serializer.  In case you want to get a look at the raw XML (which you probably will) you can use the style sheet below which will output in a textarea the bare XML.

<xsl:stylesheet version="1.0" exclude-result-prefixes="x d xsl msxsl cmswrt" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" xmlns:cmswrt="http://schemas.microsoft.com/WebParts/v3/Publishing/runtime" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">


<xsl:template match="/">
    <textarea rows="20" cols="100">
        <xsl:apply-templates/>
    </textarea>
    
</xsl:template>
<xsl:template match="node()|@*">
    <!-- Copy the current node -->
        <xsl:copy>
        <!-- Including any child nodes it has and any attributes -->
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

That's all there is to this really.  Hopefully this will help you in writing all your web parts and controls in SharePoint to take advantage of XSL!

 

Posted by joshuag | 3 Comments
More Posts Next page »
 
Page view tracker