Monday, November 30, 2009

How To: Page search results in SharePoint 2007

Technorati Tags: ,,,

Sometimes a little knowledge can go a long way in implementing a scalable search solution. Like in the case of not returning all the results from a SharePoint Microsoft.Office.Server.Search.Query.FullTextSQLQuery. If your search returns hundreds or even thousands of results you should consider paging the results. It is fairly easy to page search results in SharePoint. The key is to return the total number of pages available in the results when you first issue the query. Then based on that number you can pass in your page number and calculate the starting row for the search and set the StartRow property. The following code shows some simple code to get up and running on implementing paging search results. This example can also use the KeywordQuery object also.

This example uses Microsoft.Office.Server.Search and System.Data namespaces.

 

public static DataTable GetFTSQueryPagedSearch(Int32 pageNumber)
{

           DataTable results = new DataTable();
           int pageSize = 50;
           int startRow = (pageNumber -1) * 50;

           using (SPSite site = new SPSite("http://basesmcdev2/sites/tester1"))
           {
               FullTextSqlQuery fts = new FullTextSqlQuery(site);              
               fts.QueryText = "SELECT Title,FileExtension,ContentType,Created,
                                            LastModifiedTime,Path FROM SCOPE()
                                            WHERE (CONTAINS(Path,'\"/sites/tester1/Lists/* \"'))";

               fts.ResultTypes = ResultType.RelevantResults;
               fts.RowLimit = pageSize;
               fts.StartRow = startRow;

               ResultTableCollection rtc = fts.Execute();
               if (rtc.Count > 0)
               {

                   using (ResultTable relevantResults = rtc[ResultType.RelevantResults])
                   {
                       results.Load(relevantResults, LoadOption.OverwriteChanges);

                       int totalNumberOfPages = (int)Math.Round((double)relevantResults.TotalRows / pageSize);
                   }

               }

               return results;

           }

}

Tuesday, November 24, 2009

How To: Copy list items across site collections in SharePoint

Technorati Tags: ,,,

The other day I demonstrated some code on how to copy files from one document library to another in another site collection. However, the code does not work with lists and list items. Dealing with folders and items within folders is completely different with SharePoint lists. In fact, it seems needlessly complicated. The sample code below takes a source site collection url, destination site collection url, list name (must exist in both source and destination site collection, the string of the UniqueId guid of the starting folder, and the folder’s url.

The difficult part was getting the folder url to work when creating a new folder in a SharePoint list. You must use the SPListItemCollection.Add method passing in an empty string, SPFileSystemObjectTyp.Folder, and the folder url.  In order to make this work with sub folders you must construct a folder url in the form of “grandparent.folder.name +/ + parent.folder.name … + / + folder.name”. There is nothing on the SPFolder object that can give you this “container” path. So I had to do some substring(ing).

The other problem was getting at the child items of a folder in a SharePoint list. When using a document library the files collection of the SPFolder works great, unfortunately, this does not exist with SharePoint lists. So you have to make use of the SPQuery object to bring back the child items within the folder. Finally, you can use linq to filter out the list items from the folders. All in all it works great and will create an identical copy of the folder structure from one list to the other across site collections.

 

public static void CopyListFolderToAnotherSiteCollection(string sourceUrl, string destinationUrl,
          string listName, string uniqueFolderID, string newFolderUrl)
      {
          SPListItem newItem = null;

          using (SPSite sourceSite = new SPSite(sourceUrl))
          {
              using (SPWeb sourceWeb = sourceSite.OpenWeb())
              {

                  SPList sourceList = sourceWeb.Lists[listName];
                  SPFolder sourceFolder = sourceWeb.GetFolder(new Guid(uniqueFolderID));
                  SPQuery q = new SPQuery();
                  q.Folder = sourceFolder;
                  SPListItemCollection sourceItems = sourceList.GetItems(q);
                  using (SPSite destSite = new SPSite(destinationUrl))
                  {
                      using (SPWeb destWeb = destSite.OpenWeb())
                      {
                          SPList destList = destWeb.Lists[listName];

                          SPListItem destItem = destList.Items.
                              Add(string.Empty, SPFileSystemObjectType.Folder, newFolderUrl);

                          destItem.Update();
                          SPFolder destFolder = destWeb.GetFolder(destItem.UniqueId);

                          List<SPListItem> listItems = (from l in sourceItems.OfType<SPListItem>()
                                         where l[SPBuiltInFieldId.ContentType].ToString() != "Folder" select l).ToList();

                          List<SPListItem> folderItems = (from l in sourceItems.OfType<SPListItem>()
                                                        where l[SPBuiltInFieldId.ContentType].ToString() == "Folder"
                                                        select l).ToList();

                          foreach (SPListItem sourceItem in listItems)
                          {
                              newItem = destList.Items.Add(destFolder.ServerRelativeUrl,
                                                                                  SPFileSystemObjectType.File,null);
                              newItem["Title"] = sourceItem["Title"];
                              newItem[SPBuiltInFieldId.ContentType] = sourceItem[SPBuiltInFieldId.ContentType];
                              newItem.Update();
                          }

                          foreach (SPListItem f in folderItems)
                          {
                              SPFolder folder = f.Folder;
                              int index = folder.ServerRelativeUrl.IndexOf(sourceList.RootFolder.Url);
                              string folderUrl = folder.ServerRelativeUrl.Substring(index + sourceList.RootFolder.Url.Length + 1);
                              CopyListFolderToAnotherSiteCollection(sourceUrl, destinationUrl,
                                                           listName, folder.UniqueId.ToString(), folderUrl);
                          }

                      }
                  }
              }
          }
      }

Monday, November 23, 2009

How To: Copy files across site collections in SharePoint

Technorati Tags: ,,,

SharePoint has great built in methods to copy or move documents across sub-sites or lists within the same site collection. You can use SPFile.MoveTo or SPFilem.CopyTo. You can even move whole folders and their contents with the SPFolder.MoveTo method. Very nice. Unfortunately, these do not work across site collections. However you can use the object model and a little bit of recursion to copy folders across site collections. The following code takes the url string to the source site collection, url string to the destination site collection, the name of the document library, the url string for the folder and the folder name.

public static void CopyFolderToAnotherSiteCollection(string sourceUrl, string destinationUrl,
          string listName, string folderUrl, string folderName)
      {
          string listRelatvieFolderUrl = folderUrl.Substring(folderUrl.IndexOf("/")+1);
          using (SPSite sourceSite = new SPSite(sourceUrl))
          {
              using (SPWeb sourceWeb = sourceSite.OpenWeb())
              {
                  SPFolder sourceFolder = sourceWeb.GetFolder(folderUrl);                            
                  using(SPSite destSite = new SPSite(destinationUrl))
                  {
                      using(SPWeb destWeb = destSite.OpenWeb())
                      {
                          SPListItem destItem = destWeb.Lists[listName].Items.
                              Add(string.Empty, SPFileSystemObjectType.Folder, listRelatvieFolderUrl);

                          destItem.Update();

                          SPFolder destFolder = destWeb.GetFolder(folderUrl);

                          foreach (SPFile file in sourceFolder.Files)
                          {  
                              destFolder.Files.Add(file.Url, file.OpenBinary());                            
                          }

                          foreach (SPFolder folder in sourceFolder.SubFolders)
                          {
                              CopyFolderToAnotherSiteCollection(sourceUrl, destinationUrl, listName,
                                  folder.Url, folder.Name);
                          }

                      }
                  }
              }
          }
      }

The SPFolder.Files.Add has many overloaded methods. You can decrease the amount of memory by using a stream object instead of the byte array returned by the SPFile.OpenBinary. You can also add metadata by populating a hashtable and sending that.

Friday, November 20, 2009

How To: Make an ECB menu item for a folder

Technorati Tags:

There seems to be a lot of information on how to create a “Edit Control Block” for a list item in SharePoint. An ECB is a menu item that shows up when you click on an item in a SharePoint list or document library. You can generally make these by defining a feature and a custom action xml file. In the custom action xml file you set certain attributes of the “CustomAction” element. The most important of which is the UrlAction attribute which can be a url which the user will be redirected to when clicking on the item.

http://msdn.microsoft.com/en-us/library/ms460194.aspx

The location attribute determines where the menu item will be displayed. Setting the location to “Edit Control Block” tells SharePoint to display the menu item in the context menu for the list item. In a document library you can further refine when the menu item is displayed by setting the RegistrationType to “FileType” and the RegistrationId to a file extension like “tif”. Then the menu item will only show up for tif files.

<CustomAction RegistrationType="FileType" RegistrationId="tif" Location="EditControlBlock" Sequence="106" Title="View" ImageUrl="/_layouts/images/view.gif">
  <UrlAction Url="_layouts/settings.aspx"/>
</CustomAction>


So how do I make this menu item just for folders in a list or library. Well you use the RegistrationType set to “ContentType” and the RegistrationId set to “0x0120” which is the contenttype id for a folder in SharePoint.



<CustomAction RegistrationType="ContentType" RegistrationId="0x0120" Location="EditControlBlock" Sequence="106" Title="View" ImageUrl="/_layouts/images/view.gif">
  <UrlAction Url="_layouts/settings.aspx"/>

</CustomAction>



Finally, you can actually call javascript from your UrlAction Url attribute. The following shows how to construct a querystring detecting whether to use https or http for the querystring.


<CustomAction RegistrationType="FileType" RegistrationId="tif" Location="EditControlBlock" Sequence="106" Title="View" ImageUrl="/_layouts/images/view.gif">
  <UrlAction Url="javascript: var isHttps = window.location.href.indexOf('https') == 0; var i = (isHttps) ? 8 : 7; var itemUrlToken = '{ItemUrl}'; var siteUrlwoHttp = window.location.href.substring(i) ; var index = siteUrlwoHttp.indexOf('/'); var itemUrl = ((isHttps) ? 'https://' : 'http://') + siteUrlwoHttp.substring(0, index) + itemUrlToken; window.open('/_layouts/custompage.aspx?doc=' + itemUrl);" "/>
</CustomAction>