Несколько недель назад от наших заказчиков мы получили задачу на создание сервиса бронирования переговорных комнат. Компания заказчика довольна крупная и весьма продвинутая в плане ИТ. У заказчика много филиалов по всей России и в каждом по 5-6 переговорных комнат, сотрудники часто летают проводить совещания из одного филиала в другой, а найти свободную аудиторию «на месте» — это реальная проблема. Поэтому сотруднику было бы неплохо прямо со своего рабочего места в Москве «застолбить» переговорку, например, в Туле и со спокойной совестью лететь в командировку. Опытом создания подобного сервиса мы хотим поделится с хабросообществом.
Анализ
Мы знали, что в компании заказчика есть внутренний портал, сделанный на Microsoft SharePoint 2010, которым ежедневно пользуются около 1500 сотрудников. Плюс в компании настроен Exchange.
Как известно в Exchange уже есть фича бронирования ресурсов в компании. Все это дело реализовано с помощью room mailbox’ов. По сути, каждой переговорной комнате присвоен свой почтовый адрес, и сотрудник должен планировать встречу с данной комнатой. Другие пользователи с помощью почтового клиента могут подключаться к календарям и смотреть на какое число, и время назначена встреча. К тому же в Outlook 2010 есть функционал Room Finder.
Более подробно можно прочитать тут о mailbox в Exchange http://technet.microsoft.com/en-us/library/bb124952(v=exchg.141).aspx
А тут — о Room Finder’e http://support.microsoft.com/kb/2673231
Вроде все выглядит круто и удобно. Однако есть несколько недостатков: во-первых, сотруднику нужен Outlook. Во-вторых, описание комнаты очень скудно, нельзя добавить описание ресурсов (доска, система конференц-связи и т.д.) в наглядном виде.
Соответственно возникла мысль: как бы нам все наглядное представление вынести в SharePoint и при этом не потерять функционал Exchange? При этом хотелось писать поменьше кода. Но как показала практика – без этого не обошлось :)
В общем, поехали!
SharePoint
Для создания сервиса мы сделали три списка: Подразделения, Комнаты совещаний, Бронирование комнат совещаний.
Подразделения представляют собой филиалы в городах.
Комнаты совещаний – это список для хранения комнат и их характеристик, таких как:
- название;
- подразделение;
- вместимость;
- наличие в комнате проектора, маркерной доски конференцсвязи и т.д.
- почтовый адрес, используемый для интеграции с MS Exchange.
И наконец, список броней:
- комната;
- дата встречи;
- время начала;
- время конца;
- автор брони.
Итак, списки у нас есть, но кто-то же должен эти списки заполнять, поэтому мы разделили пользователей по ролям:
- Пользователи могут планировать свои встречи и совещания, бронировать комнаты или делать специализированный поиск по параметрам.
- Администратор переговорных комнат подразделения может переносить отменять существующие брони в рамках своего подразделения.
- Администратор создает и редактирует переговорные комнаты, а также справочник с их характеристиками – количество мест, проектор, доска и т.д. Имеет возможность переключаться между подразделениями и исполнять роль Администратора переговорных комнат.
UI
Т.к. у заказчика дизайн уже был натянут на портал, нашим дизайнерам нужно было поработать совсем чуть-чуть. С чем они отлично справилась:
1. Главная страница пользователя:
2. По отдельной ссылке можно перейти на список комнат. В данном списке выводится основная информация. Подробная информация о комнате отображается по клику на название комнаты.
3. По ссылке «Поиск аудиторий» отображается календарь на две недели. В каждой ячейке отображено время, кем комната забронирована и на какое время.
4. Отдельным кликом на «плюсик» можно будет забронировать переговорную комнату.
Реализация
Чтобы сделать такую красоту нам потребовалось написать WCF-сервис, который возвращает json и при этом работает в контексте SharePoint’a. Данная проблема решается правильным web.config’ом и наличием SVC в папке ISAPI. О том, как правильно настроить web.config для отправки json’a, написано очень много. Например, тут:
http://www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide
Далее мы сделали несколько ASPX-страниц, добавили их в модуль и реализовали всю логику работы с WCF-сервисом через javascript с использованием knockout-фрэймворка.
Основные проблемы у нас возникли с Exchange. Практически весь нужный функционал реализован через вызовы методов EWS Managed API. Однако нам нужно было автоматически создавать mailbox’ы при создании элемента в списке комнат.
На просторах интернета было найдено, что mailbox можно создавать лишь через powershell, и вызвать команду из C#-кода в принципе не проблема.
На список комнат мы написали event receiver и попытались вызвать powershell команду на удаленном сервере через использование Runspace и WSManConnectionInfo. Однако, во-первых, это не безопасно, а во-вторых, сделать это у нас так и не получилось из-за проблем отказа доступа при соединении.
В результате решили написать еще один WCF-сервис и развернуть его на сервере с Exchange. По сути, у этого сервиса есть только один метод:
public void CreateRoom(ExchangeRoom Room)
{
RunspaceConfiguration runspaceConfig = RunspaceConfiguration.Create();
PSSnapInException snapEx = null;
PSSnapInInfo psinfo = runspaceConfig.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out snapEx);
using (Runspace runSpace = RunspaceFactory.CreateRunspace(runspaceConfig))
{
runSpace.Open();
if (!(MailBoxAlreadyExist(Room.Email, runSpace)))
{
Command createMailbox = new Command("New-Mailbox");
createMailbox.Parameters.Add("UserPrincipalName", Room.Email);
createMailbox.Parameters.Add("Name", Room.Name);
createMailbox.Parameters.Add("Room");
using (Pipeline pipeLine = runSpace.CreatePipeline())
{
pipeLine.Commands.Add(createMailbox);
pipeLine.Invoke();
}
}
else
{
throw new Exception("Комната с таким адресом уже создана");
}
}
}
Как мы писали выше, вся логика работы у нас реализована через WCF-сервис, поэтому логично было дополнить наш сервис методами для бронирования комнаты в Exchange. Для этого мы вызывали метод сервиса для создания нашей брони в Exchange. Всю необходимую информацию помещали в Microsoft.Exchange.Data.Appointment, а далее с помощью API создавалось собрание(meeting) в календаре Exchange.
public static void CreateReservation(Reservation reservation)
{
InitService();
var appointment = new Appointment(service);
appointment.Subject = "Meeting";
appointment.Start = new DateTime(reservation.Date.Year, reservation.Date.Month, reservation.Date.Day, reservation.FromTime, 0, 0);
appointment.End = new DateTime(reservation.Date.Year, reservation.Date.Month, reservation.Date.Day, reservation.ToTime, 0, 0);
string roomMailboxAddress = string.Format("room_{0}@{1}",reservation.RoomId, SharePointConstantString.MailDomain);
appointment.Location = reservation.RoomName;
appointment.Resources.Add(roomMailboxAddress);
appointment.RequiredAttendees.Add(reservation.User.Email);
appointment.Save(SendInvitationsMode.SendToAllAndSaveCopy);
}
Проблему «обратной» синхронизации (т.е. когда сотрудник в своем календаре через Outlook забронировал комнату) было решено решить через задание (timer job). Данный job собирал всю информацию с Exchange и создавал недостающие брони, а отмененные удалял.
Итого
Нам удалось создать такую архитектуру, которая позволяет получить масштабируемое и гибкое решение, построенное на стеке Microsoft технологий и использующее все преимущества комплексного решения. Система настраивается и сопровождается стандартными инструментами платформ.
Таким образом, сегодня
- руководство компании получает единые инструменты совместной работы, повышающие эффективность работы большого количества сотрудников;
- сотрудники компании проще, быстрее и без нервов планируют свою работу и ключевые встречи и совещания;
- ИТ-персонал получил в распоряжение гибкое, масштабируемое решение, которое сопровождается стандартными средствами и максимально использует функциональность “из коробки”. А значит, при переходе на новые версии SharePoint и Exchange проблем особых не возникнет.
Автор: eastbanctech