Tuesday, December 18, 2007

This is customer support!

Sorry - another non-sharepoint post. I promise to post something good later today.

I just got a response from the GPS company TomTom about a question I sent them. You see, my TomTom GPS is also a bluetooth hands free in my car, and has a microphone that you attach to the sun screen which allows you to talk with great sound quality. The problem is that tomtom never expected someone like me to use that - I broke the microphone clip after about 8 months use.
The annoying thing - they dont sell spare parts. In the UK you can buy another set of microphone+clip, but here in Australia it is not available for some reason. So I got to tomtom web site, and in the support section asked for help. within a day (!) I got a response that they are sending me a replacement!

This is the kind of customer service I like to see. I know that the next GPS I get will be a tomtom.

Side note - if anyone from tomtom is reading this - can you add an Israel map to your collections of maps? I really want to travel with my tomtom...

Sunday, December 16, 2007

DELL XPS and 64bit operating systems

This has me angry enough to blog about. I have a Dell XPS m1330 laptop. Its a great laptop - fast, small, efficient and best of all - it is configured with all the best options available from dell (except for a solid state hard drive), including 4GB RAM - which dictates a 64bit operating system to take advantage of the juice.
The only problem is that you cannot install a 64bit operating system on it without turning off AHCI - which sucks. I called Dell, and a supporter named "Michael MG" confirmed that they (dell) do not know what drivers are needed to download to make this work (he advised me to "google it") and when I asked, if my laptop doesnt have a "A:" drive how do I install drivers during windows installation. Michael told me that I can't and therefore should only install windows 32bit or turn off AHCI support.
WTF?!
Does anyone know if the Apple machines support AHCI on vista 64bit?

Tuesday, December 11, 2007

Service pack is released

Ok, so all other bloggers are talking about it, you probably dont need me to tell you - but just in case I have a reader who only reads my blog - here are the links to the service packs:
  1. Windows SharePoint Services v3 SP1
  2. Microsoft Office SharePoint Server 2007 SP1
  3. Office SharePoint Designer 2007 SP1
I didn't install those myself yet - if I run into trouble I will post.

Thursday, November 29, 2007

Why event handlers are triggered twice?

A lot of people call me Mr Event Handlers (ok, that is not 100% correct. Currently I am the only one calling me that, but never mind), but today's article comes from a Friend who we will refer to as Antin.
Antin did some research on why event handlers behaved strangely in publishing sites and emailed me the results. He logged every event that happens when you check-in a file in a publishing site, and logged the thread of each call, along with the time, so he figured out the sequence.

I think his results are important for all of us writing event handlers - we need to know what is happening when. If you do find this helpful, post a "thank you antin" comment on this post, and I will make sure he know about it (enough encouragment and he may start his own blog, the lazy bugger).

The test results:
(In brackets will be thread number, then list item is old/new, then before properties is old/new, then after values is old/new) Change AND Check-in:
1. Updating (10; old; old; old)
2. Updating (10; new; new; new)
3. CheckingIn (10; new; new; new)
4. CheckedIn (15; new; new; new)
5. Updated (11; new; new; new)
6. Updated (1; new; old; old;)

Change AND Save:
1. Updating (10; old; old; old)
2. Updated (12;new; old; old)

Check-in ONLY
1. CheckingIn (9; new; new; new)
2. CheckedIn (11; new; new; new)
3. Updated (13; new; new; new)
4. Updated (10; new; new; new)

Publish ONLY
1. Updating (1; new; new; new)
2. Updated (13; new; new; new)

If you look at what happens for the save only and the check in only and the combination of new/old values it looks like it working like this:
1. ItemUpdating - from the Save event
2. ItemUpdating - from the CheckIn event (which should fire after the Updated from the Save)
3. ItemCheckingIn
4. ItemCheckedIn
5. ItemUpdated - from the CheckIn event
6. ItemUpdated - from the Save event

Tuesday, November 27, 2007

Avoiding CSS caching issues

I had a problem in several projects where we changed the CSS and deployed it to the load balanced server environment, and still the old CSS file was cached somewhere, and we couldnt roll out the changes to the users.
Another problem on the same line are browsers that cache the css files, and users do not see the changes we make to the css until we tell them to clear their browser's cache.

To resolve this, we took a trick from Microsoft - notice how Microsoft always links to the css with a version number in sharepoint? this is done to avoid caching when you put a new version.

My trick was to develop a custom web control (that I call the "CssInjector") that gets as a property the link to the css file, and renders that link with a random querystring. this forces the browsers (as well as IIS or other caching applications) not to cache the file.
The problem with this solutions is that it will have performance implications - slower loading of pages, so what I'd really like to do is what MS did - rely on a version number. However, I have yet to come up with the architecture that will let me manage version numbers in my solution package without having to manually setting the version every time. I thought of reading the version number from the CSS file itself, but that will introduce performance issues again - as the code will have to load and read the css file each time a page is opened.

So until I get a better solution, here is the code I am using for my web control:

using System;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
namespace SharePointTips.SharePoint.WebControls{
public class CssInjector : WebControl
    {
        private const string CSS_LINK_FORMAT = @"<link rel=""stylesheet"" type=""text/css"" href=""{0}"">";
        private string _CSSFileLink = "";
        public string CSSFileLink
        {
            get { return _CSSFileLink; }
            set { _CSSFileLink = value; }
        }
        protected override void Render(HtmlTextWriter writer)
        {
            if (this.CSSFileLink.Length > 0)
            {
                writer.Write(string.Format(CSS_LINK_FORMAT, this.CSSFileLink + "?k=" + GetUniqueKey(8)));
            }
        }
        public static string GetUniqueKey(int length)
        {
            //create an array of acceptable characters
            char[] chars = new char[62];
            chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            StringBuilder result = new StringBuilder(length);
            Random rnd = new Random();
            //create the random string in the specified length
            for (int i = 0; i < length; i++)
            {
                result.Append(chars[rnd.Next(chars.Length)]);
            }
            return result.ToString();
        }
    }
}
And here is how I use it in the master page:
<%@ Register Tagprefix="SharePointTipsWebControls" Namespace="SharePointTips.SharePoint.WebControls" Assembly="SharePointTips.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxx" %>
<SharePointTipsWebControls:CssInjector runat="server" ID="SharePointTipsCSS1" CSSFileLink="/_layouts/SharePointTips/SharePointTipsCustomCss.css" />

Wednesday, November 07, 2007

MVP Night in Vegas

Text to come...

Monday, November 05, 2007

SharePointPedia is live

Microsoft has just released a site built on sharepoint, that will hold information (links) for sharepoint. (The short link is http://sharepointpedia.com)
Check out Lawrence Liu post about this.

Sunday, November 04, 2007

First night in Vegas

Yes!
I am there at last, Vegas for the sharepoint dev connections conference. It only took me 30 hours to get from home to here, and I landed only 12 hours after I left (confused? so am I...)

So far, Vegas stinks. I don't mean it sucks - I mean it stinks with cigarette smoke. Man, its like going back to eastern europe (or Amsterdam). One reason I love Australia so much is the anti smoking laws.

So just a reminder - If you want to meet, in two days (Tuesday) we are having the SharePoint by Day, SharePint by Night event, with all MVPs having a few drinks and talking sharepoint with everyone who will stop to listen. Must be funny!
See you there!

Wednesday, October 31, 2007

Another simple code snippet - using the user field


A user field in a list allows people to choose a user for each list item.


When looking at the value that is stored, we can see it is a lookup field - and is stored using the format ID#useraccount. For example:

listitem["User Name"] will have "1#development\ishai"
Click on the image below to see it in action in visual studio:





Where does this number come from? well, everytime you create a site in sharepoint, sharepoint creates a list for users. Each user gets an ID in the list, and when you add a user column to a list in that site, it is infact a special case of a lookup column into the users list. So in each site the same user will have a different ID.



So how do we use this field in code?

Get a user from a list item:



To get a user we use the SPFieldUserValue class, which accepts a SPWeb and a string value as parameters. The string value is the value from the list that contains the ID and the account name. Once we have that, we can use the SPFieldUserValue to get information about the user.

Example:


 using (SPSite site = new SPSite("http://portal"))

            {

                using (SPWeb web = site.OpenWeb())

                {

                    SPList list = web.Lists["Example For Users"];

                    SPListItem item = list.Items[0];

 

                    SPFieldUserValue userValue = new SPFieldUserValue(web, item["User Name"].ToString());

                    if (userValue.User.LoginName == web.CurrentUser.LoginName)

                    {

                        //do something!

                    }

                }

            }







Adding a value to a user field



This is the same, but in reverse. We use the same class (SPFieldUserValue ) and give it the values we want for the user.



using (SPSite site = new SPSite("http://portal"))

            {

                using (SPWeb web = site.OpenWeb())

                {

                    SPList list = web.Lists["Example For Users"];

                    SPListItem item = list.Items[0];

 

                    SPFieldUserValue userValue = new SPFieldUserValue(web, web.CurrentUser.ID, web.CurrentUser.LoginName);

                    item["User Name"] = userValue;

                    item.Update();

                }

            }



The image below shows the list after I updated the list item:






Monday, October 29, 2007

Failed to instantiate file "default.master"

I was building a new solution package that deployed a site definition, and when I created a new site from that definition I got the error "Failed to instantiate file "default.master" from module "DefaultMasterPage": Source path "default.master" not found.".
It turns out that I wrote the wrong folder name in the webtemp xml file. Thank you Bjarne L. Gram for writing about this.

Wednesday, October 24, 2007

Code Practices - getting\setting values from\to the lookup and the hyperlink fields

How to get data from a field that is a lookup? how to set data into a field that is a hyperlink?
I don't believe I didn't write about this yet. It's so common!

For both cases, sharepoint object model exposes classes to help us get or set the data we want. These are the SPFieldLookupValue and the SPFieldUrlValue.

Example 1: Set the url field of a link


Use the SPFieldUrlValue class to create an object that holds the url to link to, and the title to display:

SPList list = web.Lists["Links"];

SPListItem newLink = list.Items.Add();

SPFieldUrlValue value = new SPFieldUrlValue();

value.Description = "test";

value.Url = "http://www.microsoft.com/sharepoint";

newLink["URL"] = value;

newLink.Update();

Example 2: Get the url field of a link


Use the SPFieldUrlValue class to create an object that gets the url and description:

SPList list = web.Lists["Links"];

SPListItem existingLink = list.Items[0];

SPFieldUrlValue value = new SPFieldUrlValue(existingLink["URL"].ToString());

string linkTitle = value.Description;

string linkURL = value.Url;

Example 3: Set the value of a lookup field for a known title and ID


In the following example I am using SPFieldLookupValue to set the value of a lookup field ("Group Name") to item "Program Operations", whose ID is 14:

SPList list = web.Lists["Branches"];

SPListItem newBranch = list.Items.Add();

newBranch["Title"] = "A New Branch";

SPFieldLookupValue newValue = new SPFieldLookupValue(14,"Program Operations");

newBranch["Group Name"] = newValue;

newBranch.Update();

Example 4: Get the value of a lookup field from an item


Here I am reading the value of the group name field (which is a lookup field in the branches list):

SPList list = web.Lists["Branches"];

SPListItem existingBranch = list.Items[0];

SPFieldLookupValue group = new SPFieldLookupValue(existingBranch["Group Name"].ToString());

int lookedUpItemID = group.LookupId;

string lookedUpItemTitle = group.LookupValue;

Sunday, October 21, 2007

Performing joins between SharePoint lists (link)

Check it out: Performing joins between SharePoint lists by fellow MVP Sahil Malik, explains how to use sharepoint designer to show two linked lists as one. I was going to write about it, but he beat me to it.
Maybe next time I won't go bush-walking on the weekend and write more?


Oh, about the weekend - I found some MOSS that I thought would interest you:



Red Moss (Seratadon?)





Wednesday, October 17, 2007

Using the SharePoint People Picker


A long time ago (beta2 time) I published a post about the people picker control (MOSS 2007 controls - have a bit of fun with the "people editor" form control!).
Lately I had many comments on that post from people who have been having issues getting the value the user selected, and asked for another demo of using the control.
So here it is:



The code creates three controls - a PeopleEditor (from the Microsoft.SharePoint.WebControls namespace), a button and a textbox. The button has an click event that itirates over the users picked in the PeopleEditor and writes them to the text box. Simple!





public class PeoplePickerWebPart : System.Web.UI.WebControls.WebParts.WebPart

    {

        PeopleEditor pe;

        TextBox t;

        Button b;

 

        protected override void CreateChildControls()

        {

            base.CreateChildControls();

            pe = new PeopleEditor();

 

 

            this.Controls.Add(pe);

            b = new Button();

            b.Text = "Click me to see the users";

            this.Controls.Add(b);

            b.Click += new EventHandler(b_Click);

            t = new TextBox();

            t.TextMode = TextBoxMode.MultiLine;

            this.Controls.Add(t);

        }

 

        void b_Click(object sender, EventArgs e)

        {

            foreach(string ent in pe.CommaSeparatedAccounts.Split(','))

            {

                t.Text += ent + Environment.NewLine;

            }

        }








Results:

The web part as the page loads





The web part does "check names"





The web part after the click of the button







Using the SharePoint date control in a web part


After a lot of requests in comments in this blog, I figured this must be very hard to implement. So I tried, and found it quite easy. Here is a code sample of a web part that uses the sharepoint date control



The code is simple - three controls - a DateTimeControl from the Microsoft.SharePoint.WebControls namespace, a button and a text box.
The button has a click event. On click, the event sets the text box to display the date that was selected in the DateTimeControl.





public class DatePickerWebPart : System.Web.UI.WebControls.WebParts.WebPart

    {

        DateTimeControl dtc;

        TextBox t;

        Button b;

 

        protected override void CreateChildControls()

        {

            base.CreateChildControls();

            dtc = new DateTimeControl();

            dtc.ID = "dtc" + this.UniqueID;

            this.Controls.Add(dtc);

            b = new Button();

            b.Text = "Click me to see the date";

            this.Controls.Add(b);

            b.Click += new EventHandler(b_Click);

            t = new TextBox();

            this.Controls.Add(t);

        }

 

        void b_Click(object sender, EventArgs e)

        {

            t.Text = dtc.SelectedDate.ToLongDateString();

        }

    }






Here is the result:

This is the web part when the page first loads






This is the web part when you click on the date picker






This is the web part when you click on the button




See? it is simple!

Tuesday, October 16, 2007

Ajax in SharePoint

Fellow MVP Daniel Larson will be hosting a live conference to show the SharePoint Ajax Toolkit. Read about it in Daniell's blog post.

Why be a sharepoint developer?

Sorry, can't resist posting the following link to Tony's post " I don’t care if you don’t want to develop on SharePoint, and neither does your boss". So true...
Bob Mixon also wrote a nice article about what is required from a sharepoint person Are You Ready For SharePoint

Monday, October 15, 2007

SharePoint by Day, SharePint by Night @ SharePoint Connections in Vegas!

Andrew Connell posted an invite in his blog for an "informal SharePoint conversation" at the Coral Reef Bar in the Mandalay hotel during SharePoint Dev Connections in Vegas.
I will be there, so if you want to have a chat, come on in!

Wednesday, October 10, 2007

Email Alerts not getting sent, while other emails are

Had this recently at a customer's - the server would send emails if you added a user to a site, or if you registered for an alert - you'd get the email saying the alert was activated. But when someone added an item to the list, you didnt get the email for the alert.

Looking in the Timer Job Status list in central admin showed that we had two front end servers, and on one of them the "Immediate Alerts" job was always on "failed". After much research and head banging, a collegue of mine suggested I look into the relay settings of the SMTP server.
When I opened the relay settings I found to my surprise that the IP addresses that were configured to allow relay did not include the IP address for one of the front end servers. Adding that IP address resolved the problem.

Now the alerts are working, but for some reason the " Send e-mail when ownership is assigned" on a task list is not working. Wish I knew how to fix that...

Monday, October 08, 2007

Blogrush - removed

After a few days evaluating blogrush, I reviewed it and decided that it is a more of a service for people with general BS blogs, and not professional, audience targeted ones. Why should people see links to blogs about starwars in a sharepoint blog?
So it is removed, and now my blog is more usable...

Monday, October 01, 2007

Business Data Columns : updating programmatically

When you create or update a list item programmatically, and one of the fields you want to set is a BDC field that displays several columns, setting the field does not set the additional columns - just the selected ID. This puzzled me, So I sent the question out to all the MVP's and sure enough - Nick Swan had an answer and was happy to share it with all of us.








Friday, September 28, 2007

Workaround - "Error" in navigation when creating a publishing site from code

Since I started working with sharepoint 2007 I was puzzled by this bug, and recently I asked more people how said they have that too. The problem happens when you use the API to create a site out of a template that has the publishing feature activated in it. The site is created ok, but the first user to open the site will see a very odd tab and quick-launch:






So far, I have no explanation, but I have a workaround. Make a call to the site in the code. I know - this adds process time and resources, but it beats having your users ask you about the "error tab".
In your code, add the following function, and call it just after the site creation code:



public static void OpenUrl(string url)
{
try
{
HttpWebRequest request = (HttpWebRequest)
WebRequest.Create(url);
request.UseDefaultCredentials = true;
HttpWebResponse response = (HttpWebResponse)
request.GetResponse();
}
catch(Exception ex) {
//do something with the error
}
}


Here is an example how to use this function:



public static void CreateSite(SPWeb parentWeb)
{
try
{
//get the template for the site using the GetWebTemplate function I wrote
SPWebTemplate template =
SharePointFunctions.GetWebTemplate(parentWeb,
this.SiteTemplateName,
ConfigVariables.SitesLCID);
//create the site using the class properties
newWeb = parentWeb.Webs.Add(this.ShortName,
this.SiteTitle,
this.SiteDescription,
ConfigVariables.SitesLCID,
template,
false,
false);
OpenSiteHomePage(newWeb.Url);
}
catch(Exception ex) {
//do something with the error
}
}









Wednesday, September 26, 2007

Joined BlogRush

Not sure if that will make any difference, but I joined this site (BlogRush) in hope of getting more visitors. I will let you know if it works!

Back from vacation

Hello my readers!

You may have noticed I didn't blog this month, and that was because I was on my annual leave, visiting my family and friends in hot Israel!

I took time in the trip to present at the Israeli SharePoint User Group in Microsoft Israel, and I was very surprised to see how many people turned up! We had more than 50 people there, a lot of past clients and past collegues.



Here is a picture of the lowest sharepoint expert in the world (me floating in the dead sea -416 meters below sea level! to get lower, you need to go on a submarine):





And here are a couple of pictures I took that I think would make great desktop backgrounds:




































Tuesday, September 04, 2007

Just for me - excel macro to generate an XML

If you are here for a sharepoint tip, disregard this post- I am posting this to myself so I will not lose this code. Basically, it is an excel macro that genenrates an xml file from the current sheet. If you do want to use it, add a reference to microsoft xml in the VBA environment, and paste the code below to a module.
There are many way to improve it, but I just wrote this to quickly achieve a small task that I needed done.

Sub makeXml()
    ActiveCell.SpecialCells(xlLastCell).Select
    Dim lastRow, lastCol As Long
    lastRow = ActiveCell.Row
    lastCol = ActiveCell.Column
    
    Dim iRow, iCol As Long
    
    Dim xDoc As New DOMDocument
    Dim rootNode As IXMLDOMNode
    Set rootNode = xDoc.createElement("Root")
    Dim rowNode As IXMLDOMNode
    Dim colNode As IXMLDOMNode
    
    'loop over the rows
    For iRow = 2 To lastRow
        Set rowNode = xDoc.createElement("Row")
        'loop over the columns
        For iCol = 1 To lastCol
            If (Len(ActiveSheet.Cells(1, iCol).Text) > 0) Then
                Set colNode = xDoc.createElement(GetXmlSafeColumnName(ActiveSheet.Cells(1, iCol).Text))
                
                colNode.Text = ActiveSheet.Cells(iRow, iCol).Text
                rowNode.appendChild colNode
            End If
        Next iCol
        rootNode.appendChild rowNode
    Next iRow
    xDoc.appendChild rootNode
    xDoc.Save ("c:\temp\temp.xml")
    set xDoc = Nothing
    
End Sub
Function GetXmlSafeColumnName(name As String)
    Dim ret As String
    ret = name
    ret = Replace(ret, " ", "_")
    ret = Replace(ret, ".", "")
    ret = Replace(ret, ",", "")
    ret = Replace(ret, "&", "")
    ret = Replace(ret, "!", "")
    ret = Replace(ret, "@", "")
    ret = Replace(ret, "$", "")
    ret = Replace(ret, "#", "")
    ret = Replace(ret, "%", "")
    ret = Replace(ret, "^", "")
    ret = Replace(ret, "*", "")
    ret = Replace(ret, "(", "")
    ret = Replace(ret, ")", "")
    ret = Replace(ret, "-", "")
    ret = Replace(ret, "+", "")
    
    GetXmlSafeColumnName = ret
End Function

Monday, September 03, 2007

The security validation for this page is invalid.

When writing code for sharepoint, you may encounter the error "The security validation for this page is invalid. Click Back in your Web browser..." and so on. A lot has been written on this, with many different people touting different solutions. For example SpiderWool says to turn on security validation for the application.
This is not recommended, and not required as one of the comments said - all you need to do before you update the list item or web object is to set "AllowUnsafeUpdates" to true for the SPWeb and SPSite objects.
But some of the other comments in the same post complain that it didn't help them. Well, most probably the reason is that they created the SPWeb or SPSite objects in another function and then tried to update an object that was returned from the function. Here are two examples of the same code, where one will work and the other will not:
Bad Example:
The code calls a function to get the list, where the SPWeb and SPSite are created in the function.

using (SPSite site = new SPSite(parentSiteUrl))
{
        site.AllowUnsafeUpdates = true;
        using (SPWeb web = site.OpenWeb())
        {
            web.AllowUnsafeUpdates = true;
            SPList list = GetList("mylist");
            _listItem = clientsList.Items.Add();
            _listItem["Title"] = "test";
            _listItem.Update();                    }
        }
}
private SPList GetList(string name)
{
   using (SPSite site = new SPSite(parentSiteUrl))
   {
        site.AllowUnsafeUpdates = true;
        using (SPWeb web = site.OpenWeb())
        {
            web.AllowUnsafeUpdates = true;
            SPList list = web.Lists["mylist"];
            return list;
        }
   }
}

Good Example:
Loading the SPSite and SPWeb object only once and using them to get the list.

using (SPSite site = new SPSite(parentSiteUrl))
{
        site.AllowUnsafeUpdates = true;
        using (SPWeb web = site.OpenWeb())
        {
            web.AllowUnsafeUpdates = true;
            SPList list = web.Lists["mylist"];
            _listItem = clientsList.Items.Add();
            _listItem["Title"] = "test";
            _listItem.Update();                    }
        }
}

Wednesday, August 29, 2007

Web Services on SharePoint - making F5 Work

I was getting many questions on best practices to write custom web services for sharepoint, and I wanted to write an article about it for some time now. Also, I just had a chance to fiddle around with making F5 (run from visual studio into debug mode) work for a web service I am testing on a sharepoint site. This involved a few tricks, so I am documenting them:

Note - only do this on your development box!

Note - code lines marked in red means that you will need to change the values to your environment values.


I hold that the best practice is not to use the web service as a web application template (that is the default with visual studio) because when deploying to sharepoint, you'r web service usualy needs to sit in the layouts folder, and you do not want to deploy your code files there. You also don't want to use the visual studio publishing mechanism that uses the frontpage server extensions.
The alternative is creating a web service that is deployed as an asmx file to the layouts folder, pointing to a DLL that is deployed to the GAC. That makes it safe and secure, and easier to deploy and track versions.



Step 1 - Create a Web Service (DLL) Project

To create the web service, use Visual Studio 2005, and click "File-New-Project" and select ASP.NET Web Service Application.



If you don't have that project type installed, you may need to change the installed features of Visual Studio on your machine. The good think about this project type is that it creates a web service as a DLL and an asmx and not as a web site.



After the project is created, change the file names, namespace and assembly names as needed (make sure the namespace of the webservice attribute is changed - you don't want "http://tempuri.org/" as your namespace...) and most importantly - sign the assembly as strong name (right click the project, properties, signing tab, sign the assembly).




Now we have to find out the key that was given to the assembly when it was signed. To do that you must first build the web service (I use ctrl-shft-B, or right click the project and select build) and then either drag and drop the DLL to the assembly folder, right click and get the key:



Or you can run a command line from the visual studio SDK (start-programs-visual studio 2005-visual studio tools-Visual Studio 2005 Command Prompt) and type

sn -T "c:\temp\WebService1\WebService1\bin\WebService1.dll"



Once we have the public key token, we can change the web service's asmx file to use the DLL that will be deployed to the gac. Right click the service1.asmx file and choose "View Markup". You need to change the markup to point to the DLL that will be in the GAC, so this is the format you need to use:



<%@ WebService Language="C#" Class="WebService1.Service1, WebService1, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=db33ac6baa259170" %>

Expalantion:

  1. The "WebService1.Service1" part is the "namespace dot classname" of your code. To get that, you will need to open your .cs file, and copy the namespace you are using, add a dot after it, and add the class name.

  2. The "WebService1" (after the comma) is the name of the DLL that you may have set in the project properties under the "application" tab (the "assembly name")

  3. The public key token is the key we got earlier.





Note - I am not sure if this is needed, but I also registered my web service as a safe control in the web.config, before I used any sharepoint code. It's worth checking if this is required or not, and I will update if I have time to test.



Step 2 - Setting up the Build Events

While in the project's properties, switch to the "Build Events" tab. Paste the following in the "post-build event command line" box:


"c:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" /i "$(TargetPath)" /f

copy "$(ProjectDir)\*.asmx" "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS"

"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\bin\RecycleAppPools.vbs"

"c:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\disco" http://localhost/_layouts/service1.asmx /o:"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS"




Explanation:


  1. The first line puts the DLL in the GAC.


  2. The second line copies the web service asmx file to the layouts folder. You should make sure your projects do not create same names for asmx files, or they will overwrite eachother!


  3. The third line recycles the application pools. I am doing this with a vbs file that I have written, and placed in the 12 hive's "\bin" folder for my comfort. Here are the contents of the file:


    Set locator = CreateObject("WbemScripting.SWbemLocator")

    Set Service = locator.connectserver(strServer, "root/MicrosoftIISv2")

    Set APCollection = Service.InstancesOf("IISApplicationPool")

    For Each APInstance In APCollection

    APInstance.Recycle

    Next

    echo "Recycle Complete."


  4. The last line build a discovery file for the web service in the layouts folder. if you installed your visual studio 2005 in a different location, you will need to change the folder path to the disco.exe file, and if you changed the name of the asmx (or if you have more than one) you will need to modify that as well. You will also want to change the url to point to a sharepoint site on your machine if you are not using the "localhost" header. In my case, I use the header "portal", so I change is to "http://portal".



Step 3- Change the F5 behavior

To change what happens when you press F5, switch to the "Web" tab, and change the start url to the url to the web service (I use http://portal/_layouts/service1.asmx), then change the "use IIS web server" and change the project url to the rool sharepoint url (I use "http://portal") and tick the "override application root URL and type the same url as the project url:





Last Step - set up your web.config to allow debugging

If you want F5 to work, and have visual studio debug the web service when you press it, the sharepoint web.config should be changed to allow debugging. To do that, you will need to open the web.config file for the virtual server you are using (in my case "http://portal" - which means the web.config is under "c:\Inetpub\wwwroot\wss\VirtualDirectories\portal80\web.config") and find the "<compilation>" tag, and set the attribute "debug" to true:




This is it!

If you didn't miss anything, you should be able now to press F5 and the web service will launch in the sharepoint context, with debugging in the visual studio! You can set breakpoints and debug the web service.







Tuesday, August 28, 2007

Passed another exam

Hi all,
I had passed yet another MSCTS 70-631 Windows SharePoint Services 3.0, Configuring. This completes the set, and now I have all four sharepoint exams under my belt.
I have to say that this one was the most difficult one for me, since it had many questions about topics I try not to touch - for example load balancing. I really don't understand why a sharepoint expert needs to know load balancing and what is the difference between multicast and unicast, and how you should configure the network adapters. On the other hand, some of the questions were easy for me because they had more to do with development (for example, if a custom web part gets installed and crashes with an unhandled error - what should the administrator do? The answer was easy for me, because I am a developer - but is it fair for an admin to get asked that?)
So all in all I am happy about this, and I plan to promptly forget everything I know about WSS and MOSS configuration and installation. Heck, people keep asking me questions about this - "Ishai, how do I configure forms authentication?" or "Ishai, I configured anonymous and it doesn't work" - and I am sick of it. My answer from now on will be "I don't know, but I will write you a web part to make it all better!".

Speaking of webparts, I didn't get any complaints about my enhanced content query webpart, so I am moving on and will start thinking of my next open source project. It is still in infancy, but I hope to let you know about it soon.

On an unrelated matter, I will be speaking at the Israeli SharePoint user group next month (19/9) about features and templates (hebrew web site) so if you are in Israel, come over and say hello.

While I am there, there is a Canberra user group without me, and I am still looking for presenters. I will keep you updated if you want to know who will talk. Last month we had Anthony Woodward from my company talk about sharepoint record managment compliance in general and with Australian-specific notes. It was quite interesting. If you want to present, give me a buzz (my contact form is on this blog - look for it!)

Sunday, August 26, 2007

What's good and what's missing from WSS Visual Studio Extensions 1.1?


The Windows SharePoint Services 3.0 Tools: Visual Studio 2005 Extensions, Version 1.1 CTP were released, with
"Support for 'Web Solution Package' editing, List Instance item template ,List Event Handler item template,Bug fixes", so I decided to take a quick look at what's in the web part project template.


First, I liked the fact that now by default when you create a web part project you don't get the "Render" event that you used to get in the past. This caused many developers to start writing html in the Render event, and then be puzzled when the events on controls didn't work (see Server side controls and data binding in web parts in this blog).



I am still looking into it, but one thing that I would expect from a webpart project template is a wizard when creating the project that asks:


  1. Do you want the web part to support connections? (add stubs for connection interface)

  2. Do you want the web part to connect to a specific list? (add properties and functions to connect to a list)

  3. Do you want a custom toolpane?



What do you think? what else would you want from a template?






Wednesday, August 22, 2007

New SDK, with a tool for BDC

Hey!

A new MOSS SDK is available, and it comes with a free tool to create BDC definitions (is this the end of MetaMan? I wonder...).
The new tool is called "Business Data Catalog Definition Editor"

MSDN already have articles on how to use the tool with web services and other systems.


Installing requires you to install SQL server 2005, so don't install on your production server (duh!), and be prepared for some drastic changes to your system when you install on your dev box.



The good news is that it can be installed on windows XP, proving that Microsoft are listening to us developers, and to our gripes about developing on a server...

I tried installing by running the MSI directly, but that didn't work (didn't install the SQL express, and the installation failed when it couldn't find SQL express).







Monday, August 20, 2007

Slides from Tech.Ed Australia 2007 - Templates and Features

Here are the slides I did in my presentation with Milan Gross (his slides are not included here, as they are his to publish):


I start we had the Tech.Ed image:




Next, our names and titles:





Some links and referances:








The agenda of the session (Milan covered that):





Here I spoke about the difference between templates (save as template for a list or a site) and definitions (file system xml and aspx files that allow developers and administrators have more control over the sites after deployment)




A quick explanation of how a solution package is built. For example I showed the package of the "print list" feature as it was done by Scott Hillier.



Next I discussed what are DDF files and why we need them (we need them to let makecab.exe know how to build our cab file - using folders), as well as giving some alternatives such as cabarc that allows you to build a cab file with folders in it - no need for a definition file. I also mentioned a tool I never used - WSPBuilder




Now to my part of the demo - I showed how to create a feature for a webpart and how to package it. I used my own Enhanced Content Query WebPart solution package to demonstrate this, and you can download that from codeplex!









After that Milan took over and showed a scenario that used the Solution Generator (part of the WSS extensions for Visual Studio) to create a site definition, with lists that are connected to workflows and have BDC fields and all. It was pretty complex, and I felt we needed more time to really explain it all.


And that was my first presentation. I will let you know about the second one soon.






How to remove farm administrators permissions from a site?

A friend asked me how can we remove farm administrators from having permissions on a specific site. Removing the permissions from the site collection and the site itself just did not work - the users who were farm admins could still browse the site and do whatever they want to it.
The answer was that in central admin, under "applications" there is a link: "Policy for web application" which allows you to set permissions at the web application level. It turns out that the farm admins have full permissions there for the application, and until you remove them from there, they will be able to go into any site in the web application.

Hotfix lets you store WSS data outside of SQL

If you missed this, give the following KB article a read. It is short, and to the point - a new hotfix is out that lets you store WSS data on external storage- out of SQL.
KB938499

Saturday, August 11, 2007

My mark @ Tech.Ed

Ok, I am back, and boy, do I have some stories...

On the first day I had a presentation about templates and features with US consultant and trainer Milan Gross. I was not very satisfied with our presentation, and I think we should have kept the scenario simpler.
I showed how I built the wsp solution and features for the Enhanced Content Query Web Part and for the Print List feature.



My next presentation was with Gayan Peiris, who left my company to join Microsoft just a month ago. We did a presentation on upgrade and migration from sharepoint 2003 to 2007.
Now, before people start asking me how to upgrade I have to say that that was Gayan's part of the presentation. My part was warning about the customization upgrade (custom webparts, event handlers etc.), and about what is different in the new version that requires re-thinking on the information architecture.
That Session went very well, if a bit tight on time, and we got good reviews (7.62) even if we didn't get to the top ten.



After our presentation we left with all of tech.ed to Movie World, where I went, for the first time in my life, on a roller coaster - Lethal Weapon. I have been told by people with more experience that this is the most frightening ride ever, everywhere. I can now believe it.
The only reason I was brave enough to go on the ride was that it was night - I couldn't see the ride. I was sure that a "lethal weapon" ride would be a video game with shooting in it.

Here is a picture of me on the ride:




Here is a movie of what the ride looks like:









Friday, August 10, 2007

MCTS in MOSS configuration

Well, Tech.ED Australia 2007 is over (I will post some details soon) and I am happy to let you know that I took the "Configuring Microsoft Office SharePoint Server 2007" exam (70-630) and passed! I only got 3 questions (out of 51) wrong, so I am pretty happy with myself right now.
Now I only have to do the WSS configuration exam to have the entire set!

Sunday, August 05, 2007

Off to teched

So this is it - the moment I have been waiting for and the reason I didnt write anything in the last month. I am flying to the goldcoast tomorrow and will be presenting there this week. I will also be at the meet the experts dinner (as a self proclaimed expert) and at the influencer party (as an MVP).
See you there!
p.s. I released today a technical refresh to the content query web part project. more features, less bugs!

Wednesday, August 01, 2007

Modifying Search to Add a Send Link By Email link


My customer wanted a "send link by email" next to each search result item. "Easy", I thought and went ahead to edit the xslt for the search results. But I found out a problem with links that had spaces in them - the link in outlook would appear broken. To solve this, I had to write some simple javascript, and the process is described below.




Problem:


If you want a mailto link (href="mailto") that has in the body a link to a file, you may get a link like this:


href="mailto:body=http://server/site/library/this is my file.doc"



This is a problem, because the file name contains spaces. The email will open with a broken link in it's body:




Solution:

Luckily, SharePoint has a javascript functions that solves that for us. They are "escapeProperly" and "navigateMailToLink". What I did is create a function of my own in the page:


function SendEmailWithLink(link)
{
var link = "mailto:?body=" + escapeProperly(link);
navigateMailToLink(link);
return false;
}



And my link is:

href="#nowhere" onclick="javascript:SendEmailWithLink('');"




This article would'nt be complete if I didn't explain why I did this - this if for the search results xslt - the idea was to add a "send link by email" button next to each search result. The solution - change the xslt to include the script above, and a button like this:



<a title="E-mail a Link" href="#nowhere">
<xsl:attribute name="onclick">
javascript:SendEmailWithLink('<xsl:value-of select="$url"></xsl:value-of>');
</xsl:attribute>
<img border="0" src="/_layouts/images/ICMSG.GIF"/> 
</a>





We also added other actions (I will try to get them published) and came up with the following for each result item: