Bulk check-in for SharePoint
This has absolutely nothing to do with Silverlight or WPF, but thought people might find it useful anyway. If you've ever tried to check in a large number of files into SharePoint, you'll notice that there isn't a way to check them in bulk -- you have to check them in one at a time, which gets pretty tedious when you have several hundred files. You can buy programs which add this feature (e.g. https://www.sharepointboost.com/batch-check-in.html) -- but you can also write it yourself, using Sharepoint's web services. I won't claim it's the simplest or most elegant thing I've ever written, but it got the job done for me.
Here's how to do it:
1) Create a new project in Visual Studio. Pretty much anything except Silverlight will work -- Silverlight requires a cross domain file that most SharePoint servers don't & shouldn't have.
2) Add some Web References. (I recommend the old-school Web Reference over Service Reference since most of the sample code out there uses the former) You'll want:
I called mine ListsWebService and SiteDataWebService, respectively
3) Grab the following code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using System.IO;
using System.Xml;
namespace SharePointCheckin
{
public class SharepointHelper
{
// useful link to know about:
// https://weblogs.asp.net/jimjackson/archive/2008/02/11/get-a-sharepoint-list-guid-from-the-browser.aspx
private string urlToSubweb;
public SharepointHelper(string urlToSubweb)
{
this.urlToSubweb = urlToSubweb;
}
public string[] GetFolderItems(string absoluteUrl)
{
var service = new SiteDataWebService.SiteData();
Initialize(service, "sitedata.asmx");
SiteDataWebService._sFPUrl[] urls = null;
var r = service.EnumerateFolder(absoluteUrl, out urls);
string[] result = (from doc in urls
select urlToSubweb + "/" + doc.Url).ToArray();
return result;
}
public bool CheckIn(string absoluteUrl, string comment)
{
ListsWebService.Lists service = new ListsWebService.Lists();
Initialize(service, "lists.asmx");
bool result = service.CheckInFile(absoluteUrl,
comment,
"1");
// "1" is the code for major check-in rather than minor or replace
return result; // true for success, false for failure or if file is already checked in
}
public bool CheckOut(string absoluteUrl)
{
ListsWebService.Lists service = new ListsWebService.Lists();
Initialize(service, "lists.asmx");
bool result = false;
try {
result = service.CheckOutFile(absoluteUrl,
"false", // checked out for offline editing
null); // lastmodified
} catch (System.Web.Services.Protocols.SoapException e) {
if (e.Detail.InnerText.Contains("is checked out or locked for editing by"))
return false;
else
throw;
}
return result; // true for success, false for failure or if file is already checked out
}
public bool RevertCheckOut(string absoluteUrl)
{
ListsWebService.Lists service = new ListsWebService.Lists();
Initialize(service, "lists.asmx");
bool result = service.UndoCheckOut(absoluteUrl);
return result; // true for success, false for failure or if file is already checked in
}
// based off https://www.sharepoint-tips.com/2007/02/how-to-use-getlistitems-web-service.html
public string GetWebID()
{
SiteDataWebService.SiteData service = new SiteDataWebService.SiteData();
Initialize(service, "sitedata.asmx");
SiteDataWebService._sWebMetadata webMetaData;
SiteDataWebService._sWebWithTime[] arrWebWithTime;
SiteDataWebService._sListWithTime[] arrListWithTime;
SiteDataWebService._sFPUrl[] arrUrls;
string roles; string[] roleUsers; string[] roleGroups;
uint i = service.GetWeb(out webMetaData, out arrWebWithTime, out arrListWithTime, out arrUrls, out roles, out roleUsers, out roleGroups);
return webMetaData.WebID;
}
// based off https://www.sharepoint-tips.com/2007/02/how-to-use-getlistitems-web-service.html
public string[] GetListItems(string listName)
{
ListsWebService.Lists service = new ListsWebService.Lists();
Initialize(service, "lists.asmx");
XmlDocument doc = new XmlDocument();
// if you want to do server-side filtering, here's a good place to do it
doc.LoadXml("<Document><Query /><ViewFields /><QueryOptions /></Document>");
XmlNode listQuery = doc.SelectSingleNode("//Query");
XmlNode listViewFields = doc.SelectSingleNode("//ViewFields");
XmlNode listQueryOptions = doc.SelectSingleNode("//QueryOptions");
string webID = GetWebID();
XmlNode items = service.GetListItems(listName, string.Empty, listQuery, listViewFields, string.Empty, listQueryOptions, webID);
List<string> results = new List<string>();
foreach (var row in items.ChildNodes[1].ChildNodes) {
if (row is XmlElement) {
string name = (row as XmlElement).GetAttribute("ows_EncodedAbsUrl");
results.Add(name);
}
}
return results.ToArray();
}
public string[] GetListItemsFromGuid(string listGuid)
{
SiteDataWebService.SiteData service = new SiteDataWebService.SiteData();
Initialize(service, "sitedata.asmx");
//string webID = GetWebID();
string items = service.GetListItems(listGuid, "", "", 0);
var document = new XmlDocument();
document.LoadXml(items);
List<string> results = new List<string>();
foreach (var row in document.DocumentElement.GetElementsByTagName("z:row")) {
if (row is XmlElement) {
string name = (row as XmlElement).GetAttribute("ows_EncodedAbsUrl");
results.Add(name);
}
}
return results.ToArray();
}
private void Initialize(System.Web.Services.Protocols.SoapHttpClientProtocol service,
string fooDotAsmx)
{
service.Url = urlToSubweb + "/_vti_bin/" + fooDotAsmx;
service.UseDefaultCredentials = true;
}
}
}
4) Write some logic to get a list of files, & enumerate over them to check them in or out. For example, mine looked like:
var helper = new SharepointHelper(https://sitename);
var guid = "{1234aaaa-1a2a-11a1-aaa1-11111a1a1111}";
var files =
from file in helper.GetListItemsFromGuid(guid)
where file.EndsWith(".docx")
select file;
foreach (string file in files) {
//helper.CheckOut(file);
helper.CheckIn(file, "bulk check in");
//helper.RevertCheckOut(file);
}
Note that SharePoint handles folders differently from lists (e.g. "shared documents"), you'll need to enumerate those differently. For folders (which live inside lists), you just need the absolute URL of the folder, then call SharepointHelper.GetFolderItems(). Enumerating a list is a little trickier, you'll need the GUID for the list, then call SharepointHelper.GetListItemsFromGuid(). There's probably a way to figure out the guid programmatically if you're willing to call enough web services, but I did it manually using the steps on https://weblogs.asp.net/jimjackson/archive/2008/02/11/get-a-sharepoint-list-guid-from-the-browser.aspx
Happy hacking!
Comments
Anonymous
November 14, 2009
SharePoint Designer does bulk check-in of documents, and it is now free.Anonymous
November 16, 2009
Nice... but I'm just going to wait for SP 2010. I have lost all desire to code for SP 2007.