Linq to SharePoint. Блокировка документов
Иногда при работе с документами необходимо на некоторое время запретить другим пользователями его изменение. В Microsoft SharePoint 2010 для реализации данного требования есть две возможности: извлечение файла и блокировка файла. В этом посте я расскажу об отличиях этих двух операций и покажу пример реализации блокировки документов с использованием репозитория.
Извлечение файлов
Первый способ, извлечение файлов, доступен из интерфейса пользователя. Это стандартная реализация системы управления версиями. Есть один недостаток: пользователи с достаточными на то привилегиями могут отменить извлечение документа или вернуть его. SharePoint в таком случае просто выдаст предупреждение о том, что файл был извлечен другим пользователем:
Такое поведение SharePoint'а оправдано, тем, что пользователь, извлекший документ может, например, уйти в отпуск. И тогда администратор может отменить извлечение документа, предоставив тем самым возможность работать с документов другим пользователям. Такой подход не подходит для обеспечения монопольного доступа к файлу.
Блокировка документов
Второй способ более радикален и позволяет обеспечить монопольный доступ к файлу. Но здесь есть несколько особенностей:
- Заблокировать файл, используя интерфейс пользователя нельзя;
- Блокировать файл можно только на определенное время. Это связано с тем, что блокировку может отменить только пользователь, наложивший её;
- При попытке редактировать свойства заблокированного файл, возникает исключение, которое не обрабатывается SharePoint'ом в дружелюбном виде.
Исключение это выглядит следующим образом:
<nativehr>0x80071779</nativehr><nativestack></nativestack>
Файл "http://[SPServer]/DocumentLibrary/Report.xlsx" заблокирован для монопольного использования пользователем SHAREPOINT\system.
Или так (по-английски):
<nativehr>0x80071779</nativehr><nativestack></nativestack>
The file "http://[SPServer]/DocumentLibrary/Report.xlsx" is locked for exclusive use by SHAREPOINT\system.
При открытии файла в клиентском приложении SharePoint выдаст вот такое сообщение:
Блокировка файла программными средствами
Так как блокировать документы в SharePoint'е можно только программно, я покажу как это сделать на примере репозитория, описанного в посте Linq to SharePoint. Паттерн Repository
Блокировка в объектной модели SharePoint
В объектной модели SharePoint блокировка реализована в классе SPFile и представлена следующими методами:
- Lock(SPLockType lockType, string lockId, TimeSpan timeout) - блокирование файла на заданный период времени;
- ** RefreshLock(string lockId, TimeSpan timeout)** - продление блокировки файла на заданный период времени;
- ReleaseLock(string lockId) - снятие блокировки с файла;
Информация о блокировке файла содержится в следующих свойствах объекта SPFile:
- LockedByUser - пользователь, заблокировавший файл;
- LockedDate - дата блокировки файла;
- LockExpires - время, на которое заблокирован файл;
- LockId - Id блокировки;
- LockType - тип блокировки.;
Тип блокировки может принимать следующие значения:
- Exclusive - доступ к файлу имеет только пользователь, наложивший на него блокировку;
- Shared - доступ к файлу имеет группа пользователей;
- None - файл не заблокирован;
Получение файла
Прежде чем блокировать файл, необходимо его получить. Для этого в репозиторий я добавил следующий метод:
/// <summary>
/// Получение файла элемента
/// </summary>
/// <param name="id">Id элемента</param>
/// <returns>Файл</returns>
protected SPFile GetFile(int id)
{
// Получаем элемент
var entity = GetEntity(id);
// Получаем список, содержащий элемент
var list = MetaData.List;
// Получаем сайт, содержащий список
var web = list.ParentWeb;
// Получаем файл
var file = web.GetFile(entity.ServerUrl);
return file;
}
Этот метод использует мета-данные списка, способ получение которых я описал в посте Linq to SharePoint. Получение мета-данных списка. В SharePoint'е и для элемента списка можно получить файл, но заблокировать его не удастся, т.к. он не является файлом, подлежащим хранению в системе управления версиями.
Блокировка файла
Файл есть, теперь можно и блокировку реализовать:
/// <summary>
/// Блокировка элемента
/// </summary>
/// <param name="id">Id элемента</param>
/// <param name="lockType">Тип блокировка</param>
/// <param name="timeSpan">Тайм-аут</param>
public void LockEntity(int id, SPFile.SPLockType lockType, TimeSpan timeSpan)
{
var file = GetFile(id);
if (file.ListItemAllFields.ParentList is SPDocumentLibrary)
{
if (file.LockType == SPFile.SPLockType.None)
{
file.Lock(lockType, Guid.NewGuid().ToString("N"), timeSpan);
}
}
}
/// <summary>
/// Снятие блокировки
/// </summary>
/// <param name="id">Id элемента</param>
public void ReleaseLockEntity(int id)
{
var file = GetFile(id);
if (file.LockType != SPFile.SPLockType.None)
{
file.ReleaseLock(file.LockId);
}
}
/// <summary>
/// Продление блокировки
/// </summary>
/// <param name="id">Id элемента</param>
/// <param name="timeSpan">Тайм-аут</param>
public void RefreshLockEntity(int id, TimeSpan timeSpan)
{
string lockId;
var file = GetFile(id);
if (file.LockType != SPFile.SPLockType.None)
{
file.RefreshLock(file.LockId, timeSpan);
}
}
В объектной модели SharePoint при блокировки файла или её продления требуется указать Id этой блокировки. В реализации этих методов в репозитории я "опустил" это требование.
Информация о блокировке
Для получения информации о блокировке файла я сделал простой класс:
public class EntityLockInfo
{
public SPFile.SPLockType LockType { get; set; }
public string LockId { get; set; }
public DateTime LockExpires { get; set; }
public DateTime LockedDate { get; set; }
public SPUser LockedByUser { get; set; }
public EntityLockInfo(SPFile file)
{
LockType = file.LockType;
LockId = file.LockId;
LockExpires = file.LockExpires;
LockedByUser = file.LockedByUser;
LockedDate = file.LockedDate;
}
}
И метод в репозитории для получения его экземпляра:
/// <summary>
/// Получение информации о блокировке элемента
/// </summary>
/// <param name="id">Id элемента</param>
public EntityLockInfo GetLockInfo(int id)
{
var file = GetFile(id);
var info = new EntityLockInfo(file);
return info;
}
Применение
Использовать новый функционал можно примерно так:
var repository = new DocumentRepository(siteUrl, false);
repository.LockEntity(1, SPFile.SPLockType.Exclusive, TimeSpan.FromHours(10));