in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

Sander's Blog

  • "This site does not host personal sites" error with CreatePersonalSite()

    Quick note on this error, it is not documented and I did not find any answers on Google;

    When you create a site by calling ProfileUser.CreatePersonalSite() you can get the error "This site does not host personal sites". To resolve this, create a root sitecollection on your MySite web application.

    btw. make sure you use the "MySite Host" template for this rootsite.

  • Centralize Master page and Alternate CSS settings

    Goals:

    • Control all Master pages, Application Master pages and the Alternate CSS url's from code and being able to change these settings at run-time
    • Have only a single copy of the Master page(s) and the alternate stylesheet(s) in your whole farm (ideally on the file system in the _/layouts folder)
    • Apply a custom Master page and stylesheet to Non-Publishing teamsites
    • Apply a different Master page and stylesheet to the application pages (_/layouts pages)

    To achieve this I want to use a HttpModule to set the Masterpage and alternate CSS settings at run time. This is probably the only way to change the application.master (do some Googling on it if you don't believe me).

    This HttpModule should determine for every request what combination of stylesheet and master should be used.

    To update the Masterpage you can easily tell the current page to use another master at runtime:

    Page.MasterPageFile = "~/layouts/custom.master";

    This works for both regular pages and application pages, although you probably want to use different master pages for the two.

    Updating the Alternate CSS is quite a bit harder. This is because the CSS link is rendered by a separate control inside the masterpage:

    <SharePoint:CssLink runat="Server"/>

    If you dive into the source of this control by using Reflector you'll find a Render() method in which the actual CSS links are written to the screen.

    This Render method will write several CSS links from several sources:

    1st: It will ask the current SPContext for its CssReferences. This is an internal collection; I didn't find a way to modify this collection

    2nd: It writes a link from its own private string m_primaryCssUrl. This is probably the link to core.css. Because it is private you cannot and you do not want to change it. I believe this string is assigned in a obfuscated method called SetDefaults() because it is not assigned anywhere else.

    3th:  It writes a link from its own pubic property DefaultURL, finally a public property we can set! Setting this property can look like:

    <SharePoint:CssLink runat="Server" DefaultUrl="~/layouts/custom.master"/>

    You'll hard-code this property in the Masterpage, meaning that every Masterpage is connected to a specific Stylesheet. If you want to be able to dynamically attach different stylesheets from code (for example based on the user or based on the time of the day, the weather, etc), this is not the way for you.

    So you really want to control the Stylesheet on run-time? Fine, but I'll warn you - it's not going to be nice.

    First thing, you're not able to access the SharePoint:CssLink  control programmatically to set the DefaultUrl. This is because it has no Id and is not registered in the code-behind. Looping to all controls does not help as well; I can't get a hook to the control...

    Luckily the Render() method of the CssLink control also has a 4th source from which it retrieves CSS links;

    4th: It writes a link which is defined in one of the current SPWeb properties: m_alternateCssUrl. This property is also accessible from the public property SPWeb.AlternateCssUrl.

    So if we set this property it will automatically be rendered by the SharePoint:CssLink control :-)

    The downsize to this is that you are actually changing SPWeb properties. If you don't save the SPWeb these changes are discarded after the request, but if the SPWeb is saved somewhere along the page execution path, this setting will be saved as well. Not very nice.  

    Custom CssLink Control?

    So this SharePoint:CssLink control is not very easy to manipulate at run-time. For the moment I am fine setting the DefaultUrl property in the control itself. This leaves me with a bit less flexibility, but it works and it is actually default functionality. Due to the limited time I have available it's a nice trade-off.  

    If you are ready to take more serious matters, there are 2 options I want to consider:

    • 1. Overriding the SharePoint:CssLink control and add our own logic on top of it
    • 2. Creating our own SharePoint:CssLink control
    • 3. Adding the CSS Link directly from the HttpModule.

    The code if have so far look like (simplified):

    public class MasterPageModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.PreRequestHandlerExecute += context_PreRequestHandlerExecute;
        }
    
        static void context_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            Page page = HttpContext.Current.CurrentHandler as Page;
            if (page != null)
            {
                page.PreInit += page_PreInit;
            }
        }
    
        static void page_PreInit(object sender, EventArgs e)
        {
            Page page = sender as Page;
    
            if (page.MasterPageFile.ToLower().Contains("application.master"))
            {
                page.MasterPageFile = "~/_layouts/customApplication.master";
            }
            else if (page.MasterPageFile.ToLower().Contains("default.master"))
            {
                page.MasterPageFile = "~/_layouts/custom.master";
            }
    
        }
    
        public void Dispose()
        {
        }
    }
     
    [update] The solution to set the masterpage as shown above does not work for publishing pages. I'm still investigating this issue.
  • Automatically upload udcx files to Central Admin

    Easier than uploading by hand, create a batch file in the folder containing your udcx files and add these lines:

    for /f "tokens=*" %%a in ('dir /b "*.udcx"') do (
    stsadm -o adddataconnectionfile -filename %%a -overwrite true[default]/false -webaccessible true/false[default] -category "Category1" 
    )

    The -overwrite, -webaccessible and category params are optional.

    More info on the stsadm command: http://technet.microsoft.com/en-us/library/cc263067.aspx 

     

  • Using database snaphots for testing SharePoint

    This can be a real time-saver! Do you know of the concept of Snapshots in SQL Server 2005? No? It is one of the new features available in the Enterprise edition of SQL 2005.

    Database Snapshots allows you to create a read-only copy of your database that can be used for other purposes, such as reporting, auditing or recovering data. Currently, you can do that by running a backup and restoring it to another database. However, the big advantage you gain by using Database Snapshots instead is the speed at which a snapshot occurs, as well as the ability to create multiple snapshots at different points in time quickly.

    If you want to read more about the concept, the advantages and the disadvantages you can read the great post of Greg Robidoux at:
    http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1176142,00.html#cons

    For SharePoint development it is a great way to test some configuration, and if you changed your mind, roll back all the changes you've made in just a few seconds! This can be handy for testing scripts, for manual configuration in the UI, or for example to give demo's.

    The following scripts create and restore a snapshot on a SharePoint Content Database (please see the limitions at the bottom of this post)

    Creating the snapshot

    To create the snapshot you can run the following T-SQL (run a new Query dialog in SQL Server Management Studio or create a .sql file)

    CREATE DATABASE WSS_Content_Snapshot ON -- [The name of the snapshot]
    (
        NAME = WSS_Content_Snapshot, -- [The name of the logical filename]
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\Snapshots\WSS_Content_Snapshot.ss'  -- [The filename and location of the snapshot]
    )

    AS SNAPSHOT OF WSS_Content -- [The original Content Database]

    Restoring 

    Restoring a database is as easy as the following line:
    RESTORE DATABASE WSS_Content FROM DATABASE_SNAPSHOT = 'WSS_Content_Snapshot'

    You might run into an error if other users (SharePoint processes) are still connected to the database: 

    Database state cannot be changed while other users are using the database 'WSS_Content'
    RESTORE DATABASE is terminating abnormally.

    To avoid this you can first run a script to kill all existing processes:

    -- First, kill all active processes
    DECLARE @DatabaseName nvarchar(50)
    SET @DatabaseName = N'WSS_Content'

    DECLARE @SQL varchar(max)
    SET @SQL = ''

    SELECT @SQL = @SQL + 'Kill ' + Convert(varchar, SPId) + ';' FROM MASTER..SysProcesses WHERE DBId = DB_ID(@DatabaseName) AND SPId <> @@SPId

    EXEC(@SQL)

    -- Second, Restore the database from the snapshot
    RESTORE DATABASE WSS_Content FROM DATABASE_SNAPSHOT = 'WSS_Content_Snapshot'

    Limitions

    Please note that the snapshot above is limited to the Content Database, the changes you can restore include:
    - Adding/Deleting/Editing Subsites
    - Adding/Deleting/Editing Lists
    - Adding/Deleting/Editing List-items
    - Adding/Deleting/Editing Content Types
    - Adding/Deleting/Editing Webparts
    -
    Enabling/Disabling Features
    - Adding/Deleting/Editing Workflows
    - Editing the Navigation
    - Editing the Sitecollection in the Site Settings (not deleting or creating sitecollections!)
    - ...

    Some things you cannot do:
    - Sitecollections are registered in the configuration database, if you add or delete sitecollections you must ensure you also snapshot the configuration database
    - Solution packages (wsp files) are registered at the Farm level, if you add or delete solution packages you must ensure you also snapshot the configuration database

    This is not an exhausting list, please beware of any configuration done at the Farm level since this is registered in the configuration database. If you really want to snapshot your whole environment I suggest you lock all databases, create snapshots of all databases and restore all databases at the same time. You might want to try some variations, but I would not recommend using it in any environment containing critical data since it is not supported my MS. 

  • Troubleshooting tempdb growth when crawling SharePoint data

    Today I received a message from our Database admin concerning the Temp Database. It seems a SharePoint process is filling the TempDB up to 15gb. After that it reaches it physical limit (disk space is very limited..) and throws an exception:

    "The transaction log for database 'tempdb' is full. To find out why space in the log cannot be reused, see the log_reuse_wait_desc column in sys.databases"

    From this moment on you can consider your SQL server to be crashed, since probably every application uses the Temp database and every query using the TempDB will result in an error.

    If you follow the suggestion given in de event-log exception you're going to check the log_reuse_wait_desc, in my case this quotes: "ACTIVE_TRANSACTION".
    In the SQL Server Activity Monitor you can see the current application using the TempDB is "Windows SharePoint Server". The details of this activity shows that there is a MOSS Crawler stored procedure (forgot to copy the name) running.

    Usually the crawler will use the Temp DB while indexing, it will then flush this data to the file system and clear the Temp database. I don't know what triggers this 'flush', but I assume it flushes between different sitecollections. Why this assumption?
    1. Because I've only got 1 (extremely large) sitecollection to index and the Temp database never shrinks in the crawl process, it just grows until it crashes.
    2. Because I can't find any other information about this process..

    When you will stop a crawl (Manage Content Sources -> Stop Crawl) it will wait until the current data is flushed, in my case  (1 extremely large sitecollection) this means it will wait until it crashes. After it crashes you'll have to empty the Temp database manually. 

    I'm now in the process of telling SharePoint not to do the crawling. This is not easy since it must complete the current crawl before it can flush the data and inherently stop crawling. You can't even stop the "Microsoft Office SharePoint Search" service, it will tell you it's busy. To stop this thing you will really need to kill the MSSearch process (I don't advice you to do this, but it is a last resort..).

    After you've killed the process it is automatically started again by the "Microsoft Office SharePoint Search" service. Workaround is to disable the service and then kill the process. 

    Now you might think you can re-configure or re-schedule the Content Source, wrong! Since you've stopped the "Microsoft Office SharePoint Search" service, you cannot use the Search Settings pages in your Shared Service Provider. 

    Once you will re-start the "Microsoft Office SharePoint Search" service it will automatically continue the Crawl, even before you can stop it. 

    Following question will now be; how can I stop this full Crawl? As far as I found I've got 2 options left:
    1. Give the Temp database more space and wait for the crawl to be finished
    2. Hack in the database to remove the current Crawl according to this thread: http://msmvps.com/blogs/obts/archive/2006/12/18/432542.aspx
    3. ...

    Of course I opt for the first one which is obviously the most healthy and logical option. 

    I hope you will never need it, but if you will you now have my experience at your dispense..

  • [Dutch] Nintex Workflow presentation - DIWUG

    (Sorry for this post being Dutch. Tonight I presented Nintex workflow at the Dutch Information Worker Group. In this post I will publish the sheets and code I've used)

    In navolging tot de Nintex Workflow presentatie kan je nu de slides downloaden van m'n blog.

    Ik heb de presentatie bijgewerkt met:
    - Een aantal dingen die ik vergeten ben
    - Toelichtingen en details over de demo's
    - Verwijzigingen naar help-files en de Nintex SDK
    - Contact gegevens 
    - De cut-scenes: sheets die de presentatie niet gered hebben..

    De code voor een Custom Activity project kan je eveneens downloaden van m'n blog. Gebruik hierbij de Nintex SDK om dit voorbeeld te installeren en aan te passen.

    Ik wil jullie via deze weg nogmaal bedanken voor de grote opkomst en de interesse. Vragen kan je uiteraard kwijt in de comments.

    Sander

  • Synchronize Content Types and Site Columns between teamsites

    This morning I had a conversation with a colleague about Schemalogic. This tool enables you to manage your content types in a central location for your whole farm or even multiple farms.

    Based on this concept I thought it might be cool to make a 'master' sitecollection with all site-columns and content-types and reflect those to all other site collections. Don't ask me why I don't use features or other concepts, we're currently evaluating the different possibilities..

    This is my quick-and-dirty script to sync the Site Columns and Content Types between sites. It does te following:

    1. Create Site Columns if they don't exist
    2. Update Site Columns if the are different
    3. Create Content Types if they don't exist
    4. Add/Remove Site Columns to existing Content Types
    5. Remove Site Columns if they are removed from the master site

    A number of questions arise when building the script:

    - How to handle lookup fields, since they link to a local list. We could ignore these fields or assume the list exists..
    - It should remove Content Types if they are removed from the master site (not very complex, just forgot it in this version)
    - It should iterate through all site collections instead of just one

    Well, I'll continue evaluating the other possibilities, but we'll might go for this method. In meanwhile you are free to use the (beta)code I've written for this concept. Please feel free to comment your findings and suggestions!

    static void Main(string[] args)
    {
        SPSite sourceRoot = new SPSite("http://wss3dev/sites/TEST2");
        SPWeb sourceWeb = sourceRoot.OpenWeb();
    
        SPSite targetRoot = new SPSite("http://wss3dev/sites/TEST3");
        SPWeb targetWeb = targetRoot.OpenWeb();
    
        SyncSiteColumns(sourceWeb, targetWeb);
        SyncContentTypes(sourceWeb, targetWeb);
        DeleteSiteColumns(sourceWeb, targetWeb);
    }
    
    /// <summary>
    /// Syncs content types based on a sourceweb and a targetweb.
    /// Add's Content Types if they don't exist.
    /// Add's/Removes Fields in existing Content Types
    /// </summary>
    /// <param name="sourceWeb"></param>
    /// <param name="targetWeb"></param>
    private static void SyncContentTypes(SPWeb sourceWeb, SPWeb targetWeb)
    {
        foreach (SPContentType sourceType in sourceWeb.ContentTypes)
        {
            if (!sourceType.ReadOnly && !sourceType.Sealed && !sourceType.Hidden) // Exclude non-editable types
            {
                bool update = false;
    
                if (targetWeb.ContentTypes[sourceType.Name] == null)
                {
                    SPContentType parentContentType = targetWeb.ContentTypes[sourceType.Parent.Name];
                    SPContentTypeCollection collection = targetWeb.ContentTypes;
    
                    SPContentType newContentType = new SPContentType(parentContentType, collection, sourceType.Name);
                    targetWeb.ContentTypes.Add(newContentType);
    
                    newContentType = targetWeb.ContentTypes[sourceType.Name];
    
                    foreach (SPField field in sourceType.Fields)
                    {
                        if (newContentType.FieldLinks[field.InternalName] == null)
                        {
                            newContentType.FieldLinks.Add(new SPFieldLink(targetWeb.Fields[field.Title]));
                        }
                    }
    
                    newContentType.Update(false); // Do not update children, there are no children yet
                }
                else
                {
                    SPContentType targetType = targetWeb.ContentTypes[sourceType.Name];
    
                    foreach (SPField sourceField in sourceType.Fields)
                    {
                        if (targetType.FieldLinks[targetWeb.Fields.GetFieldByInternalName(sourceField.InternalName).Id] == null)
                        {
                            targetType.FieldLinks.Add(new SPFieldLink(targetWeb.Fields.GetFieldByInternalName(sourceField.InternalName)));
                            update = true;
                        }
                    }
    
                    for (int i = (targetType.FieldLinks.Count - 1); i >= 0; i--)
                    {
                        if (sourceType.FieldLinks[targetType.FieldLinks[i ].Name] == null)
                        {
                            targetType.FieldLinks.Delete(targetType.FieldLinks[i ].Name);
                            update = true;
                        }
                    }
    
                    if (update)
                        targetType.Update(false);
                }
            }
        }
    }
    
    /// <summary>
    /// Syncs Sitecolumns based on a sourceweb and targetweb
    /// Updates a sitecolumn if it is different.
    /// Add's a sitecolumn if does not exist.
    /// </summary>
    /// <param name="sourceWeb"></param>
    /// <param name="targetWeb"></param>
    private static void SyncSiteColumns(SPWeb sourceWeb, SPWeb targetWeb)
    {
        foreach (SPField field in sourceWeb.Fields)
        {
            if (!targetWeb.Fields.ContainsField(field.Title))
            {
                targetWeb.Fields.AddFieldAsXml(field.SchemaXml);
            }
            else
            {
                SPField targetField = targetWeb.Fields.GetFieldByInternalName(field.InternalName);
    
                if (targetField.SchemaXml != field.SchemaXml)
                {
                    targetField.SchemaXml = field.SchemaXml;
                    targetField.Update(true);
                }
            }
        }
    
        targetWeb.Update();
    }
    
    /// <summary>
    /// Removes sitecolumns from the target-site which do not exist in the source-site
    /// </summary>
    /// <param name="sourceWeb"></param>
    /// <param name="targetWeb"></param>
    private static void DeleteSiteColumns(SPWeb sourceWeb, SPWeb targetWeb)
    {
        for (int i = (targetWeb.Fields.Count - 1); i >= 0; i--)
        {
            if (!sourceWeb.Fields.ContainsField(targetWeb.Fields[ i].Title))
            {
                targetWeb.Fields.Delete(targetWeb.Fields[i ].Title);
            }
        }
    }
  • Programmatically added Site Columns and Site Gallery issue

    If you add Site Columns to a site or web using code you can do it like this:

    SPSite root = new SPSite("http://wss/sites/site");
    SPWeb web = root.OpenWeb();
    string fieldname = web.Fields.Add("TEST", SPFieldType.Text, true);

    After adding a column this way my Site Gallery page (mngfield.asp) broke with the following error:

    Object reference not set to an instance of an object.   at Microsoft.SharePoint.ApplicationPages.FieldListRenderer.Render(HtmlTextWriter output)
       at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
       at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
       at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
     
    .....

    The cause of this is the case sensitivity when using the Object Model to open site objects. The Site Gallery page uses the FieldListRenderer control to display all Site Columns. In the Render method (by using Reflector) you'll find a piece like:

    SPWeb web = allWebs[field.Scope];
    string title = web.Title;

    In this case field is the actual Site Column and field.Scope stores the URL as it was once used to construct the parent web. In our code snippet it would contain "http://wss/sites/site". This value is used to get the web from allWebs, and this method turns out to be case sensitive...!! Idea

    In my case the site was created with the url: http://wss/sites/Site. So the allWebs method returns null and the web.Title causes an Object reference exception.

    By using the right casing everything was fixed in a second:

    SPSite root = new SPSite(http://wss/sites/Site);
    SPWeb web = root.OpenWeb();
    string fieldname = web.Fields.Add("TEST", SPFieldType.Text, true);

     

  • Automatically deploy the ServerAdminTemplates

    Installing the ServerAdminTemplates and creating sites is a lot easier and quicker by using these scripts:

    Star 1. Download the package from http://www.microsoft.com/downloads/details.aspx?familyid=AAE4CB5A-91D2-4F1B-9A45-3BB894E218F8&displaylang=en

    Star 2. Unpack it to C:\Temp

    Star 3. Create and run a .bat file in C:\Temp with this script to deploy the solutions (thanks to Adnan for this part):

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\AbsenceVacationSchedule.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "AbsenceVacationSchedule.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\ApplicationTemplateCore.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "ApplicationTemplateCore" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\BudgetingTrackingMultipleProjects.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "BudgetingTrackingMultipleProjects.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\BugDatabase.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "BugDatabase.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\CallCenter.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "CallCenter.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\ChangeRequest.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "ChangeRequest.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\ComplianceProcessSupport.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "ComplianceProcessSupport.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\ContactsManagement.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "ContactsManagement.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\DocumentLibraryReview.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "DocumentLibraryReview.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\EventPlanning.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "EventPlanning.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\ExpenseReimbursementApproval.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "ExpenseReimbursementApproval.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\HelpDesk.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "HelpDesk.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\InventoryTracking.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "InventoryTracking.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\ITTeamWorkspace.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "ITTeamWorkspace.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\JobRequisition.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "JobRequisition.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\KnowledgeBase.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "KnowledgeBase.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\LendingLibrary.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "LendingLibrary.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\PhysicalAssetTracking.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "PhysicalAssetTracking.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\ProjectTrackingWorkspace.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "ProjectTrackingWorkspace.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\RoomEquipmentReservations.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "RoomEquipmentReservations.wsp" -allowgacdeployment -immediate -force

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o addsolution -filename "C:\temp\SalesLeadPipeline.wsp"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o deploysolution -name "SalesLeadPipeline.wsp" -allowgacdeployment -immediate -force

    Star 4. Create and run a .bat file in C:\Temp with this script to create the sites. First replace the [[YOURVALUE]] parts:

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/AbsenceVacationSchedule -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate absence#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/BudgetingTrackingMultipleProjects -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate projmulti#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/BugDatabase -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate BT#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/CallCenter -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate callcenter#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/ChangeRequest -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate projchange#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/ComplianceProcessSupport -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate comproc#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/ContactsManagement -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate CM#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/DocumentLibraryReview -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate DR#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/EventPlanning -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate eventplan#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/ExpenseReimbursementApproval -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate exreports#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/HelpDesk -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate helpdesk#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/InventoryTracking -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate IT#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/ITTeamWorkspace -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate itteam#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/JobRequisition -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate JRIM#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/KnowledgeBase -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate KB#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/LendingLibrary -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate LL#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/PhysicalAssetTracking -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate PATM#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/ProjectTrackingWorkspace -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate projsing#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/RoomEquipmentReservations -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate RER#0 -description "[[YOUR DESCRIPTION]]"

    "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe" -o createsite -url http://[[SERVERNAME]]/sites/SalesLeadPipeline -owneremail [[OWNER-EMAIL]] -ownerlogin [[OWNER-LOGIN]] -secondarylogin [[SECONDARY-LOGIN]] -sitetemplate ST#0 -description "[[YOUR DESCRIPTION]]"

  • Indexed Columns on Lookup fields or Non-Supported fieldtypes

    Indexed columns are a great way to improve performance. Especially the SiteMapProviders (used by a CQWP) benefit from the indexes. I know there are some performance-tests out there, but just try to index every field you use in a Content Query Webpart and you'll know the difference. 

    It's a shame not every fieldtype can be indexed. In my experience only Text, Number, Date and Choice can be indexed. So what about Lookups, Calculated values or Custom fieldtypes?

    My workaround is to copy the values of those field types to a plain textfield and to index those textfields. This can easily be done by a workflow or an event handler:

    public override void HandleEvent(SPItemEventProperties properties)
    {
        if (properties.EventType == SPEventReceiverType.ItemUpdated || properties.EventType == SPEventReceiverType.ItemAdded)
        {
            SPListItem item = properties.ListItem;
    
            // Copy contents of the Lookup to a text field for indexing
            SPFieldLookupValueCollection values = item["LOOKUP"] as SPFieldLookupValueCollection;
            if (values != null)
            {
                foreach (SPFieldLookupValue value in values)
                {
                    item["LOOKUP_INDEXED"] += ", " + value.LookupValue;
                }
    
                // update the item without user/time-stamp 
                item.SystemUpdate(false);
            }
        }
    }
  • Programmatically Add/Remove Site Templates in the Site Gallery

    Took me a while to find out how to connect to the Site Gallery, so this might save you some time.

    using (SPSite root = new SPSite([yourRootURL]))
    {
        // DELETE OLD TEMPLATES
        List<string> toDelete = new List<string>();
    
        using (SPWeb web = root.OpenWeb())
        {
            SPFolder folder = web.Folders["_catalogs"];
            SPFolder subfolder = folder.SubFolders["wt"];
    
            using (SPWeb web = root.OpenWeb())
            {
                SPFolder folder = web.Folders["_catalogs"];
                SPFolder subfolder = folder.SubFolders["wt"];
    
                 int numberOfTemplates = subfolder.Files.Count;
        
                 for (int i = (numberOfTemplates - 1); i >= 0; i--)
                 {
                     subfolder.Files.Delete(subfolder.FilesIdea.Url);
                 }
            }
        }
    
        // SAVE SITE AS TEMPLATE
        using (SPWeb web = root.OpenWeb([yourSiteUrl]))
        {
            web.SaveAsTemplate("[Filename]", "[Title]", "[Description]", true/false);
        }
    }

    Credits to Serge for pointing me in the right direction: http://weblogs.asp.net/soever/archive/2006/06/28/NAnt-task-for-SharePoint_3A00_-Save-SPWeb-as-site-template-to-the-filesystem.aspx

  • Multi-valued (ARRAY) Comparisons only for Choice fields

    The new Multi-Value fields are causing me quite some headaches lately. Exporting to Excel does not work, showing in Content-Editor-Webparts does not work, SharePoint Designer does not understand them, and now there's an issue querying them trough the Search Engine…

       

    The SDK describes Multi-valued (ARRAY) Comparisons in Windows SharePoint Services Search SQL Syntax, but when using it it returns an error. The problem lies in SharePoint not recognizing an Multi-Value Lookup as Multi-valued.

    You can use the MetaData Property Mappings page to check whether an Site Column gets indexed as a Multi-Value column:

    I used an out-of-the-box environment with SP1. I've made a Content Type and added/created some Site Columns of different Multi-Value types:
    - Multi Choice field
    - Multiline text field (I know, this is just a blob, but I wanted to be sure..)
    - Multiline text field with unlimited length
    - Multi-Value Lookup field
    - Multi-Value Lookup field with unlimited length
    - Multi-Value People field

    Except for the Multi Choice field none of them gets recognized as a Multi-Value field. I even tried to create a piece of content based on the content type with Multiple selections made, before I let the Index server crawl the content and discover the new Site Columns. If it doesn't see it by then it never wil… So were stuck using LIKE operators, which will probably not do the performance any good.  

       

    Anyone with the same experience or a workaround?

  • MOSS Search: TotalAvailable & TotalRows not accurate

    A query against MOSS search engine returns a specified number of results and a indication of the total number of results. When working with the API this is the TotalRows property of the ResultsTable, working with the WebService this is the TotalAvailable element of the ResponsePacket.

    If you, like me, want to base your paging on the TotalRows or TotalAvailable properties and you expect it to return the exact number of results you're in for some strange behaviour...

    I do not have a nice solution yet, although the API also exposes the IsTotalRowsExact property. This will tell you when you're on the save side. The webservice ResponsePacket has not got an equalivant property.

    The MSDN documentation of the API does not mention this number not being exact: http://msdn2.microsoft.com/en-us/library/microsoft.office.server.search.query.resulttable_members.aspx

    The webservice documentation actually does mention it: http://msdn2.microsoft.com/en-us/library/aa193748(office.11).aspx#RangeType_TotalAvailable

    "The total number available. This number may not be 100% accurate and all these results may not be accessible."

    The number will become more accurate once you move to the end of the resultset.

    Be warned...

  • Showing attachments in a Content Query Webpart

    Not quite that complicated, but I'm it sure can save you (and myself next time) some time.

    To show the attachment paperclip in your CQWP, just add this section to the xsl:template in the ItemStyle.xsl:

    <xsl:choose>
        <xsl:when test="@Attachments='1' or msxsl:string-compare(string(@Attachments),'Yes','','i')=0 or msxsl:string-compare(string(@Attachments),'N0','','i')=0">
            <img src="_layouts/images/attach.gif" border="0" alt="Attachment"></img>
        </xsl:when>
        <xsl:otherwise>
            <!-- message no attachments attached -->
           </xsl:otherwise>
    </xsl:choose>

    Secondly, export the webpart and add the Attachment field to the commonviewfields:

    <property name="CommonViewFields" type="string">Attachments,Attachments;</property> 
  • Passed my first SharePoint MCP exam!!

    This morning I did the 70-630 exam: 'Microsoft Office SharePoint Server 2007, Configuring'.

    Although the specific training for this exam was cancelled last week I've passed! A lot of the questions are in fact pretty logical once you've got some Sharepoint experience. Everything's multiple choice, so determining the options that would definitely be wrong will leave you with a 50-50 change. You'll get 51 questions, ranging from Administrating MOSS 2007, to Excel Services and Search (this is not the complete list!). Just to know; there's no in-depth WSS knowledge needed.

    As preparation I've read Patrick Tisseghems book: Inside Microsoft® Office SharePoint® Server 2007.

    Beside that I've worked through the Transcender and Pass4sure training exams. This combination is more than enough to pass.

    Next exam will be Configuring WSS, after that the 2 development certifications are left.

  • Adding querystring parameters to a SPNavigationNode

    I've had a hard time figuring out how to programatically add or edit Querystring parameters to a SPNavigationNode. The SPNavigationNode is the internal type used to build the current (left) and global (top) WSS navigation.

    It seems pretty straight forward to pass the full url to the new SPNavigationNode constructor, but the querystring part gets removed by the SharePoint API.

    It turns out the Querystring part is saved in a separated propertybag.

    This examples retrieves an existing link and adds a Querysstring:

    SPNavigationNodeCollection nodes = PublishingWeb.GetPublishingWeb(spweb).CurrentNavigationNodes;
    node = nodes[4];
    
    // Remove the UrlQueryString property because updates are not persisted 
    if (node.Properties.ContainsKey("UrlQueryString"))
        node.Properties.Remove("UrlQueryString");
    
    node.Properties.Add("UrlQueryString", "param1=foo&param2=foo");
    node.Update();
    Note that you should not add the "?" at the beginning of the Querystring.
  • Error in ContentByQueryWebPart when calling from a non-web context

    Just run in some strange behaviour of the Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart, better known as the Content Query Webpart.

    I want to update a large number of these webparts, so I've made the following console app doing the work for me:

        foreach (SPFile file in [spweb].Files)
        {  
            using (SPLimitedWebPartManager wpMan = file.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared))
            {
    
                SPLimitedWebPartCollection wpColl = wpMan.WebParts;
                int numberOfWebparts = wpColl.Count;
    
                for (int i = 0; i < numberOfWebparts; i++)
                {
                    System.Web.UI.WebControls.WebParts.WebPart wp = (System.Web.UI.WebControls.WebParts.WebPart)wpCollIdea;
    
                    if (wp is Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart)
                    {
                        ((ContentByQueryWebPart)wp).ListGuid = [spweb].Lists[((ContentByQueryWebPart)wp).ListName].ID.ToString();
                        ((ContentByQueryWebPart)wp).WebUrl = [spweb].ServerRelativeUrl;
                        wpMan.SaveChanges(wp);
                    }
    
                    wp.Dispose();
                }
    
                wpMan.Web.Dispose(); // dispose manual because of a memoryleak in SPWebpartManager (http://blog.ofonesandzeros.com/2007/06/05/splimitedwebpartmanager-memory-leak)
            }
        }

    This code loops through all webparts on all pages, checks to see if the webpart is a ContentByQueryWebpart and updates some properties. This works, as long as you don't change the HeaderXslLink, ItemXslLink or MainXslLink in properties the .webpart file!

    As soon as you change one of those the webpartmanager will retrun a Microsoft.SharePoint.WebPartPages.ErrorWebPart. Why? Because of the following code is called by the specific properties of the ContentByQueryWebpart (actually its parent, the CmsDataFormWebpart):

    internal static string MakeServerRelativeUrl(string url)
    {
        return concatenateUrls(SPContext.GetContext(HttpContext.Current).Site.ServerRelativeUrl, url);
    }
    The webpart will always call the SPContext, but from a console application there is no web-context. Therefore when initiating the ContentByQueryWebpart, it will always thow an exception like:

    "An error occured while setting the value of this property: Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart:MainXslLink - Exception has been thrown by the target of an invocation."

    This can be seen as a design error, since all webparts should be able to at least expose themselves through a WebpartManager. Once the real rendering is done calls to the SPContext can be made.
  • Content Types: Title Column Rename Issue

    Once you've (by accident of course) changed the name of the title column, there seems to be no way back.

    You cannot rename the column back to Title because SharePoint tells you this is a reserved name for a column. There are some solutions on the web which require coding or database access. I found that by using the SharePoint Manager 2007 tool you can easily rename the column. No coding or SQL skills required.

    Just browse to your site collection, open the Site Columns, look for your renamed column and change the name property back to Title.

    Please note that you need to enable the 'full object model' mode:


  • Content deployment issue: Changes to "List View" Web Part are not retained

    This might be a very unusual case: we have one single teamsite which acts like a model for +20.000 other teamsites.

    Changes made to this teamsite are copied to the others by using the Import-Export functionality of the WSS API. Because we do not want the actual content to be copied we only copy certain files; to simplify, lets say we only copy the default.aspx page.

    The list and library structure is maintained through content types, every site has the same list and library structure. In the future new lists can be deployed with Content Deployment (import-export) or by script.

    After we run the import (so the default.aspx pages in all sites are updated) everything looks all right; except for the list-view webparts. Although they are nicely attached to the local lists of their new environment, the 'changes' made the the views and toolbars are lost.

    On one hand I can imagine this is a unsupported scenario; you cannot guarantee the list even exists in the target site.
    On the other hand this issue is very similar to the "Changes that you make to the 'List View' Web Part are not retained after you save the site as a template in Windows SharePoint Services 3.0" issue: http://support.microsoft.com/kb/926284

    I can't tell yet if this is really a bug. But I can tell you our workaround: use Content Query Webparts. This also has a big advantage of centrally managing the layout, which in this situation is a big plus.

     

  • The Enhanced Content Query Webpart

    Just some free advertisement of the very cool and totaly free Enhanced Content Query Webpart.

    It does exactly everything the standard CQWP does, but it adds some cool features like:
    - A toolbar for lists
    - A content menu for documents
    - Table layout
    - Direct editing of the most-used webpart properties directly in the toolpane
    - A code-layer which you can extent to your own needs.

  • Violation of PRIMARY KEY constraint 'PK__#ExportObjects____XXXXXXXX'

    Not a nice title, not a nice error as well...

    I encountered this error while trying export/import a file through the object model.

    Searching the net I found some posts suggesting this could be caused by the versioning in the Style Library. As soon as you've checked-in multiple major versions of a file the error 'could' occur. But that had to do with Incremental deployments.
    MS has a hotfix for that case: http://support.microsoft.com/kb/936867.

    But I was not doing any Incremental deployments, I just wanted to export one single file from a site. Also, I did not have the time to request, wait and install the Hotfix so I tried something else:
    Delete all content from the Style Library, and it worked.

    Just a cut-and-paste everything to your local machine, refresh the browser (just to be sure) and put everything back in place. Of course you will lose any version data, but thats a fair trade-off.

    The issue is also related to the Site Image gallery and other site-level-galleries, so you might want to try resetting them as well.

  • Copying Infopath VSTO projects

    Copying VSTO Infopath project can save you a lot of time, but it can also bring you a lot of problems.

    Follow these steps to avoid problems:

    • 1. Copy the project directory, change the name and add the new project to your solution
    • 2. Rename the project (from VS)
    • 3. Open the manifest.xsf in the XML Editor (Open with..)
    • 4. Update the name attribute in the xsf:xDocumentClass element
    • 5. Update the projectpath attribute in the xsf2:managedCode element
    • 6. Update the initialDocumentName attribute in the xsf:initialXmlDocument element
    • 7. Open template.xml and update the name attribute in the mso-infoPathSolution element
    • 8. Update the AssemblyProduct in AssemblyInfo.cs

      [Optional]
    • 9. [optional from here] Rename the assemblies and namespaces
    • 10. Update the Assembly name and the Default namespace in de project properties
    • 11. Update the Namespace in your code (note the partial classes!)
    • 12. Delete old dll's and debug files from your project
    • 13. Open manifest.xsf in de XML Editor (Open with..)
    • 14. Delete the xsf:file elements to the old dll en debug files

    • 15. Rebuild your project
  • Infopath VSTO post-build steps

    Also tired of running the publish form wizard every time? Especially when you are developing a larger number of forms this can be a frustrating and time consuming task.

    This is a description of the post-build steps for automatically building and deploying the forms:

    1. Copy the files to a temp dir (Visual Studio creates a lock on the files)
    xcopy "$(ProjectDir)Infopath Form Template\*" "c:\temp\$(ProjectName)\"

    2. Delete Myschema.xsx - this is not part of the form template
    del /Q "c:\temp\$(ProjectName)\*.xsx"

    3. Put the folder in a cab file (download the CAB tool here)
    cabtool "c:\temp\$(ProjectName)"

    4. Rename the CAB to XSN
    ren "c:\temp\$(ProjectName).cab" "$(ProjectName).xsn"

    5. Deactivate the previous edition of the form
    stsadm -o deactivateformtemplate -url http://yoursite -filename "c:\temp\$(ProjectName).xsn"

    6. Remove the previous version of the form
    stsadm -o removeformtemplate -filename "c:\temp\$(ProjectName).xsn"

    7. Kick the job service (saves you the wait)
    stsadm -o execadmsvcjobs

    8. Upload the new template
    stsadm -o uploadformtemplate -filename "c:\temp\$(ProjectName).xsn"

    9. Kick the job service (again)
    stsadm -o execadmsvcjobs

    10. Activate the new form template
    stsadm -o activateformtemplate -url http://yoursite -filename "c:\temp\$(ProjectName).xsn"

    11. Clean up the mesh 
    del /Q "c:\temp\$(ProjectName)"
    del /Q "c:\temp\$(ProjectName).xsn"
    rmdir "c:\temp\$(ProjectName)" /q /s

    This will result in:
    xcopy "$(ProjectDir)Infopath Form Template\*" "c:\temp\$(ProjectName)\"
    del /Q "c:\temp\$(ProjectName)\*.xsx"
    cabtool c:\temp\$(ProjectName)
    ren "c:\temp\$(ProjectName).cab" "$(ProjectName).xsn"
    stsadm -o deactivateformtemplate -url http://yoursite -filename "c:\temp\$(ProjectName).xsn"
    stsadm -o deactivateformtemplate -url http://theirsite -filename "c:\temp\$(ProjectName).xsn"
    stsadm -o removeformtemplate -filename "c:\temp\$(ProjectName).xsn"
    stsadm -o execadmsvcjobs
    stsadm -o uploadformtemplate -filename "c:\temp\$(ProjectName).xsn"
    stsadm -o execadmsvcjobs
    stsadm -o activateformtemplate -url http://yoursite -filename "c:\temp\$(ProjectName).xsn"
    stsadm -o activateformtemplate -url http://theirsite -filename "c:\temp\$(ProjectName).xsn"
    del /Q "c:\temp\$(ProjectName)"
    del /Q "c:\temp\$(ProjectName).xsn"
    rmdir "c:\temp\$(ProjectName)" /q /s

    Additional tip:
    Once you switch from a Debug build to the Release build you should:
    - manually remove the pdb files from your manifest file
    - remove the pdb files from your project directory

  • Avoiding performance issues in Infopath Forms Servers

    Recently I've been doing a lot with IFS. It seems like the ideal solution to create and present forms, far more capable than regular SharePoint list forms and far less complicated than coding forms in  Visual Studio.

    But there are some caveats to this beauty, which begin to appear just after you've just got yourself and your client in a good mood by finding all of your requirements covered by Infopath: Performance.

    We've ran into a number of problems after publishing some more 'complex' forms, these are some examples:
    - A repeating section does not persist more than 9 items
    - IE  crashes while filling out the form, totaly random
    - Rendering the form takes >60 seconds, taking 99% CPU and +600mb RAM
    - Uploading the form results in a error (both Central Admin and STSADM)
    - The design checker times-out, InfoPath cannot open the form

    Most of these problems occur at the browser. It's interesting to note that the server does not have any trouble serving the forms. Rendering the form in the browser is done fully dynamically. With complex and large forms this can lead to bad performance and functional issues. Uploading complex forms can take up to 30 minutes (depending on the time-outs you've defined in SharePoint).

    This is a list of best practices to keep you in control of performance. There's definitely not one golden solution, you should look for a balance throughout your form. 

     1. Use IE7 instead of IE6
    IE7 used a new scripting engine, which is far more efficient with resources. While rendering forms this could save you hundreds of mb's and up to 50% CPU. In practice forms will render up to 10x faster in IE7.

    2. Deploy the hotfix for conditional formatting issues (must have!)
    Conditional visibility logic has some known performance problems.  A hotfix is available http://support.microsoft.com/kb/937206, which is manifested in a modified 250K initial download (de core.js) when accessing Forms Services the first time.  The performance issue is related to the IE Script Engine and occurs at the browser level, not server-side.

    I've tested a form before and after installing the hotfix:

      IE6.0* IE7.0*
      Without hotfix With hotfix Without hotfix With hotfix

    Rendering

    65 sec