 |
|
If you have a question or suggestion, please contact me through Windows Live Messenger. My status:
 . If I am not online, please send me an e-mail. |
|
|
|
|
|
 |
|
|
|
|
|
|
|
|
7/5/2008I just added my Content By Type webpart to CodePlex. You can find it in this project. I have removed the installers from the Content By Type site, And they are now available from this CodePlex release page. And if you are wondering what this web part is about, please read these blog postings: Enjoy! 6/30/2008In a lot of places in SharePoint you can select a list (or other objects like a site) in a nice popup dialog. It turns out it is really easy to do that yourself. In this example I will show how to let your users select a list in the webpart toolpart. The code is attached to this post (see link at the bottom of the post). The webpart itself is nothing special, it just renders the relative url to the list that the user has selected: In the CreateChildControls of the EditorPart, we will add a readonly textbox and a button to the controls collection. When clicked, the button calls the javascript function that we will add later. private TextBox _listUrl;
private Button _selectList;
public ListSelectEditorPart(string webPartID)
{
this.ID = "ListSelectEditorPart" + webPartID;
this.Title = "Select a list";
}
protected override void CreateChildControls()
{
base.CreateChildControls();
_listUrl = new TextBox();
_listUrl.Enabled = false;
Controls.Add(_listUrl);
_selectList = new Button();
_selectList.OnClientClick = "javascript:launchPicker();";
_selectList.Text = "...";
Controls.Add(_selectList);
}
Please note the ID that I set in the constructor. This blog post contains an explanation of why I do that. When you create an editorpart you will need to implement SyncChanges and ApplyChanges to read the value to your control and to store your values in the webpart:
public override void SyncChanges()
{
EnsureChildControls();
ListSelectWebPart webPart = WebPartToEdit as ListSelectWebPart;
if (webPart != null)
{
_listUrl.Text = webPart.ListUrl;
}
}
public override bool ApplyChanges()
{
EnsureChildControls();
ListSelectWebPart webPart = WebPartToEdit as ListSelectWebPart;
if (webPart != null)
{
webPart.ListUrl = _listUrl.Text;
}
return true;
}
In the OnLoad for the editorpart, we register the script that will show the popup. You also need to include the PickerTreeDialog.js file (which is localized, so in an en-US site, it is in the 1033 folder).
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string webLocale = SPContext.Current.Web.Locale.LCID.ToString();
Page.ClientScript.RegisterClientScriptInclude("PickerTreeDialog", string.Format("/_layouts/{0}/PickerTreeDialog.js", webLocale));
RegisterSelectListScript();
}
private void RegisterSelectListScript()
{
StringBuilder launchPicker = new StringBuilder();
launchPicker.Append("<SCRIPT LANGUAGE='JavaScript' >");
launchPicker.Append("function launchPicker()\n");
launchPicker.Append("{\n");
launchPicker.Append(" var listurlfield = document.getElementById(\"" + _listUrl.ClientID + "\");\n");
launchPicker.AppendFormat(" var defaulturl = '{0}';\n", SPContext.Current.Web.ServerRelativeUrl);
launchPicker.Append(" var url = defaulturl;\n");
launchPicker.Append(" if(listurlfield != null && listurlfield.value != '')\n");
launchPicker.Append(" {\n");
launchPicker.Append(" url = listurlfield.value.substring(0,listurlfield.value.lastIndexOf('/'));");
launchPicker.Append(" }\n");
launchPicker.Append(" var callback=function(arr)\n");
launchPicker.Append(" {\n");
launchPicker.Append(" if(arr==null || arr==undefined)\n");
launchPicker.Append(" return;\n");
launchPicker.Append(" var site=arr[1];\n");
launchPicker.Append(" var list=arr[2];\n");
launchPicker.Append(" if(list != '')\n");
launchPicker.Append(" {\n");
launchPicker.Append(" listurlfield.value = site + (site == '/' ? '' : '/') + list;\n");
launchPicker.Append(" }\n");
launchPicker.Append(" " + Page.ClientScript.GetPostBackEventReference(_listUrl, string.Empty) + ";\n");
launchPicker.Append(" }\n");
launchPicker.Append("LaunchPickerTreeDialog(\'CbqPickerSelectListTitle','CbqPickerSelectListTitle','listsOnly',\"\",url,null,\"\",\"\",\"/_layouts/images/generic.png\", 0, callback );\n");
launchPicker.Append("}\n");
launchPicker.Append("</SCRIPT>");
if (!Page.ClientScript.IsClientScriptBlockRegistered("launchPicker"))
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "launchPicker", launchPicker.ToString());
}
After building and installing your webpart and opening the editorpart, your users will see this after they click the button:
You can find the code in this zip file. 6/15/2008In SharePoint you can limit the site templates the site templates that are available to users when they create a subsite. To do this you navigate to the page "Page layouts and site templates" from the site settings. For WSS there is no link to the UI to do this. You can limit the templates by manually entering the URL to the AreaTemplateSettings.aspx page. In code there are 2 ways to do it. You can make a template available for all languages that are installed on the SharePoint server, or you can make it available just for a specific lanugage. The examples below show you how to do this, both for a publishing site and for a teamsite. Example 1 This code snippet forces the users of our site to just use the "Team Site" template. The team site will be available in any language installed on the server. The code first finds the template using the GetWebTemplates of our SPSite object. To make it available for all languages, we use the method SetAvailableCrossLanguageWebTemplates. This example works for both publishing sites and team sites. Although team sites do not have the site settings link to this, you can limit the number of available site templates by using the same method as on a publishingweb. You will have to set one of the properties in the property bag of the SPWeb to make it work. using (SPSite site = new SPSite("http://moss/news"))
{
using (SPWeb web = site.OpenWeb())
{
// Find the Team Site template.
SPWebTemplate templateToAdd = null;
foreach (SPWebTemplate template in site.GetWebTemplates(web.Language))
{
if (string.Compare("STS#0", template.Name, true) == 0)
{
templateToAdd = template;
break;
}
}
if (templateToAdd != null)
{
// Create a new collection of available templates
Collection<SPWebTemplate> templates = new Collection<SPWebTemplate>();
templates.Add(templateToAdd);
// Update the web and save the changes
if (PublishingWeb.IsPublishingWeb(web))
{
PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
publishingWeb.SetAvailableCrossLanguageWebTemplates(templates, false);
}
else
{
web.SetAvailableCrossLanguageWebTemplates(templates);
web.AllProperties["__InheritWebTemplates"] = false.ToString();
web.Update();
}
}
}
}
After running this, the "Page layouts and site templates" page looks like this:
Example 2
The second example does the same thing, but users are now forced to use the Dutch Team Site template. Instead of using SetAvailableCrossLanguageWebTemplates, we now use SetAvailableWebTemplates:
using (SPSite site = new SPSite("http://moss/news"))
{
using (SPWeb web = site.OpenWeb())
{
// Find the Team Site template.
SPWebTemplate templateToAdd = null;
foreach (SPWebTemplate template in site.GetWebTemplates(1043))
{
if (string.Compare("STS#0", template.Name, true) == 0)
{
templateToAdd = template;
break;
}
}
if (templateToAdd != null)
{
// Create a new collection of available templates
Collection<SPWebTemplate> templates = new Collection<SPWebTemplate>();
templates.Add(templateToAdd);
// Update the web and save the changes
if (PublishingWeb.IsPublishingWeb(web))
{
PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
publishingWeb.SetAvailableWebTemplates(templates, 1043, false);
}
else
{
web.SetAvailableWebTemplates(templates, 1043);
web.AllProperties["__InheritWebTemplates"] = false.ToString();
web.Update();
}
}
}
}
After running this sample, the "Page layouts and site templates" page looks like this:

6/1/2008Two more days of SharePoint and then I will be on my way to do something completely different. This Thursday 5 colleagues and me will drive off to France to cycle tot the top of the Dutch Mountain, Alpe d'Huez. We do this for Stichting Alpe d'Huzes. Their mission is "To facilitate and inspire people to lead Happy and Healthy lives in Harmony with cancer." The second half of this page contains more information in English, the rest of the site is in Dutch. Thursday June 5th, 100 individual cyclists and 50 teams will at least climb the mountain 6 times on one day. The last few months I have been pretty busy preparing for that. During the day we will post the progress of Team e-office on this site: http://www.tonstegeman.com/alp The organization is posting their information during the day on this site: http://www.opgevenisgeenoptie.nl/ Both will be in Dutch. De name of the second site is the motto of the organization, "Never, ever quit!", which we will try to do on Thursday. Hope you willl check out our site and the official event site to see how everything progresses. On the event site they will probably post the final amount of money that all people have raised. The goal is to raise 3 million euro. If you want to add money to that, send me a message and we will work out how to get the money on their account. 5/20/2008
In one of my current projects, we have a publication process in which we read content from Lotus Notes. Users do some editting and then publish the items to MOSS. In a previous item, I showed how I read the content from Notes. In this post I will show you how we publish the items to MOSS. At first it sounds easy, but a big requirement we had was to do this without deploying server side code. To do this, we created a Windows application. This application posts pages in SharePoint using Frontpage RPC. The Windows application creates a publishing page of the specified page layout, and adds the content to that page.
A good place for a quick start on RPC is the RPC test page by the IW Kid. You can find it here. The referenced zip file WSS RPC.zip contains a test page with several examples of RPC calls. This should get you started pretty quickly. The sample code below is a simplified version of the code in the testpage. I stripped it just for demonstration purposes.
Another way to get a better understanding of how RPC works it to install Fiddler and use SharePoint Designer. In Fiddler you will find the calls and the responses.
The rest of this post decribes the most important parts of the code. You can find the full code in this zip file.
Step 1 - Create the user interface and the MOSSPage object.
The first thing I did was to create a user interface for the windows application. It is shown in the screenshot below. The user enters a name for the page, a title and the content for the page. The url of the site to which the page will be published is entered. The last stap is to edit a reference to a page layout. In SharePoint, the between a page and its page layout is a Hyperlink field on the Pages library. Therefore you need to supply the reference to the page layout as a hyperlink value, which looks like this:
http://moss/_catalogs/masterpage/ArticleLeft.aspx, Article page with image on left
It is composed of the full links to the aspx, followed by a "," separator and the name of the page layout.
When the user clicks the "Publish" button, a new object of type MOSSPage is created and the properties are set:
After that the Publish method is called:
MOSSPage page = new MOSSPage();
page.Name = textBoxName.Text;
page.PageLayout = textBoxPageLayout.Text;
page.Title = textBoxTitle.Text;
page.Content = textBoxContent.Text;
textBoxResult.Text = page.Publish(textBoxSite.Text);
Step 2 - Publication
The publish method of the MOSSPage object generates a dictionary of properties that will be added to the RPC request when it is submitted. The key of the property is the name of the property as it needs to be supplied to the RPC request.
Dictionary<string, string> properties = new Dictionary<string, string>();
properties.Add("vti_title", Title);
properties.Add("PublishingPageLayout", PageLayout);
properties.Add("PublishingPageContent", Content);
byte[] fileContents = System.Text.Encoding.ASCII.GetBytes(emptyASPX);
return UploadDocument(siteUrl, string.Format("{0}.aspx", Name), fileContents, properties);
After creating the dictionary it creates a byte array that contains the content of the page. A page in SharePoint is nothing more than an ASPX document in a document library, with custom metadata. Therefore you need to supply the contents of the ASPX file that the RPC request will submit. For a publishing page, this looks like this:
private const string emptyASPX = "<%@ Page Inherits=\"Microsoft.SharePoint.Publishing.TemplateRedirectionPage, Microsoft.SharePoint.Publishing,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c\" %> <%@ Reference VirtualPath=\"~TemplatePageUrl\" %> <%@ Reference VirtualPath=\"~masterurl/custom.master\" %>";
It calls the UploadDocument method, that creates and submits the RPC call.
Step 3 - Upload
UploadDocument first creates the RPC request. First it initializes and encodes a number of variables.
Uri uri = new Uri(url);
// Init variables.
string method = "put document:12.0.4518.1016";
string service = uri.AbsolutePath;
string document = "[document_name={0};meta_info=[{1}]]";
string documentName = string.Format("{0}/{1}", pagesLibrary, fileName);
document = String.Format(document, documentName, GetMetaInfo(metaInfo));
string rpcString = "method={0}&service_name={1}&document={2}&put_option=createdir";
// Encode variables.
method = HttpUtility.UrlEncode(method);
service = HttpUtility.UrlEncode(service);
document = HttpUtility.UrlEncode(document);
// Get the RPC call data
rpcString = String.Format(rpcString, method, service, document);
byte[] callData = GetRPCCall(rpcString, fileContents);
In this snippet, the variable metaInfo contains the dictionary of properties that was created in the previous step. The are added to the rpcString by the function "GetMetaInfo":
private static string GetMetaInfo(Dictionary<string, string> properties)
{
if (properties == null)
return string.Empty;
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string, string> property in properties)
{
if (property.Value != null)
{
string data = EscapeVectorChars(property.Value.ToString());
sb.AppendFormat("{0};SW|{1};", property.Key, data);
}
}
return sb.ToString().TrimEnd(';');
}
This function escapes a number of characters and returns one big string that contains all property names and values. For one property, this string looks like this:
meta_info=[vti_title;SW|Testpage published by using Frontpage RPC]"
It contains the name of the property (please note, for this "Title" this is "vti_title" instead of "Title"). After the pipe symbol, it contains the value. In between the two you will find (in this case) the value "SW". The S is used for the datatype (always string in my simplified example). You can find the code to escape the characters in the ZIP file referenced before.
After setting up the rpcString, our UploadMethod function combines the contents of the ASPX file that will be submitted and the RPC call to a byte array:
private byte[] GetRPCCall(string rpcString, byte[] fileContents)
{
string callString = rpcString;
callString.Replace(".", "%2e");
callString.Replace("_", "%5f");
byte[] callBytes = System.Text.Encoding.UTF8.GetBytes(callString);
byte[] data = new byte[callBytes.Length + fileContents.Length + 1];
callBytes.CopyTo(data, 0);
data[callBytes.Length] = 0x0A;
fileContents.CopyTo(data, callBytes.Length + 1);
return data;
}
Now our call is ready to be submitted. We do this by using a WebClient object:
// Send the request.
byte[] result = null;
using (WebClient client = new WebClient())
{
client.Credentials = System.Net.CredentialCache.DefaultCredentials;
client.Headers.Add("Content-Type", "application/x-vermeer-urlencoded");
client.Headers.Add("X-Vermeer-Content-Type", "application/x-vermeer-urlencoded");
result = client.UploadData(url + "/_vti_bin/_vti_aut/author.dll", "POST", callData);
}
// Return the result.
return System.Text.Encoding.UTF8.GetString(result);
The result of the call returned to the user interface.
Step 4 - Test
Now that we have completed our publication process, we can test the application. After clicking the Publish button we will find the page and its content in the Pages library that we specified:
Below you will find an example of the output that is returned by our POST command. Just to give you an idea of what is looks like.
<html><head><title>vermeer RPC packet</title></head>
<body>
<p>method=put document:12.0.0.6219
<p>message=successfully put document 'Pages/Testpage2.aspx' as 'Pages/Testpage2.aspx'
<p>document=
<ul>
<li>document_name=Pages/Testpage2.aspx
<li>meta_info=
<ul>
<li>PublishingPageContent
<li>SW|content
<li>vti_rtag
<li>SW|rt:C45F9E3B-4F82-46D8-8757-1577D94E4A9D@00000000001
<li>vti_etag
<li>SW|"{C45F9E3B-4F82-46D8-8757-1577D94E4A9D},1"
<li>vti_filesize
<li>IR|279
<li>vti_parserversion
<li>SR|12.0.0.6219
<li>vti_modifiedby
<li>SR|TST_MOSS2007\administrator
<li>vti_timecreated
<li>TR|20 May 2008 19:55:14 -0000
<li>vti_title
<li>SW|Title for second test page
<li>vti_charset
<li>SR|windows-1252
<li>ContentTypeId
<li>SW|0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900EF933E554CA5E54FAC89BEC91C15C93D
<li>vti_sourcecontrolmultiuserchkoutby
<li>VR|TST_MOSS2007\\administrator
<li>vti_timelastmodified
<li>TR|20 May 2008 19:55:14 -0000
<li>PublishingPageLayout
<li>SW|http://moss/_catalogs/masterpage/ArticleLeft.aspx, Article page with image on left
<li>vti_author
<li>SR|TST_MOSS2007\administrator
<li>vti_sourcecontrolcheckedoutby
<li>SR|TST_MOSS2007\administrator
<li>vti_sourcecontroltimecheckedout
<li>TR|20 May 2008 19:55:14 -0000
<li>vti_sourcecontrolversion
<li>SR|V0.1
<li>vti_sourcecontrolcookie
<li>SR|fp_internal
<li>vti_linkinfo
<li>VX|UJUS|/_catalogs/masterpage/ArticleLeft.aspx UHUS|~masterurl/custom.master UHUS|~TemplatePageUrl
</ul>
</ul>
</body>
</html>
5/9/2008I finally finished configuring my new Lenovo T61p laptop. I have used it for a week as my production development machine. It has 4 Gb of memory and I am running Vista 64 bits. I expected some issues when moving to Vista 64, bit it all was very smooth. The only issues I have had until so far are the VPN clients that I use to connect to our corporate network and to the networks of our clients. I created a virtual machine running on Windows XP that has all the VPN software. Works really well. I am not doing any development directly on my host OS, so I don't have any experience with developing on the machine itself. All my development work is done in virtual machines. I just upgraded to version 6.0.3 of VMWare Workstation. I was always a fan of VMWare Workstation (I like cloning and teaming of the VMs), but the setup that I am running now really rocks! The virtual machines are performing very well. And loading / suspending them is much faster. I can now even run 2 SharePoint development machines at the same time, without my machine becoming extremely slow. The images of my VMs are all on a separate disk, that I have in a bay in the DVD slot. If you are looking for a performance boost of your VMs, that probably is the easiest thing to do. For me, moving to a new laptop with 2Gb extra memory and Vista 64 bit also worked really well. And what I lke about the Lenovo is the screen. It is the first laptop with a wide screen and I reallly like it. Something very easy that I mainly post as a reminder to self. Took me some time (and Michiel) to get it working. I was working on a AfterBuild MSBuild script in one of my projects. I added some logging using the Message element: <Message Text="Hello World!" />
The MSBuild tasks ran perfectly fine, except that I did not see the message appear in the output window.
I had to increase the "MSBuild project build output verbosity". To do this go to Tools - Options. Select "Projects and Solutions" and "Build and Run". Set the value to "Normal" and your messages appear.

4/21/2008In my current project, we are reading data from Lotus Notes and migrate that into SharePoint. If the Notes database is web enabled, the easiest way to read data is using the "ReadViewEntries" parameter. Instead of the HTML for the web view that you are looking at, this returns XML. The url for the webview in my testdatabase is something like: http://[domino_server]/[databasename].nsf/[viewname] To get the XML view of this Notes view, you simply need to add "?ReadViewEntries": http://[domino_server]/[databasename].nsf/[viewname]?ReadViewEntries The most difficult part here is to login to the database. If I enter the url of our test database in Internet Explorer, I get a login window. This is the Lotus Domino Web Access login box: That is a bit of an issue when you want to automate the process and read data from Notes from you code. Below you will find the code that shows you how to do it. Obviously you still need a username and a password to login. But now you can login from your code, get the XML and do whatever you want with that. Step 1 - Login The first step is to login in Notes. This returns a cookie. You will need to store that cookie and re-use that when requesting data. In example above, the url I need to login to my Notes database is: http://[domino_server]/[databasename].nsf/?Login In the sample code below, this url is entered in textBoxLoginUrl. This is the code for my login procedure. I have tested it on 2 different Domino servers, and I found 2 different types of cookies that were returned by the login procedure. There might be more, but I ran across these 2 and the code works for both. private Cookie Login() { string username = textBoxUsername.Text; string password = textBoxPassword.Text; string loginUrl = textBoxLoginUrl.Text; string postData = String.Format("username={0}&password={1}", username, password); // Get the web request and its stream. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(loginUrl); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = postData.Length; request.CookieContainer = new CookieContainer(); request.AllowAutoRedirect = false; Stream requestStream = request.GetRequestStream(); byte[] bytedata = Encoding.ASCII.GetBytes(postData); requestStream.Write(bytedata, 0, bytedata.Length); requestStream.Close(); // Get the response and read the auth. cookie type. HttpWebResponse httpWebResponse = (HttpWebResponse)request.GetResponse(); httpWebResponse.Cookies = request.CookieContainer.GetCookies(request.RequestUri); string cookieName = string.Empty; if (httpWebResponse.Cookies["LtpaToken"] != null) { cookieName = "LtpaToken"; } else if (httpWebResponse.Cookies["DomAuthSessId"] != null) { cookieName = "DomAuthSessId"; } // If we found a valid cookie, return it. if (!string.IsNullOrEmpty(cookieName)) { Cookie newCookie = new Cookie(); newCookie.Name = httpWebResponse.Cookies[cookieName].Name; newCookie.Domain = httpWebResponse.Cookies[cookieName].Domain; newCookie.Value = httpWebResponse.Cookies[cookieName].Value; newCookie.Expires = httpWebResponse.Cookies[cookieName].Expires; newCookie.Path = httpWebResponse.Cookies[cookieName].Path; return newCookie; } return null; }
Please note: do not forget to set "AllowAutoRedirect" to false. Otherwise you always end up getting the HTML for the login form. Took me forever to find that out, so I hope it helps you.
Step 2 - Get the data
After logging in, we can get the data using a "HttpWebRequest" object:
Cookie cookie = Login(); if (cookie != null) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(textBoxUrl.Text); request.Method = "GET"; CookieContainer container = new CookieContainer(); container.Add(cookie); request.CookieContainer = container; HttpWebResponse httpWebResponse = (HttpWebResponse)request.GetResponse(); Stream responseStream = httpWebResponse.GetResponseStream(); StreamReader reader2 = new StreamReader(responseStream, System.Text.Encoding.UTF8); textBoxXml.Text = reader2.ReadToEnd(); }
In this sample, textBoxUrl holds the url to the 'ReadViewEntries' url (see above). The cookie that was returned by the Login function, is added to the CookieContainer of the WebRequest. In this simple example I just displayed the XML in a textbox.
It turned out to be fairly simple to get data from Lotus Notes using the built in XML capabilities. After some attempts that failed I finally sorted out the code to authenticate properly. Hope it can help you when you need to do something similar. Last week I was at the MVP Summit in Seattle and Redmond. You are probably already sick and tired of my fellow MVPs being very enthusiastic about the Summit, so here comes another post :-). Being at the Summit for the first time was a special experience. We had 2 days loaded with sessions on SharePoint. Very interesting to hear things directly from the product teams. We also had a lot of opportunities to give feedback on what we heard (and other things). At some points I felt sorry for the product teams, because if you ask a group like this for feedback, you get feedback! Apart from working so close with the teams, I really enjoyed meeting my fellow MVPs. You know the names of a lot of them, and suddenly you are in the same room with all of them talking about SharePoint. Wow! It also was the first time that I got to see Ray Ozzie and Steve Ballmer live on stage. Really impressive to see how they handle the Q&A sessions where the audience (1800 people) got to ask questions. It was also the first time I was at Daniel's Broiler in Bellevue at the 21st floor. Brad Smith, Bob Fox and Eric Shupps were so kind to invite us for dinner. It was an excellent evening and the steaks and wine were very good. The last first time experience at the Summit was the paintball. Played it for the first time and we had a lot of fun. Some things I learned about paintball: - It is a good idea to wear good shoes. My sneakers were not the best choice in the Seattle mud.
- It is pretty hard to aim when you can't stop laughing.
- It is very hard not to laugh when you see people crashing into a barrier, taking the barrier down and being out in the open. Especially the one by Andrew Connell was very funny.
- Paintball weapons do not shoot very well when loaded with mud. After running for the flag in capture the flag, I had to return to the base as quickly as possible. Trying to run too fast, I sort of dived into the mud, loading my gun with mud.
The last night in Seattle we had a local MVP dinner, organized by our local MVP lead. With all MVPs from the Benelux and the Scandinavian countries we enjoyed steak and wine in El Gaucho. Nice place and the food and wine are very good! Gerard and Ruud, thanks! The SharePoint team blog has some pictures, and a nice wrapup of this week. 4/10/2008I just released version 1.2 of the (free to use) Content By Type webpart. The most important new features are described below. Clicking the links takes you to the details page of that feature. For more information and to download the webpart and installation guide, follow this link. If you first want to read more about what the Content By Type webpart can do for you, click here. The webpart is free to download and use. Please understand that I cannot support the webpart. If you run into issues, or have feature requests, I will do my best to help you, but I cannot guarantee response times etc. Special thanks to the testers, who helped me to test the webpart before I released it.
|
|
|
|
|
|
|
|