MySite link back to portal

I have seen a lot of people looking for a simple way to create a link on the mysites pages within SharePoint 2007 back to the main portal site.   The issue is that the MySites are in a seperate site collection within the portal, therefore the navigation does not carry over.

Here are a few solutions I have seen:

  1. Write some code and deploy a feature which causes the mysites to use a different master page that has your new link on it.   This is ok, but seems to be troublesome to get working.   It also is overkill when you just want to get a single link back to the portal working.
  2. Use the personalization site links option in the shared services provider administration page to add a link back to the portal.   This does get you back to the portal but has a nasty feature which causes the navigation on your portal to look all screwy.   This wonderful feature is the addition of the MySiteView=1 parameter being tacked to the end of any URL.
  3. Users could create a site portal connection back to the main portal.   But this requires the user to have a good understanding of SharePoint.   Since this is a manual process most end users are not happy with this idea.
  4. Create an automated scheduled process using the SharePoint APIs to automatically add the site portal connection.  (haven’t really looked into this, but I am guessing it would be possible).

So none of those solutions really were simple or gave the results I needed.    I came up with a simple solution that requires just a little bit of work using SharePoint designer.  (download a free trial if you don’t have it).

1. Download the redirect.aspx page located here.

2. Using SharePoint designer import the redirect.aspx page into the root of your Portal.

3. Open up SharePoint central administration and go to the shared services section.

4. Click on the personalization site links option

5. Click new to add a new link back to your portal from the MySites. For the URL enter: http://[servername]/redirect.aspx?URL=http://[servername] (replace [servername] with the name of your portal server)

6. Add a proper description for the link, specify the owner of the link and then click OK.

Go take a look at your mysite and you will now see the link back to your portal. If you do not see the link wait a few minutes and try again. It can take a little while for SharePoint to update the mysite navigation.

A quick warning about this solution: I have only tested this with Internet Explorer. It also requires that JavaScript be enabled on the browser.

I have received feedback that you could also have the redirect code done on the server side so that JavaScript is not an issue.   In that case you would need to take the code behind for the aspx redirect page, compile it with a strong name and deploy it to the server’s GAC  (global assembly cache)

MOSS and Office XP

It has been a while since I have posted anything out here on the blog. I guess it is about time I add a little bit of new content. J

I recently had a situation where a client wanted to utilize the document libraries in MOSS to provide version control and searching. They also wanted to have a unique ID automatically created for each document. This ID must be displayed in the footer of every document, added to the library, AND they must be able to filter the document library by that ID. So far the requirements could be met with little difficulty with the use of Word 2007… but the issue is that they only have Office XP. They do have plans to upgrade to Office 2007 within the year but they needed this solution to work now.

One of the first challenges we ran into was creating a unique ID for each document. It seemed like a simple task since each record in the document library is automatically given an ID by SharePoint. We thought we could easily read that information and use it for filtering. We were wrong. You cannot filter or create custom searches using the ID property of a document. For the final solution we created a new numeric column in the document library called DOCUMENTID. We quickly wrote a small C# console app that made the DOCUMENTID field hidden so that when users check in a document they are not prompted to enter in this value. The next step was to write a simple event handler that we attached to the document library that upon document check-in would grab the ID field from the document library and store it in the DOCUMENTID field. We now have a unique ID that can be searched.

Now that we have a unique ID in the DOCUMENTID field, how do we go about showing this in the footer of the document? To accomplish this task we created a custom Word XP template that has a bit of VBA code in the document load event. We can then on document load read this property and just add the text into the footer of the document. Very simple really.

Having a template is not an issue since the users will always click on the "NEW" button in Sharepoint whenever they wish to create a new Word document. It is interesting to note that the new DOCUMENTID field is a property that also gets embedded into the word document when it is checked into our document library. This happens automatically and requires no coding or special action to occur.

One of the other challenges we had is that when you check in a document using Office 2007 into a version controlled document library you are asked if you want to create a major version, minor version or overwrite the existing minor version. In Office XP you do not get this dialog. In fact the document does not get checked in until the user returns to the website and manually checks in the document. Our client felt that their users may not remember to return to the document library to check in the document. So the requirement was added that upon closing of the document in Office XP a window must appear asking the user to save the document (if not already saved) and then if the document is checked out ask the user if they wish to check in the document as a major version, minor version, overwrite the existing minor version or to keep the document checked out.

Since we have already created a modified Word template for this project we decided to enhance this template with even more VBA code and a custom form. One section of the VBA code checks to make sure that the user saved the document prior to displaying the custom check-in form. The check-in form then asks the user if they would like to check in the document as a major version, minor version, overwrite an existing minor version or keep the document checked out. After the user makes a selection and clicks on an "accept" button the VBA code takes over. If the user selects to keep the document checked out Word XP closes and no further actions occur. If the user selects a check-in option then we utilize a MOSS webservice (using MSSoap.SoapClient object) , passing in the proper check-in parameters, to check the document in. Word XP closes once the document has been checked into MOSS.

As you can see with a little bit of creativity you can integrate even older applications into Microsoft Office SharePoint 2007. I am not able to provide C# or VBA code samples for this article but I am sure with the information presented above you can quickly create a solution that meets your clients needs.

Visual Studio 2005 for Database Professionals

Visit Sogeti (and myself) at the Visual Studio 2005 for Database Professionals launch event in Detroit, Michigan on December 5, 2006. This is a free event where you can learn how Visual Studio Team System improves development team collaboration and increases application quality with better control over database changes and automated database testing.

Visit http://www.teams-deliver.com/ to register.

Also check out the partners page where you will see a brief overview of Sogeti.

Tip: Configuring Search – SharePoint Server 2007

After creating your first SharePoint 2007 site collection depending on the template you selected you may not see the search scope options of "All Sites" or "People" in your search scope dropdown. To make those scopes show up you need to make sure you have a search center site created. Once you have a search center site you will want to go into your site collection administration, select search settings, and then select the option to use custom scopes. You will need to provide the path to your search center site. Once you do this you should now see the "All Sites" and "People" search scopes in your drop down search scope list.

HttpListener and Forms

As shown in the previous few posts the HttpListener class can be very useful for handling basic HTTP requests to your custom application. I have shown how you can use the class to respond to basic HTTP requests, but what if you need to request information from a user? Simple, just have your application create a standard HTML page with the necessary form fields. Then you need to decide what method you will use to send that form data back to your application. You have a choice of using GET or POST. The GET method sends all of the form fields across in the querystring of the HTML request. A POST sends the information as a stream of data inside the actual request.

So what is the best method to use, get or post? It really depends on the amount of data you need to send back to your application. If you only have a few fields then "get" will work fine. If you have a large amount of data or you are trying to upload a file you will need to use "post". Most browsers have a size limit for a URL and since "get" uses the querystring you can easily exceed the URL size limit.

As a quick recap of HTML 101; to create a form that uses the "get" method you would use something like:

<form method="get" action="someURL">

And for a post:

<form method="post" action="someURL">

Now that you know the difference between get and post methods for a form, how do we use that with the HttpListener class? If you are using the "get" method it is pretty simple. In the callback method for your HttpListener class you will use the request object to access the querystring property directly. Below is an example for reading a value from the querystring:

string myValue = request.QueryString["myValue"];

As you can see pulling information out of the querystring is very simple and exactly like you would do when working within ASP.NET. Getting data from a form that used the post method is not so straight forward. The request object for the HttpListener class does not contain a Forms method for retrieving the data. We can, however, get access directly to the request object’s data stream and then parse out the form fields using a custom helper method:

 

private Hashtable GetFormValues(HttpListenerRequest request)

{

Hashtable formVars = new Hashtable();

 

//add request data at bottom of page

if (request.HasEntityBody)

{

System.IO.Stream body = request.InputStream;

System.Text.Encoding encoding = request.ContentEncoding;

System.IO.StreamReader reader = new System.IO.StreamReader(body, encoding);

if (request.ContentType.ToLower() == "application/x-www-form-urlencoded")

{

string s = reader.ReadToEnd();

string[] pairs = s.Split(‘&’);

for (int x = 0; x < pairs.Length; x++)

{

string[] item = pairs[x].Split(‘=’);

formVars.Add(item[0],System.Web.HttpUtility.UrlDecode(item[1]));

}

}

body.Close();

reader.Close();

}

return formVars;

}

 

A couple things to note about the code sample above; we use the HasEntityBody property of the request object to make sure that we do have some data to work with. Once we know we have data we need to use the ContentType property of the request object to make sure the data that was sent is form field data. It is then a simple process of breaking apart the data and placing it into a Hashtable for use in other parts of the application.

You could also modify the method shown above to also pull out all of the variables in the querystring and place them in the Hashtable. This would then provide you with one single place to go for all of your form data.

for (int x = 0; x < request.QueryString.Count; x++)

{

if (!formVars.ContainsKey(request.QueryString.Keys[x]))

{

formVars.Add(request.QueryString.Keys[x], request.QueryString[x]);

}

}

 

To use the GetFormValues method you need to create a new Hashtable object and assign it to the results of the GetFormsValues method. Example:

Hashtable formVals = GetformValues(myRequest);

Now to access a form value you simple use the formVals hashtable:

string myFormValue = formVals["myFormValue"].ToString();

As you can see using a simple helper method you can quickly and easily utilize forms and querystring or form values within your application using the HttpListener class.

HttpListener Continued

As promised, below is a short example of how to use the HttpListener class. After you have started the sample server below, open your web browser and load the URL http://blog.mikehacker.net:8080. You should see the words "Hello World". Try http://blog.mikehacker.net:8080/date to see the current date or http://blog.mikehacker.net:8080/time to see the current time. Very cool eh?

You may need to remove some of the line wraps when you copy the code to VS.NET.

Code:

using System;

using System.Collections.Generic;

using System.Text;

using System.Net;

namespace httplistenertest

{

class Program

{

static void Main(string[] args)

{

HttpListener listener = new HttpListener();

listener.Prefixes.Add("http://blog.mikehacker.net:8080/");

listener.Start();

 

IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListnerCallback), listener);

Console.WriteLine("Waiting for request");

 

while (true) { }

 

listener.Close();

Console.WriteLine("done");

Console.ReadLine();

}

 

public static void ListnerCallback(IAsyncResult result)

{

HttpListener listener = (HttpListener)result.AsyncState;

HttpListenerContext context = listener.EndGetContext(result);

HttpListenerRequest request = context.Request;

HttpListenerResponse response = context.Response;

 

string responseString="";

switch (request.Url.AbsolutePath)

{

case "/time":

responseString = "<html><body>The current time is: " + DateTime.Now.ToLongTimeString() + "</body></html>";

break;

 

case "/date":

responseString = "<html><body>The current date is: " + DateTime.Now.ToLongDateString() + "</body></html>";

break;

 

 

default:

responseString = "<html><body>Hello World!";

break;

}

 

 

byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);

response.ContentLength64 = buffer.Length;

System.IO.Stream output = response.OutputStream;

output.Write(buffer, 0, buffer.Length);

output.Close();

 

listener.BeginGetContext(new AsyncCallback(ListnerCallback), listener);

}

 

}

}

 

Sorry about the code formatting, some reason it was messed up during the publishing of this entry.

 

Dynamic GridView Paging Issue – NullReferenceException

Today I ran into a very strange situation with the ASP.NET 2.0 GridView control.   I was creating a SharePoint 2007 web part which consisted of a gridview control which was being dynamically created during the CreateChildControls method of the web part.    The gridview was being added to the web part’s control collection after the DataBind method was called on the gridview.   Everything was displaying correctly until I turned on paging for the gridview.   Once paging was turned on I started receiving a NullReferenceException error.   I used VS.NET to debug the web part and found that the error was being thrown as soon as the DataBind method was called.     In the stack trace of the error I noticed that the last entry referenced a method called BuildCallbackArgument(Int32 pageIndex).    I did a few searches on the net and came up with no information that was usefull on that method or on the error I was seeing.
 
I finally was able to track down the problem.  It appears that the gridview needs to be added to a controls collection prior to calling the DataBind method.   I modified my code so that immediately after creating the gridview control I added it to the web part’s control collection.  Once I did that the web part worked as expected and the gridview displayed properly.  

Web UI for Windows Service

I have been spending some time thinking about how I could allow a user to interact with a custom Windows service that I had written.   I want to allow the user to alter the configuration of the service and to receive feedback on the different processes occurring inside this service.   In the past I had written a Windows application that used TCP/IP to communicate with the service.    To me this seemed a bit much since I now have to maintain not only a Windows service but also a Windows client application… not to mention all of the work to add the TCP/IP communication layer.    I want something simple to deploy, simple to manage and of course simple to code.

In my search for the ultimate solution I came across a nice little gem in the .NET 2.0/3.0 framework that I had not previously not seen; the HttpListener class.   This class is amazing!  Before jumping to far into this you need to understand that the HttpListner class only works for Windows XP SP2 and Windows Server 2003.   I have not tested it on Vista but I am guessing that it would also work under that operating system;  you can always use the HttpListner.IsSupported static property to check if the run time operating system supports this class.

With the HttpListener class I can easily have my Windows service accept HTTP connections and respond to them appropriately.   This means I can have a simple HTML UI for my Windows Service without any IIS configuration or complex coding.  

The HttpListener class can handle connections in either a synchronous or asynchronous manner.   In my Windows service I have decided to use the asynchronous method so that my service can continue to operate without the need for me to write any complex threading code.    Therefore I utilized the BeginGetContext and EndGetContext methods of the HttpListener class.  

The BeginGetContext method accepts two parameters;  a callback delegate and a state object.   The callback delegate is pretty self-explanatory but you may wonder what the state object is.   The state object is any object that you want passed to the callback method.   I would believe that most of the time you would want to use the instance of your HttpListener class as the state object so you have access to the HttpListener class methods and properties in your callback.

Using HttpListener

Using the HttpListener class is simple.  Below is a quick checklist that will get you started. 

Starting the HttpListener

1.       Verify that the user’s OS supports HttpListener by using the IsSupported static property.

2.       Create an instance of the HttpListener class.

3.       Specify the prefixes that the HttpListener will respond to.  For example http://blog.mikehacker.net:8080

4.       Start the listener using the Start method.

5.       Call the BeginGetContext method passing in the callback delegate and the instance of HttpListener created in step 2.

Responding to connections

1.       Create a new method with the name used for the delegate in the BeginGetContext call.  This method should accept an IASyncResult parameter.

2.       Retrieve the HttpListener instance from the IASyncResult parameter using the get_AsyncState method.

3.       Use the HttpListener object from step #2 to access the context, request and response objects.

4.       Craft your HTML output string and then convert it to a ubyte array.

5.       Specify the length of the output HTML ubyte array in the HttpListener’s response object.   Do this using the set_ContentLength64 method.

6.       Use the HttpListener’s response object’s get_OutputStream method to access the response data stream.  

7.       Write your HTML ubyte array to the output stream.

8.       Call the BeginGetContext method so that the listener can respond to the next connection.

Ok… so starting the HttpListener sounds simple but the responding to connections does appear to be a bit more complicated.   In a future post I will show the code for creating a simple asynchronous HttpListener.   Stay tuned for more…. J

Technology Blog