in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

Aaron Robertson-Hodders SharePoint Blog

  • Yet Another SharePoint Conference Post

    Yep, BandWagon.JumpOn();!

    I'm off to Seattle on Sunday for the SharePoint Conference.

    Hopefully I'll be able to get in the door at Kells and put some faces to names! I've never been there, but judging by the number of people interested, I hope they have a large place and a large supply of tasty bevies!

    If you're going up from down-under and willing to discuss how great the Blues look this season over a cold one, lemme know! Smile

  • 2008'ified

    Actually I've been 2008'ed for quite a while now, but only recently fully 2008'rtm'd!

    In a recent fit of over-exuberance (somewhat short of the Steve Ballmer opening keynote level mind you), I decided to embark on an upgrade journey.

    The plan was to upgrade the development environment for our Product's Server component (including SharePoint Solutions) to Visual Studio 2008, nuke my Vista Machine and re-birth it as a Windows Server 2008 development machine - no more VPC dev for me! Oh, and just for kicks - move our projects from Team Foundation Server 2005 to 2008...

    To be honest, I was kind of expecting something to go wrong so I backed up everything - the source, my development server (24Gb of VPC), my desktop and the VPC that TFS is running on.

    I need not have bothered really (although something would have happened if I didn't) because it took me nearly as long to do all the backing up as it did to do the upgrades!

    For Visual Studio 2005 -> 2008 I checked out the project, opened it in VS 2008, let the wizard run, ignored a few warnings and voila - upgraded! This is a pretty complex sort of solution, made up of a dozen projects, tens of thousands of lines of code, custom targets and build events etc, and so I was a little uneasy until we'd re-built and tested everything, but after some pretty thorough testing everything came through perfectly!

    The desktop upgrade was also very smooth. Well, it was really a re-build from scratch, but the Features concept in Windows Server 2008 makes things a little quicker, and once I got over the issue of having to slipstream SP1 MOSS with the help of this post I was done. In search of the best possible experience (and so I didn't get Vista envy), I also ran through the tweaks provided here. I noticed today that there are a few more in Part 2 which look interesting like adjusting the priority for foreground tasks, but I'm certainly not complaining. It performs very, very well - and it looks pretty. Smile

    TFS it turned out was the easiest of the lot! I was really careful with this one, not only did I back up the databases, but I also made a copy of the VPC - just in case. I did a bit of trawling through the net to check for any nasty surprises, and I'm sure that there are people for whom this has not been smooth sailing, but for me it was simply a matter of un-installing TFS 2005, without removing the database and installing 2008 - which prompted be to use the existing databases - and hey presto, 2008'ified!!

    In summary, I'm extremely happy with the performance of the whole environment, the speed at which I was able to get set up with the latest and greatest and the lack of gotchas that often accompany these sorts of exercises!

    [Feeling brave]And now lets migrate our Visual SourceSafe database...

     

     

  • Free SharePoint Business Data Catalog Column

    I have decided to release the custom BDC column that I created to the public domain. This custom column type provides a BDC Column type that can be created at the Site Column level and therefore used in Site Content Types. Out of the box this is not possible, and the ability to reuse BDC columns across sites and content has been identified as a common requirement. At this stage there are a number of things that are not yet implemented, including related fields, refreshing and nicer admin UI. However, I have created a Project on CodePlex so if you are interested in participating let me know! There is plenty of scope for features and function!

    The new column is included in a SharePoint Solution (.wsp package) and can be deployed to SharePoint via the standard solution deployment framework.

    Once installed a new column type is available to be chosen when creating columns in lists and when creating site columns:

    When the BDC field type is selected the user is presented with a custom UI which displays all BDC Applications (and their Entities) registered on the current Server’s Share Services Provider. The user also has the ability to set the fields that will be displayed in the Entity picker dialog when searching.

     Enough of the UI already, where's the code I hear you ask! ... 

    To get the data for the Application and Entity drop downs, we query the BDC metadata:

    //Declare the relevent references
    using Microsoft.Office.Server.ApplicationRegistry.Runtime;
    using Microsoft.Office.Server.ApplicationRegistry.MetadataModel;

    For the Applications…

    //Get the Line of Business Instances.
    NamedLobSystemInstanceDictionary sysInstances = ApplicationRegistry.GetLobSystemInstances();
    cboApplications.Items.Clear();

    //Loop through their names and add them to the drop down.
    foreach (String name in sysInstances.Keys)
    {
     cboApplications.Items.Add(name);
    }

    For the Entities, once an application is selected…

    LobSystemInstance currentInstance = sysInstances[cboApplications.SelectedValue];

    //Get the Entities for the given Application.
    NamedEntityDictionary currentEntities = currentInstance.GetEntities();

    foreach (String name in currentEntities.Keys)
    {
     cboEntities.Items.Add(name);
    }

    Once created the column appears as a Site Column, and is available for use in Content Types.

    When rendered in a list form, the field is very similar to the out of the box BDC column:

     

    The picker dialog used to find and select Entity items is almost identical to the standard SharePoint BDC Picker. This dialog is AJAX enabled so that if AJAX is enabled on the server the user will experience the AJAX feel, ie. no page refreshes for search and selection. The fall back if AJAX is not enabled is a standard aspx style form with full page refresh required for operations such as searching. There is a great blog entry from the Microsoft SharePoint Team (via Mike Ammerlaan’s blog on the subject) on Integrating ASP.Net AJAX with SharePoint that you should read if you want AJAX to be available on you SharePoint server. If you install your AJAX enabled solution on a server where this process is not complete, it should still function in the standard ASP.Net (ie. without AJAX) way. In theory, now that SP1 is out this is supported too, although I'm not sure that configuring it is any easier...

    The Pop Up is called via a JavaScript function on the book image above. This function build a URL based on the Application, Entity and Display Fields properties of the column. To save time and make the popup consistent with the standard UI, it is based on the pickerdialog.master Master page.

    The fields displayed in the list view are set when the column is defined, but can be modified once the column is created by editing the settings of the new column.

     
    We use the following code to get the list of Finders and populate the dropdown of fields to search on. Finders are defined in the XML definition of the Application and control the way data is retrieved from the external source in relation to searching.

    NamedLobSystemInstanceDictionary sysInstances = ApplicationRegistry.GetLobSystemInstances();

    LobSystemInstance myIns = sysInstances[Request[“BDCApplication”]];

    Entity myEntity = myIns.GetEntities()[Request[“BDCEntity”]];

    FilterCollection fc = myEntity.GetFinderFilters();

    The GridView containing the search results is contained within an AJAX UpdatePanel so that operations that would normally cause postback (and hence full page refreshes) are handled in the background. Inside the CreateChildControls we set up the AJAX UpdatePanel, GridView and Trigger Controls.

    dvUpdatePanel = new System.Web.UI.UpdatePanel();

    ajaxScriptManager = new System.Web.UI.ScriptManager();
    ajaxScriptManager.EnablePartialRendering = true;
    Controls.Add(ajaxScriptManager);

    //Fixup the UpdatePanel. This function is defined in the blog from SharePoint Team on AJAX and SharePoint.
    EnsureUpdatePanelFixups();

    //Create a conditional Update Panel and make sure the Children of the Panel cause updates.
    dvUpdatePanel.UpdateMode = System.Web.UI.UpdatePanelUpdateMode.Conditional;
    dvUpdatePanel.ChildrenAsTriggers = true;
    dvUpdatePanel.ContentTemplateContainer.Controls.Add(gvResults);

    //Create a postback trigger on the search button to handle the search.
    System.Web.UI.AsyncPostBackTrigger uptTrigger=new System.Web.UI.AsyncPostBackTrigger();
    uptTrigger.ControlID = btnGoSearch.UniqueID;
    uptTrigger.EventName = "Click";
    dvUpdatePanel.Triggers.Add(uptTrigger);

    //Set up a new SharePoint Grid View and turn off Auto Generate so we can control the columns.
    gvResults = new SPGridView();
    gvResults.ID="gvResults";
    gvResults.AutoGenerateColumns = false;

     

    Searching for Entities to show in the GridView is handled using the objects and methods provided by the SharePoint object model:

    NamedLobSystemInstanceDictionary sysInstances = ApplicationRegistry.GetLobSystemInstances();
    LobSystemInstance myIns = sysInstances[Request[“BDCApplication”]];
    Entity myEntity = myIns.GetEntities()[Request[“BDCEntity”]];

    //Build up a finder collection which is the query for the BDC source.
    FilterCollection fc = myEntity.GetFinderFilters();

    for (int iCounter = 0; iCounter < fc.Count; iCounter++)
          {
           if (fc[iCounter].Name == sFinderName || sFinderName == "")
                {
                 switch (fc[iCounter].GetType().FullName)
                      {
                       case "Microsoft.Office.Server.ApplicationRegistry.Runtime.WildcardFilter":
                             ((WildcardFilter)fc[iCounter]).SystemIndependentValue = "*" + sQueryString + "*";
        break;
       case "Microsoft.Office.Server.ApplicationRegistry.Runtime.ComparisonFilter":
                             ((ComparisonFilter)fc[iCounter]).Value = Convert.ChangeType(sQueryString, ((ComparisonFilter)fc[iCounter]).GetFilterValueType());
                                   break;
       case "Microsoft.Office.Server.ApplicationRegistry.Runtime.LimitFilter":
                             ((LimitFilter)fc[iCounter]).Value = Math.Min(0xc9, ((LimitFilter)fc[iCounter]).MaximumValue);
                                   break;
         }
          }
     }

    IEntityInstanceEnumerator prodEntityInstanceEnumerator = myEntity.FindFiltered(fc, myIns);

    //Create a data table to store the results.
    DataTable Results = new DataTable("BDCData");
    Resultset.Tables.Add(Results);

    //Get the collection of IDs and create an array to store their names.
    IdentifierCollection EntIDS = myEntity.GetIdentifiers();
    string[] sIDNames = new string[EntIDS.Count];

    //Get the names.
    int nCounter = 0;

    foreach (Identifier currentID in EntIDS)
    {
     sIDNames[nCounter] = currentID.Name;
     nCounter++;
    }

    //Add columns to the results data table named as per the Entities fields.
    //Check if there any of the fields have show in picker set so we know to display only those fields.
    Boolean blnShowInPickers = false;
    foreach (Field f in myEntity.GetFinderView().Fields)
     {
            if (f.TypeDescriptor.GetProperties().ContainsKey("ShowInPicker"))
                  {
                   if (((Boolean)f.TypeDescriptor.GetProperties()["ShowInPicker"]))
                   {
                         blnShowInPickers = true;
        }
       }
     }

    DataColumn currentColumn = null;
    foreach (Field f in myEntity.GetFinderView().Fields)
          {
     //Add the default display name to the caption if there is one so we can pull it out when we create the list.
           if (f.DefaultDisplayName == "")
                {
                 currentColumn = Results.Columns.Add(f.Name);
         }
                else
                {
                 currentColumn = Results.Columns.Add(f.Name);
                    currentColumn.Caption = f.DefaultDisplayName;
         }

                if (blnShowInPickers)
                {
      //Only show fields that have show in picker set (if any do).
                 if (f.Name != sTitle)
                      {
                       if (f.TypeDescriptor.GetProperties().ContainsKey("ShowInPicker"))
                            {
                             if (((Boolean)f.TypeDescriptor.GetProperties()["ShowInPicker"]))
                                  {
                                   currentColumn.ExtendedProperties.Add("ShowInPicker", "TRUE");
             }
             else
                                  {
                                   currentColumn.ExtendedProperties.Add("ShowInPicker", "FALSE");
             }
       }
        }
                      else
                      {
                       currentColumn.ExtendedProperties.Add("ShowInPicker", "TRUE");
        }
         }
                else
                {
      //Else if the field is an identifier or it’s the title field then show it.
                 if (f.TypeDescriptor.ContainsIdentifier || f.Name == sTitle)
                      {
                       currentColumn.ExtendedProperties.Add("ShowInPicker", "TRUE");
        }
                      else
                      {
                       currentColumn.ExtendedProperties.Add("ShowInPicker", "FALSE");
          }
          }
     }

    //Loop through the found Entity instances and add them to the results.
    while (prodEntityInstanceEnumerator.MoveNext())
    {
     try
     {
      IEntityInstance IE = prodEntityInstanceEnumerator.Current;
      DataRow newResultRow = Results.NewRow();
      System.Collections.IList MyList = (System.Collections.IList)IE.GetIdentifierValues();

      //Create an array to hold the identifiers.
      object[] oIdentitiers = new object[sIDNames.Length];

      for (int iCounter = 0; iCounter < MyList.Count; iCounter++)
      {
       oIdentitiers[iCounter] = MyList[iCounter].ToString();
      }

      foreach (Field f in myEntity.GetFinderView().Fields)
      {
       if (IE[f] != null)
       {
        newResultRow[f.Name] = IE[f];
       }
      }

      Results.Rows.Add(newResultRow);
     }

     catch (Exception rowex)
     {
     }
    }

    //Grab the Display Fields and add columns for them in the Grid View.
    gvResults.DataSource = Resultset;
    string[] keys = Request["DisplayFields"].ToUpper().Split(',');
    gvResults.DataKeyNames = keys;
    gvResults.Columns.Clear();

    foreach (DataColumn currentCol in ((DataTable)gvResults.DataSource).Columns)
    {
            string[] displayFields = Request["DisplayFields"].ToUpper().Split(',');
            if (Array.BinarySearch(displayFields,currentCol.ColumnName.ToString().ToUpper())>=0)
      
     {
                    BoundField newfield=new BoundField();
                    newfield.HeaderText=currentCol.ColumnName.ToString();
                    newfield.DataField=currentCol.ColumnName.ToString();
             gvResults.Columns.Add(newfield);
     }
    }

    //Bind the results to the grid.
    gvResults.DataBind();

    Feel free to wander over to CodePlex and grab a copy if this is a requirement for you or your clients and feel free to offer any suggestions you think of!

    I'm off for a month with the Kids and the Family, so I wish you all a great Xmas, Holiday, or work time!

     

     


     

  • Best Practice : Using disposable objects in SharePoint

    I found this article on MSDN via Andrew's post posing the question "So is it best practice to only use C# for SharePoint development?". A good question and one that will have plenty of comment from all I imagine. For what it's worth (although not the point of this post), I'm a fan of C# because I like using (SPSite currentSite=new SPSite(http://Server)) ...

    The interesting thing I took from the MSDN article was that objects that are accessed via SPSite objects, like RootWeb and ParentWeb need to be explicitly disposed of when you're finished with them. I had not appreciated this. I figured that if you 'used' using on the SPSite object then that would dispose all the members etc of that object. Not so say our MS friends! Smile

    This means:

    String str;
    using(SPSite oSPSite = new SPSite("http://server"))
    {
       str = oSPSite.RootWeb.Title;
       str = oSPSite.RootWeb.Url;

       ... additional processing on RootWeb ...

    oSPSite.RootWeb.Dispose();
    }

    And, in the interest of fairness: 

    Dim str as string
    Dim oSPSite as SPSite

    oSPSite=new SPSite(http://server)

    str = oSPSite.RootWeb.Title
    str = oSPSite.RootWeb.Url

    ... additional processing on RootWeb ...

    oSPSite.RootWeb.Dispose()
    oSPSite.Dispose()

    Not really that difficult, but it will make a difference to your memory usage, have positive performance benefits and keep the "Potentially excessive number of SPRequest objects (11) currently unreleased on thread" error messages out of the already huge logs!

    Regardless of you language of choice, this article is definitely worth a read if you are coding for SharePoint!

     

  • Addendum to the SharePoint Deployment Daylight Savings patch issue

    There has been much written about the issue (in particular Joel's Q&A) with Deploying Solutions on SharePoint 2007 after Daylight Savings started (and ended depending on where you are) this year and I have run into it several times. Most recently when I attempted to deploy and upgrade to a solution to a server I thought had the patch applied, but it had not.

    So I kicked off the upgrade, saw the job sitting in deploying (scheduled...) status, banged my head and installed the patch, rebooted, re-ran the Configuration Wizard (and lamented the time it took on setp 8!).

    However, after all that my job still said deploying and clicking on the Solution in Solution Management did not display the usual Cancel Deployment option. 

    Sooo.... stsadm time!

    To cancel the deployment:

    stsadm -o canceldeployment -id ...

    Of course I assumed that the ID parameter was the ID of the Solution - it's not. It's the ID of the deployment job. Incidentally, running stsadm -o enumsolutions gives you a nice list of the Solutions installed and their ID's.

    stsadm -o enumdeployments gives you a list of the current deployment jobs and their ID's.

    Plug the ID of the deployment job into the canceldeployment operation above and voila - cancelled deployment!

    TechNet has some documentation of the operations available via stsadm. Interestingly canceldeployment is not on the list!

    Mind you I much prefer to figure these things out on the fly and under pressure, don't you? Smile

     

     

  • And now, auto-create Manifest XML

    About a day after I posted about Rich Finn's auto-create DDF tool, he completed the story with the Manifest generation!

    That's pretty fast turnaround!

    Of course I still haven't managed to make time to try it out, but I will certainly try next week.

    Although, I wonder if I sent him my Dev VPC image if he would install it for me... Smile

     

  • Auto-create DDF for SharePoint Solutions

    A long time ago I said "One day I'd like to automate the generation of the DDF file, perhaps via a macro or XSLT".

    Well, I never did, but Rich Finn did. Good job Rich, can't wait for the Manifest creation bits!

    Now if I just had time to install this...

     

  • A great resource for MOSS Search - SharePointSearch.com

    If you're looking to get info about MOSS Search then SharePointSearch.com is a good place to start.

    The guys over there are doing a great job of information about aggregating news, information about related products and commentary from a range of excellent sources across the industry. There is a lot about MOSS, but also link and articles about Enterprise Search in general.

    If you have an interest in Search then you should check it out.

    Features include:

  • Vendor and product pages and reviews
  • Vendor discussion forums
  • Weblog spaces hosted by SharePoint and Enterprise Search experts
  • Upcoming Enterprise Search events
  • References to the top Search sites, Search blogs and MS links
  • Current articles relevant to Enterprise Search for SharePoint
  • Enterprise Search Solution Builder
  • Consultant list and reviews
  • Research Center for SharePoint Portal, SharePoint Search, Enterprise Search, Knowledge Management and Business Data Catalog 
  •  

  • Enterprise Search - SharePoint Search vs Google

    There has been plenty written about the Google Enterprise Search products and their possible competition with Microsoft SharePoint Search technologies and I recently had the need to advise someone on which would suit them best.

    Obviously, given this is a SharePoint Blog and I'm pro SharePoint this post is not going to be particularly un-biased, but here goes...

    I think there are two questions that need answers up front:

    Do you already have licenses for MOSS?

    If the organisation in question already has licenses for MOSS via Software Assurance, Open License etc then it's a bit of a no-brainer because they get MOSS Search functionality for free!

    Is it really only about Search?

    Especially relevant if they already have some version of SharePoint, because they are already getting value out of the collaboration and content management features so going MOSS is probably a natural progression anyway. If they don't have SharePoint, then consider this - it's great to be able to search for stuff, but if the users don't attach relevant meta-data to the 'stuff' then the search becomes a full-text kind of thing (although SharePoint does a pretty good job of this anyway, see below). So getting people to put their documents into SharePoint and enforcing meta-data capture at the same time is only going to improve the relevency of results! All that on top of the fact that you get the collaboration, workflow, content management OOB too!

    Now, if they really only want Search and you can't let the price speak for itself and lets be honest, there are going to be situations where it's a tough sell because the pricing models/number of documents/hardware costs/support can make it complex, what can you say? Here's a bit of a start on that conversation, with the usual caveat - these points were compiled by me after some research and discussions with 'they shall not be named, but know their stuff, and know who they are'!

    Relevance

    SharePoint relevance has had a big overhaul since Portal Server 2003. There have been all kinds of work done on making sure that the most relevant results appear at the top of your search results and there are also things you can do to tune this relevance - a whole posts worth really ;-)

    Although Google other offerings might be excellent at indexing and searching web content how well does this translate in the organisation? Bear in mind that things like linked documents, page rankings and referrals don't really translate to your latest PowerPoint presentation or HR's policy and procedures manual in Word. The fact of the matter is that most of the things you are interested in finding in your organisation are files that Microsoft knows pretty well. So if you're looking for someone who knows how to find the most relevant documents in the Enterprise you can be pretty sure it is Microsoft - and you can be sure they will be making this a priority.

    The SharePoint indexer does a lot of cool stuff when it crawls files like looking at the document properties and headings in document to improve their ranking. For example, a document on Policies will have a higher ranking if the Subject, Keywords and Category properties are filled in and the content of the document contains Headings with relevant words or phrases in them. I belive the short answer is that the SharePoint algorithms for relevance are just better suited to the types of content you find in the average organisation.

    Document Types

    While Google and others do support a huge range of document types, it's not clear to me that they have such a vibrant third party network providing indexer plugins to support custom file formats. There are plenty of file formats supported out of the box for SharePoint and any number available from places such as the iFilterShop. And, if you can't find one and you like writing C code, you can write your own - or get someone to do it somewhere very cheaply!

    People Search

    This may not be relevant for small organisation, but for large or geographically diverse organisations, could be the tipping point. Imagine searching for somethig like, hmmm, SharePoint Search and getting not only whitepapers and blog entries but also the details of people in your organisation who are experts in the subject. And, assuming you have presence enabled (and ideally of course Live Communication Server) clicking on their name and initiating an IM chat! This functionality is built into MOSS and will only be getting better and richer in the coming years.

    Structured Data

    There is a lot of information hidden away in back end systems that is relevant when searching too. While it is useful to be able to search documentation about your products, it's also useful to be able to return details about pricing and other structured data without having to re-publish it into 'searchable content', such as web pages. Using MOSS and the BDC you can search directly into any backend data source that can be accessed via a connection string or web service and return data as part of search results.

    Security

    This one is a bit unclear to me at present, because there was a time when certain non SharePoint searches did not have a good story to tell here. It was necessary if you had complex (meaning pretty much any) security you needed to configure multiple search frontends and content sources to cope. I'm not so sure this is the case any more as I have recently seen advertising suggesting that document level security is now supported in these products although the FAQ on this subject is a little unclear...

    Customization

    I'm certainly not an expert on customization on any platform other then SharePoint, so I can only speak from experience there, but the customization story is VERY good. Custom UI using Web Parts (which I do know quite a lot about!), custom results using XSL, custom query using additional properties, custom tabs for different types of search or scoping. The major benefit is that because Search is an integrated part of SharePoint as a whole, it can be leveraged for specific applications or requirements, for example, a custom search web part for a specific type of content or user process. And if you don't like the Advanced Search in SharePoint, you can simple write your own and remove the standard OOB web part. You can create custom advanced searching in Google, but it requires a new search web page, rather than modification/removal of the OOB page.

    Depth

    SharePoint Search is not a shallow one product story, it is based on the same technologies that Desktop Search, SQL Search and Live Search are based on. Its one that spans multiple levels of content and can be accessed from multiple points, ie. Internet Explorer search, Desktop Search, Office etc... 

    Future

    It's pretty clear that neither of the big players in the Enterprise Search space are going to be slacking off any time soon, which can only be good for everyone - competition and all that! However, given that there are some very nice things in the pipeline around SharePoint Search and Microsoft is committed to this space (and we all know how determined they are when they want to be!), SharePoint is the place to be.

    The biggest things that I believe Google does have is a very strong brand in the Search space. On top of that they have done a great job of marketing their offerings as a very simple 'drop in and go' solution to Enterprise Search.

    However, when you look closely at the the fact that SharePoint provides the Search functionality (and superior relevancy for Enterprise content) plus all the other OOB functionality and what is actually required to make the Google products perform well in an organisation such as configuring security, UI and integration with an organisations environment, it's not so simple. Its sort of like making a cake from pre-packaged cake mix because it seemed easier and then finding that the one your friend made from scratch took the same amount of time, cost the same, but is in a cool shape and has icing! Smile 

    According to IDC (Content Technologies Study 2004-2005), workers spend 9.5 hours per week searching. That's a lot of time and money right there and really relegates the question of cost firmly into the 'How can you afford not to?' arena!

    I'm sure that there are plenty of people out there who have strong (and probably differing opinions) about all this and I'd be happy to hear any comments people have to make - especially any Google experts who are willing to admit they subscribe to SharePoint Blogs! Smile 

    Note - My opinions do not reflect those of my blog host, SharePoint Experts.

  • Partner Conference Day -1

    After a landing I'd rather forget, which might actually be a good one in Denver (lucky I recently learned that modern passenger jets are designed to land at up a to 45 degree angle to the runway!), we arrived in Denver last night (depending on where you are). Today was pre-partner-conference-event-day (I'm pretty sure there is a catchy acroynom, I'm just not sure what it is!).

    Today was a day of meetings and MS Partner 101 - I think that means I am officially assimilated! Smile

    We had a good meeting with several key members of the Microsoft Information Worker Partner Team, which was great - very synergistic (if that is a real word) with what our philosophy around SharePoint and Office is. And then a fairly indepth look at what the value that Microsoft adds in terms of an organisation to be partnered with - there were actually some surprising results here in terms of rankings in market share and growth. After which in the midst of several thousand Partner (approx 7,000 I think) we ran into Bjarne. About 48 hours of travel between us - but only 6 degrees of separation.

    Then, after a rest (read: email check) it was off to the welcome reception, shortly followed by the Australian Welcome reception and drinks and a free (possible hijacked) Limo ride around the sights of Denver!

    Looking forward to the sessions tomorrow, especially the one on MOSS Search...

     

  • The Dark Side? MOSS on a MacBook Pro!

    I may as well admit it, I have been seduced by the power of the Dark Side (that should generate the 5,911 Google hit for that phrase and maybe knock Google off the top few spots in Live Search! Smile).

    After much research I decided on one of the new MacBook Pro's as my new laptop for work. I've never been a fan partly because of the proprietory (near predatory) nature of Apple, and partly because of the price. However, in my research I discovered that the new ones are fast (mine is Core 2 Duo 2.4Ghz, 4GB RAM and 160GB Hard Disk), light (about .5 kgs lighter than similar spec'd notebooks), pretty standard (SATA drives, DDR2 RAM), can run any OS you want (using Bootcamp, Virtualized with VMWare Fusion or Parallels).

    On to the SharePoint bit - I installed bootcamp on MAC OS, created the drivers disk, ran the Windows install wizard, put in the Vista DVD, 10-15 minutes later put in the Bootcamp drivers disk (created at step 2) and voila VISTA on a Mac, complete with all peripherals (including the iSight camera which surprised me!). I won't go into details, but being sad about the fact that 32-bit Vista would only see 3GB of my 4GB, I tried 64-bit Vista which was NOT so good - drivers didn't all work. I suspect/hope that the final release of Bootcamp might fix that tho...

    Next step, installed Virtual Server 2005, copied over my images and then started them up (note the use of 'them'). I ran 2 MOSS servers (one domain controller with AD) AND played a little Halo 2 (eval) at 1440x900 just to see if the machine was up to it - it was.

    So, I'm off the the Microsoft Worldwide Partner Conference next week in Denver complete with shiny new Mac!

    I hope they let me in (I'll take some kiddy stickers to cover the logo just in case - although it'll take more than that to disguise it)!

    Oh, and just in case this feels in any way like a review (It's not meant to!) - My opinions do not reflect those of my blog host, SharePoint Experts.

     

  • Microsoft Office SharePoint Server's Most Innovative Search Solution

    In the middle of all the excitement of the APAC SharePoint Conference, Breakfast Briefings, Partner Conference Prep and product releases, it was announced in June that we at MacroView have won the Quickstart for Microsoft Search Innovative Solution Contest!! This contest was open at all Partners who signed up to the Quickstart for Search program and had Innovative Search solutions developed for the Portal Server or MOSS environments.

    Way back in February I posted about some work I had been doing around a customized search UI. This was based on a generic, customizable Web Part for rendering user friendly and consistant UI for the user when they search using MOSS. This customizable Search solution is what we submitted as our entry.

    Congratulations also to the other Australian Partners (OBS and Unique World) who made the 13 Finalists. It says a lot about the quality of Partner - and competition Smile - in Australia that there was such a representation from down under amongst some major players from around the world!

    I'm pretty excited about our win, and also about the awesome customization features within MOSS and the Search area in particular. If you're deploying MOSS and looking for ways to improve the OOB Search feel free to drop me a line, or even better, if you are going to the Worldwide Partner Conference look me up in WPC Connect!

     

  • The solution to saving properties in Custom Field Types!

    I have finally managed to put Anton's code to the test in my own custom field type with great results.

    The underlying issue is that the Update method does not seem to get called when adding a new field, and because there are a number of initializations of the field object the values in the custom propery editor controls get lost before you get to the OnAdded event where you could set them!

    So, Anton's plan (which I confirm works very well) is to store the values of the properties in a static string dictionary, keyed with the hash of the current SharePoint context SPContext.Current.GetHashCode() so we get the right property back out in the OnAdded event and can set the correct values.

    You can get the full code in the thread linked to above, but I'll summarize as best I can. I'm using a property called Application that is set by selecting a value from a combo box - which is actually part of a parent child combination used in the Property Editor, but that's another story...

    First we create a new dictionary in the Field Class (the one that inherits from the SPField... class) to store the properties:

    private static Dictionary<int, string> updatedApplicationProperty = new Dictionary<int, string>(); 

    Create a new method that the Property Editor Control can call to update the dictionary to the value in the property editor control:

            public void UpdateApplicationProperty(string value)
            {
                updatedApplicationProperty[ContextId] = value;
            } 

    Then, in the get for the property, return the dictionary stored property value if it exists or the internal value if not:

    public string Application

    get

    {

                    if (updatedApplicationProperty.ContainsKey(ContextId))
                    {

                        //If we saved a value away and we're in the middle of the save field process, then pull it out.
                        return  updatedApplicationProperty[ContextId];
                    }
                    else
                    {

                        //Return the existing value for the application property. This will be the case if we already have a field.
                        return sApplication;
                    }

    }

    In the Update method of the Field Control set the custom property value and remove the stored property value:

    this.SetCustomProperty("Application", this.Application);

    if (updatedApplicationProperty.ContainsKey(ContextId))
         updatedApplicationProperty.Remove(ContextId);

    And, lastly, in the OnSaveChange event in the class for the Property Editor Control store the value of the control that represents the property into the dictionary via the Update...Property method for a new field, or set the property as normal if the field already exists:

                    BDCField currentBDCField = field as BDCField;

                    if (isNewField)
                    {
                        currentBDCField.UpdateApplicationProperty(this.cboApplications.SelectedValue);
                    }
                    else
                    {
                        currentBDCField.Application = this.cboApplications.SelectedValue;
                    }

    Hopefully that's a reasonable description of the required steps. Anton has a good description of the why he chose the static dictionary in the forum post.

     Thanks again Anton and sorry it took me sooooo long to try it out!!!

  • Adding additional properties to the MOSS People Search

    I figured out how to add additional fields from the Profile Import into the People Search today.

    This requirement occured to me after discussing how this might be done with Aaron Saikovshi at the APAC SharePoint Conference.

    In this particular case, I wanted to add one of the extended properties from the Exchange Users into both the Search Box control and the results page. I suspect, but have not confirmed that this process would work for extensions to the AD Schema too.

    There are several steps to make this happen:

    1. Assuming that you have a property with data in it, go to the Shared Services Administration page and click on 'User Profiles and Properties' link.

    2. Add a new Profile Property for the property you want to include by clicking the 'Add profile property' link at the bottom of the page. The things to pay attention to are the policy settings (properties will need to be available to all those who need to see them!) and the Property Import Mapping (you need to map your property to a physical property crawled by the indexer - just like Managed Properties in MOSS Search - which is actually what this property becomes). In the case of my Exchange extended property this was extensionAttribute1.

    3.  At this point I did a full import, just to make sure I could see the values coming through when I viewed the user profiles.

    [Ishai has a nice post with pictures for the remaining steps...]

    4. Go to the people search page in the Search Centre (or wherever you put the people search).  Edit the page and then edit the properties of the People Search Box. Open the Properties property and add a new Property element to the list, eg:

    <Property Name="NewProp" ManagedName="NewProp" ProfileURI="urn:schemas-microsoft-com:sharepoint:portal:profile:NewProp"/>

    where NewProp is the name you gave to the new Profile Property above.

    5. In the Results Query Options section of the properties for the People Search Core Results web part, edit the Selected Columns property. If your default value is like mine, it will look like there is nothing in there - which tricked me for a while, but click the ... button and add a new Column element for your property equal to the name of your new Profile Property.

    6. Edit the Data View Properties of the People Search Core Results web part to add the new column (in lower case), somewhere in the XSL eg. <xsl:value-of select="newprop"/>. Click Apply and Publish (if you have content management turned on). Don't worry if you get a nasty looking error in the web part unless it happens after you publish! You did backup the XSL before you modified it, right?!

    I also had to do some crawling of the Content Sources once I had added the new Property and at one point had to reset and re-crawl. I think there is something strange going on there, because the first property I added only seemed to require a Crawl to be available, but the second would not work as a Selected Column until I reset and re-crawled! I'm not 100% on what requires what level of crawling. I can understand a full crawl being required for new properties because that probably updates the property store, but a Reset Crawled Content?!

    If anyone has, or tries to get this working with a custom Active Directory column it would be great to know and complete the puzzle!

     

  • SharePoint Farm, Virtual Server and Network Load Balancing

    I had reason this week to test our WISDOM DMF products operation in a Load Balanced SharePoint environment.

    The outcome was very successful and surprising easy to achieve, especially given my previous level or Load Balancing - which was VERY limited, but there were a few thing I learnt that I thought the story was interesting enough to share...

    Because I didn’t have two physical machines that I felt like re-purposing for a bit of testing I created two fresh Windows 2003 Server Virtual Server machines, called mvwssloadbalanced1 and mvwssloadbalanced2 on our Virtual Server machine.

     

    I configured two Virtual Networks on the servers which I believe is required to use the Unicast Cluster Operation Mode as opposed to Multicast which can be used with one Network Interface.

     

    It really amounts to the same outcome, just different physical operation (which you can google if you’ve a mind) but I wanted to replicate a certain configuration. In reality I actually tested both Unicast and Multicast configuration with the same results.

     

    Once I had Windows installed I installed SharePoint Services on one of the machines and created a new Farm. Then I extended a SharePoint web application onto the Default IIS web site (root) and created a root site collection on it. When extending the web application there is an option to set the load balanced URL which you need to set (in our case http://mvcluster.macroview.com.au) because by default it uses the machine URL - which I imagine will not work because there will not be any alternate access mapping (see the previous Blog entry on AAM’s) created for the load balanced URL. 

     

    Next I installed WSS onto the second machine using the default settings - which was a mistake. The defaults do not install the Central Admin web site onto additional servers in the Farm. I fell for this one, because whenever I’ve come to the last screen of the SharePoint Products and Technologies Configuration Wizard which has the Advanced button on it I’ve ignored it (because it didn't contain anything interesting)! So, click the button and check the box that installs the central admin site on the additional servers in the farm.

     

    This might not be an issue for some (perhaps your) situations, but for us, we have an admin operation that we have added to the Operations tab of Central Admin that needs access to the physical server. If you don’t have Central Admin installed on all of the servers then you’ll always end up physically on the first server in the farm.

     

    In practice, adding the Central Admin Console to all the servers (or at least more than 1) is not a bad idea, because if you don’t put it on other servers, then it would'nt be Load Balanced and if the primary server dies, you won’t be able to get there! Smile

     

    So then I installed our application, which consists of Features, Pages, Web Services etc and is packaged in a SharePoint Solution. This was very cool, because the installation on the Farm deployment was exactly the same as single server:

     

    stsadm –o addsolution –filename …

     

    Then go to Manage Solutions in the Operations tab of Central Admin and click Deploy! Viola – solution deployed to all servers in the Farm with a single click.

     

    Our client application connects to a custom web service in _vti_bin to communicate with SharePoint and so pointing it at the Load Balanced URL worked fine.

    Shutting down one of the servers in the cluster from the NLB console resulted in the other server processing the requests as expected. Of course if the server was shutdown in the middle of a call (which you can actually avoid by using a shutdown option to drain all current requests), or just as you made the call some errors were seen, but once the general result was fine.

     

    Note that this result is dependent to some degree on configuration in that it is possible to set Affinity on the cluster such that a client’s requests are serviced by the same server each time and if that actual server goes down or is shut down using the NLB console, then future requests will fail. However, I believe that Affinity set to none is best for performance and fine when you don’t need to manage state.

     

    When I shut down a web site or application pool using the IIS console I did get intermittent connection and usage errors caused by failure to connect to the web service. Shutting down services and pools like this causes symptoms similar to a failure that might happen if the web application causes too many errors (say for example there is some faulty memory or something, but not catastrophic hardware failure) and is shut down by IIS. It should be noted that this behaviour is consistent with SharePoint and SharePoint Web Services in general, in that you get intermittent browser connection failures in this situation.

     

    This is because NLB does not provide failover for applications or servers only for failure of the server itself - meaning it will not poll a machine to check that a particular service or facility is available and drop the box from the NLB cluster if it does not respond. Therefore requests are still sent to the stopped web site/application pool as they would normally unless a monitoring application is configured to remove servers from the cluster under certain circumstances. NLB provides the services required by monitoring applications to remove servers from the cluster remotely. For example a monitor could be set up to check that a certain web site or application pool responds and remove the server from the cluster if it does not. I think that Microsoft Operations Manager with the SharePoint MOM Pack might assist in this regard and I am about to find out...

     

    So, I'm pretty happy with the ease with which SharePoint (and our application) was able to be configured by someone who has never configured a NLB SharePoint environment before.

    For more details on the architecture, configurations and features in relation to NLB there is some good information about NLB on TechNet.

  • Adding SharePoint 2007 Search to the Search Providers in Internet Explorer 7

    IE 7 allows you to add as many search providers as you like into your search box. While you can add all the usual suspects - Live, Google, Yahoo etc, you can also add SharePoint Search servers:

    Open IE and drop down the menu on the search box:

    Click on 'Find More Providers'. This will take you to a page that allows you to either select from one of the known Search providers or add your own, given the URL for it. To add MOSS, enter the URL for a search for the word TEST (as it says on the right-hand side of the page).

     

    For example, for me the URL is:

    http://moss.macroview.com.au/SearchCenter/Pages/results.aspx?k=TEST

    You can automate this process for your users by generating the XML and adding a Link tag into the Head of a web page.

    There is an article on MSDN that shows how to do this.

     

     

     

  • Excluding Content Types from Search Results

    I received a question recently about excluding certain items from the search results and thought it was worth a post. There are two main options that I thought of when asked.

    Firstly, editing the XSL on the Core Results web part that controls the format of the returned results. This works fine for actually hiding the items, for example, you can wrap the results display part of the XML in an xsl:if to exclude the current item:

    <xsl:if test="contenttype='Contract'">

    (assuming you have contenttype included in the Selected Columns XML within the Core Results web part)

    The reason why this is not such a great idea is that the web part that displays the results statistics will still include the items in the count and seeing as you cannot access the hidden control that binds the web parts together and can't change the output of the Statistics web part you cannot change that.

    The best way is to limit the results based on Scope. Either by creating a separate Scope that can be used when searching or by modifying the default Scope used, ie. All Sites.

    First you need to make sure that the Managed Property you want to limit the Scope with is allowed to be used in scopes. To do this go to Central Admin, Shared Services Admin for the SSP in question, Search Settings, Meta-data Property Mappings. Then edit the Managed Property and check the 'Allow this property to be used in scopes' check box:

    Next, you need to add a new rule to the Scope to Exclude the items based on the value for the Managed Property. Go back to Search Settings, Click on View Scopes and then edit the scope you want to change. Click Add new Rule and create a Property Query rule that is set the to Exclude items where the property you modified above is equal to the value you want to exclude. For example to exclude Folders from search results:

    (note that if you're interested in excluding folders, the above works in SharePoint, but you'll need to do something more for file shares - see Scott Jamisons's post)

    Then make sure that you run the Update Scopes option so that the change to Scope takes effect.

    I'm not 100% sure, but I think that if you have not had a Scope based on this Managed Property (or it might be if you enable the Managed Property for use in Scopes) you may need to do a Full Crawl to have this all work properly.

    Also, if you check the 'Show Scope Picker' option in the Advanced Search Box web part, and you have created a new scope for the Exclusion to apply to, the user can select that Scope when searching. 

    Be aware when doing all this that the will be a price to pay somewhere if you have a lot of Scopes defined in this way (even if it's only in confusing the user!), and that if you need really custom restrictions, perhaps you are better off with a custom search solution tailored for the way that the users need to search!

     

  • Querying the SharePoint Change Logs

    SharePoint 2007 keeps a record of all changes made to data, users, permissions, sites etc in a Change Log which you can access via the object model. This is actually how the indexer figures out what to crawl as part of an incremental crawl.

    You need to be aware that the log history length is controlled by configuration available to server admins so it is possible that the changes you are looking for might have dropped off the log. And this is why there is a caveat around incremental crawling in Indexing...

    For example, say I wanted to get all the items that have been added, modified or deleted from a list:

      //Create a new query, and set it not to return anything by default.
     SPChangeQuery listQuery = new SPChangeQuery(false, false);

     //Now set if to return the types of info we want.
     //In this case item changes of type Add, Delete and Update...
     listQuery.Item = true;
     listQuery.Add = true;
     listQuery.Delete = true;
     listQuery.Update = true;

     //Get the changes that match the query.
     SPChangeCollection changesCollection = currentWeb.GetChanges(listQuery);

     //Do something with them.
     foreach (SPChangeItem currentChange in changesCollection)
      {
         ...
       }

    The code above will return ALL changes in the log for list items in the specified Web. A Change Token is returned in the as part of the SPChangeCollection and can be passed to subsequent calls to GetChanges to return changes since the last time you checked. Meaning you can perform synchronisation operations in your own apps.

    To get the last change token:

        SPChangeToken lastChangeToken = changesCollection.LastChangeToken;

    Set the queries start token to the last change token we had:

        listQuery.ChangeTokenStart = lastChangeToken;

    And then call the GetChanges method as above.

     

  • SharePoint Alternate Access Mappings

    I came across the second part of Troy Starr's Trilogy on Alternate Access Mappings in SharePoint 2007.

    It's a great series from an Administrators perpective on what they are and why you need them.

    I recently 'stumbled' upon them when writing some code that accessed a site based on the URL. When I passed the internal name to the web service everything worked fine, by FQDN, IP Address, or externally accessible URL did not work (note I didn't realise that at THAT stage neither would all of the external links - see Troy's post). Once I added alternate access mappings for all the possible ways people could pass a URL to the site to SharePoint all was well.

    Whatever you use as the URL when setting up your new server (and therefore becomes the URL you see when choosing application pools in central admin) is the only URL that will work 100% unless you create AAM's!!

    What I think often happens is people set up a server and answer all the configuration prompts with the internal port 80 details. Then, when they come to go into production, realise that they need another port, or the server to be accessible via SSL, or FQDN and forget that AAM's need to be configured - and it's not until you try to do...

    SPSite mysite=new SPSite("http://...");

     ... where the URL is not mapped that you notice it's broken. Or someone reports that some links don't work...

    Great post Troy - awaiting Part 3 with antici...........pation. Smile

     

  • SharePoint Solution Template

    I included a link to a SharePoint Solution Template for VS.Net 2005 in my previous post, which I discovered was a broken link, so I have created my own and you can get it here.

    It is VERY basic. Just one of our deployment solutions stripped of all the content, which the makecab/build targets setup and the directory structure for things like assemblies, templates and the folders for each file destination.

    One day I'd like to automate the generation of the DDF file, perhaps via a macro or XSLT.

    The Project Template will appear under My Templates in the C# area (assuming you put it in the top level templates area under the Visual Studio 2005 directory in your My Documents area).

    It will prompt you with a warning about possible badness because of the custom build target stuff, but ignore and open normally - unless you don't trust me of course ;-)

  • SharePoint Solutions Rock!

    Been working pretty hard recently to get our 2007 product releases out the door, and one of the things that has made our lives much easier is the Solution Packaging in SharePoint V3.

    Our application consists of several features, a web service, custom admin pages and numerous assemblies. The Solution deployment model that V3 uses means we can have one VS.Net 2005 project that handles the deployment of all these pieces across all servers in the farm with one stsadm command and about three clicks in central admin!

    There are quite a few posts that have details about how to get started, one, which saved me today when I had 'gone it alone' and copied the guts of a collegues Solution project was this one from Andrew Connell - I didn't have the build targets setup correctly! Another that I just found has a download of a VS Solution package template which I will be checking out tomorrow.

    One little issue that I'm having trouble coming to terms with is when you have several solutions that share files. For example a situation where you have several solutions that have assemblies that inherit from a common assembly. So long as the solutions are deployed together and when one is upgraded they both are everything is fine. However, if you upgrade one and the common assembly is removed by the upgrade process and the second solution is not compatible with the new version of the common assembly, things break. Does anyone know a way around this?

     

  • Custom Search Web Part

    Been a while since my last post. Mostly because we have been working very hard to complete a custom search solution.

    The requirements for the solutions were:

    - Data from Treeview controls that is entered into items must be able to be search using the Treeview control to select the search terms.

    - Where there are Choice fields in the item columns these must be rendered as multiple select choices in the search UI.

    - Values must be