Thursday, March 25, 2010

A new internet site using SharePoint - indigenous.gov.au

Take a look at the new site - an external facing internet site created for the Australian goverment to spread news about the Indigenous Australian related projects. My company, Extelligent Design, helped build the site which includes such features as tagging (with a custom built tag cloud), content query web parts and jquery picture library slide show components. I have helped write some of the components above as part of my day job.

Update: I was asked what the slide show component is - the answer is not flash, nor sliverlight - but a jquery script called galleryview (http://www.spaceforaname.com/jquery/galleryview/) that we customized for our needs (different CSS, do not show descriptions and so on) and then embedded in a web control that pulls information from a picture library, formats it as a UL list, and then uses script to show it as a slide show. Easy, and accessible - because even if you don't have javascript enabled you see the picture as a UL list (which is shown anyway at the bottom to make it accessible for other people).

Sunday, March 14, 2010

My SharePoint 2010 book available for pre-order

I just noticed that Amazon UK is making my 2010 book available for pre-order.
This is a bit stressful for me, since I have only written 5 chapters so far (out of 14 planned) - I guess I'd better start typing faster!
If you are interested in a end user, how to book that covers all the basics for your end users, have a look at the link, and keep a close watch - I will be posting updates. I can tell you that the book will be very similar in structure to the 2007 book I have already published.

Friday, March 12, 2010

Adding the "Sort by modified date" option to SharePoint 2010 search

By default in SharePoint 2010, the search actions link web part does not show the option to change the sorting order to sort by modified date. However, it is easily added back through the web part's properties. Note that it is now shown as a dropdown and not as a link as it did in SharePoint 2007...

SharePoint Foundation - forcing a search index

If you have SharePoint foundation and you want it to index your sites NOW, all you have to do is go to the service job definitions page and tell the timer job to start.
To do it - open central administration > monitoring>review job definitions>"sharepoint foundation search refresh" and click the "run now" button at the bottom.

SharePoint Foundation - no indexer error

If you have installed just SharePoint Foundation (SPF) and created a site collection before turning search service on (which is off by default when you install SPF), you will get an error "your search cannot be completed because this site is not assigned to an indexer.".
After starting the service it still did not resolve the issue, so I tried to find where to assign the indexer to the site. Before long, I realized that it is not on the site collection settings, nor on the web application settings but instead on the content database setting.
Just go to the manage content databases screen in central management, click on the content database in question (usually WSS_Content) and in the middle of the page there is the option to select a search server.

PS - is anyone else finding it puzzling that the default name for a SPF database starts with WSS? Maybe MS will change that before release?

Friday, February 26, 2010

Web Part architecture - what I had to fix in ChartPart

An important aspect of web part architecture is "redeploy-ability".
Redeploy-ability is when you want to use the web part as part of a site template. When you develop a web part, you need to think of redeployment - since the web part will need to be deployed with a set of pre-configured settings into a new site - the web part's properties. If those properties cannot be set as part of the site template, the web part cannot be redeployed.

Let me use ChartPart as an example.
If you don't already know, there is a project called "ChartPart" in codeplex, that shows lists as charts:
chartpart2-1.png
The web part needs to store a lot of information in its properties when it gets configured. For example, it needs to store the X axis and the Y axis columns. For this, the developer created a property called "XAxisSourceColumns", of type "List<string>". That allowed him to add column names to the list easily in the code.
The problem with this, is that this property has to be stored with the web part, and is required for the web part to work - that means if I export the web part, then I expect that when I import it again, all the settings will be there. With a property of type "List<string>", this will not happen - because the web part system does not know how to serialize a "List<string>" object into XML (a .webpart file) and doesnt export those settings. Then, if you import the web part you just exported, you lost those settings. I recommend storing the information as a simple type (defining the properties as strings for example) for serialization purposes.

A second problem, and one that you can see with a lot of Microsoft web parts as well as ChartPart (for example the Content Query Web Part) is that when a web part is connected to a list, the property stores the list's ID - a GUID, instead of the list name. This is smart - because someone may rename the list, and the ID will remain the same, so the web part will keep working. On the other hand - using an ID means that you lose "redeploy-ability" - since a list's ID gets generated randomly every time the site gets created, if your template has the web part set with a specific ID, then when you create a new site off that template, the web part will be broken - since it will not find a list with that ID in the site.
I recommend you store in the web part both the list's ID and the list's name. This way, if there is a list with the ID that was specified, the web part uses it, and therefore does not break when the list's title changes. But if there is no list with that ID (or no ID was set - which is what we will do in a template), the web part will look for a list with the name that was set - which means it will support redeploy-ability.

Finally, a problem with many web parts that allow linking to objects in other sites is that the site url property often expects a value - and does not support a "this site" option (which I like to add as a blank value). This means again that we lose "redeploy-ability" - since there is no way to configure the web part to look in this new site that was created from the template - the web part expects a link to a site...
I recommend supporting a "leave site url empty for current site" as a minimum requirement for a web part, and if you want to be real good, add support for relative URLs.

So how do we resolve this?


Usually we implement "redeploy-ability" when we start writing the web part - we create properties of type string that are not browse-able, and then create ToolParts that populate them using a good user interface - that gets the list of columns (in this example) from the user, then serializes it into a string that gets saved in the web part. It does mean a bit more effort to serialize the list and deserialize the string - but the end result is that when you export the web part you get what you expected - the serialized property gets stored in the .webpart file.

Supporting "blank" as current site url
In ChartPart to make this change was easy - I looked for all the places that SiteUrl property was used, and changed the code to check if the siteurl property is empty then to use the current site url. This is not as effective as you can make it (a function that gets a SPWeb object would have done it better) but it saved me a lot of refactoring:

string url = this.SiteUrl;
if (string.IsNullOrEmpty(url))
   url = SPContext.Current.Web.Url;
using (SPSite site = new SPSite(url)) 
{
   ...
}

Supporting list names as well as IDs
In ChartPart I added a property called ListName and then made the following modifications to all locations that referred to the list ID:

if(this.ListId != null && this.ListId != Guid.Empty)
   list = web.Lists[this.ListId];
else if(!string.IsNullOrEmpty(this.ListName))
   list = web.Lists[this.ListName];
I also changed the toolpart so that when a user picks a list from the dropdown, both the list ID and the list name gets stored.

Serializing complex type properties

In ChartPart for example, since it was already developed and I had to keep supporting existing web parts that store the information as a list, I couldnt remove the existing properties. However, what I did is add a string property for each one of the "List<T>" properties, that return the serialized value of the "List<T>" property it reflects, and when set it sets the "List<T>" property. Here is an example:

//the original property
[Personalizable(PersonalizationScope.Shared)]
public List XAxisSourceColumns
{
    get;
    set;
}
//this is for supporting exporting and importing the web part (which does not serialize List)
//we will have these as pipe seperated strings.
[Personalizable(PersonalizationScope.Shared)]
public string XAxisSourceColumnNames
{
    get
    {
        if (this.XAxisSourceColumns != null)
        {
            string ret = "";
            foreach (string xAxisColumn in this.XAxisSourceColumns)
            {
                if (!string.IsNullOrEmpty(ret))
                    ret += "|";
                ret += xAxisColumn;
            }
            return ret;
        }
        else
            return "";
    }
    set
    {
        this.XAxisSourceColumns = new List();
        if(!string.IsNullOrEmpty(value))
        {                 
            string[] values = value.Split('|');
            this.XAxisSourceColumns.AddRange(values);
        }                
            
    }
}

Monday, February 22, 2010

Making the content query web part deployable

My biggest issue with the CQWP? its not deployable when connected to a specific list in a specific site.
If you configure a CQWP web part to connect to a list in a site, the web part saves the ID (GUID) of the list - and if you want to deploy the web part as part of a feature or onet.xml so that the web part gets added every time a user creates a site, it will fail - because the ID of the list changes every time you create a new site.

My solution? override the web part and implement the following 2 functions:

public class MyBetterQueryWebPart: ContentByQueryWebPart
{
public void SetListGuid()
        {
            if (!string.IsNullOrEmpty(this.WebUrl) && 
!string.IsNullOrEmpty(this.ListName))
            {
                using (SPWeb web = SPContext.Current.Site.OpenWeb(this.WebUrl, true))
                {
                    SPList list = web.Lists[this.ListName];
                    this.ListGuid = list.ID.ToString();
 
                }
            }
 
        }
        protected override void OnLoad(EventArgs e)
        {
            if (!string.IsNullOrEmpty(this.WebUrl) && 
!string.IsNullOrEmpty(this.ListName) && 
string.IsNullOrEmpty(this.ListGuid))
            {
                SetListGuid();
            }
            base.OnLoad(e);
        }
}

This code checks if the web part is in list mode (has a list name and a web url) and is "corrupt" - doesnt have a list ID. if it finds that is the case, it dynamically loads the list ID based on the site url and the list name.

Tuesday, February 16, 2010

Help me decide on a logo for my new company - Extelligent Design

Yes, I have opened my new company and am now working for myself. I will give details soon - including releasing the web site, but I cannot do that without a decent logo. Can you help?

If you are interested in earning $300 USD, go to my logo contest and submit an entry there. It finishes on the 20th of February, so there is not much time.

You don't have to submit an entry - you can also vote on the entries other people entered and let me know what you like. To do that, go to the contest next week when the submissions has ended (after the 20th) and login to vote (you will have to register in that site - which I recommend if you have a few hours to spend looking at photoshop wonders).

At the end of next week I will announce the winner and release my brand new site - offering sharepoint consulting, training and products (I am now the official distributer of KWizCom products in Australia and New Zealand)

Monday, February 15, 2010

Error: "Failed to connect to the database server or the database name does not exist"

I have installed a new development computer at home (quad core, 12GB RAM dell studio xps) and have set up boot from VHD - to a win2008r2 machine.
I figured that setting up SQL and domain controlers on the host would be best, and hyper-v for guests (sharepoint 2007 and sharepoint 2010 beta) that would connect to the host machine's domain and sql.
However, when I tried to connect either version of sharepoint to the SQL, I got the same error:
---------------------------
SharePoint Products and Technologies Configuration Wizard
---------------------------
Failed to connect to the database server or the database name does not exist.
Ensure the database server exists, is a Sql server, and that you have the appropriate permissions to access the database server.
To diagnose the problem, review the extended error information located at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\LOGS\PSCDiagnostics_2_17_2010_8_45_30_259_232810164.log.
Please consult the SharePoint Products and Technologies Configuration Wizard help for additional information regarding database server security configuration and network access.

The solution? I turned off the firewall on the host machine...So now I am open for attacks, but atleast my virtual machines can talk to my sql on the host... So instead of leaving it off, I just followed this MSDN article that explains how to open the ports for SQL...

Wednesday, December 16, 2009

ErrorWebPart when loading a CQWP using the object model

When you are using the object model from a console or windows application to load web parts, you will notice that the CQWP (content query web part) instances all show up as "ErrorWebPart", and do not allow changing any of the web part's settings.

This is because when you load the web part, the web part code tries to access all kinds of data from it's context. Since you are loading the web part without a context (that is, from a windows application instead of a web application) the web part fails to get the context, and gets reported as an ErrorWebPart, when infact it works well if you look at it on the web interface.

I have yet to find a solution for this, but one workaround that works is to move your code to a web page, in the "_layouts" folder. This allows the code to run withing the site's context, and so accessing the web part does not cause it to report ErrorWebPart.

Update: It has been brought to my attention by a reader that Sandeep has a workaround for this:http://sharepoint-insight.blogspot.com/2008/10/sharepoint-all-webparts-appear-as.html