Этот пост посвящен описанию создания справочника на сайте SharePoint с использованием WCF-сервиса, библиотеки jQuery и плагинов к ней.

В рамках запуска нового сайта ИНГГ СО РАН, который построен на SharePoint Server 2007, было решено разработать новую версию телефонного справочника сотрудников. Для клиентской части был выбран jqGrid в качестве элемента для таблицы сотрудников и jQueryUI для отображения подробной информации о сотруднике. Данные клиентская часть получает от RESTful веб-сервиса, реализованного на WCF, который развернут тут же, на сайте SharePoint.
Пара картинок того, что получилось:


Справочник сотрудников отображается на странице веб-частей на сайте SharePoint в виде веб-части, содержимое которой загружается из ascx-контрола.
Контрол JqGridPhoneBookControl.ascx практически не содержит серверного кода, за исключением значения скрытого тега <input>, с помощью которого через свойства веб-части передается номер подразделения для начальной фильтации.
Сервис, предоставляющий данные, имеет следующий интерфейс.
JqGridResult – класс, возвращаемый методом GetAllRecordsForJqGrid и принимаемый reader’ом jqGrid.
Код сервиса разворачивается в GAC вместе с остальными сборками wsp-решения для SharePoint, svc-файл веб-сервиса помещается в подпапке папки c:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\ вместе с файлом web.config, в котором необходимо указать webHttpBinding
Для корректной работы WCF-сервисов в приложении SharePoint, необходимо установить SPWCFSupport, либо реализовать подобный код у себя в решении. Подробности зачем это нужно – здесь.
Содержимое таблицы phoneBookGrid (см. JqGridPhoneBookControl.ascx) формируется с помощью jqGrid, плагина к jQuery, который берет данные из WCF-сервиса, описанного выше, посредством ajax-запроса, в формате json.
jqGrid принимает json-объект с определенными именами свойств, однако можно настроить jsonReader и передавать какой угодно json-объект. В нашем случае – это JqGridResult.
При щелчке по строке таблицы вызывается функция GetEmployeeDetails, которая отправляет еще один ajax-запрос к веб-сервису и отображает данные в модальном диалоге jQueryUI
Фотография сотрудника берется из веб-сервиса с помощью GET-запроса
В данной статье не приведен код, реализующий доступ к данным. Могу лишь сказать, что это простая реализация паттерна Репозиторий на Linq To SQL. Для фильтрации, поиска и постраничного вывода использован интерфейс IQueryable<T>.
Как оказалось, нет ничего сложного в реализации и развертывании решений на jQuery иWCF под SharePoint.
Однако, если SharePoint работает на IIS7, то может возникнуть проблема, на решение которой мне пришлось потратить некоторое время.
После развертывания решения на production-сервере, выдавалась 404 ошибка при запросах на URL типа “~/_layouts/IPGG.IntegrationSystem/PhoneBookService.svc/GetRecord”.
Решение состоит в том, что необходимо зарегистрировать обработчник запросов для .svc. По умолчанию, в IIS7 для SharePoint-сайта этого не сделано.

P.S. Спасибо хабраюзерам Atreides07 и Atv за помощь в освоении Хабра.
UPD: Перенес в блог «Веб-разработка».
UPD2: Пост в моем блоге

Введение
В рамках запуска нового сайта ИНГГ СО РАН, который построен на SharePoint Server 2007, было решено разработать новую версию телефонного справочника сотрудников. Для клиентской части был выбран jqGrid в качестве элемента для таблицы сотрудников и jQueryUI для отображения подробной информации о сотруднике. Данные клиентская часть получает от RESTful веб-сервиса, реализованного на WCF, который развернут тут же, на сайте SharePoint.
Пара картинок того, что получилось:


WCF-сервис и PhoneBookWebpart
Справочник сотрудников отображается на странице веб-частей на сайте SharePoint в виде веб-части, содержимое которой загружается из ascx-контрола.
protected override void CreateChildControls()
{
if (!_error)
{
try
{
base.CreateChildControls();
if (!this.WebPartManager.DisplayMode.AllowPageDesign)
{
var gridControl =
(JqGridPhoneBookControl)Page.LoadControl(
"~/_controltemplates/IPGG.IntegrationSystem/PhoneBook/JqGridPhoneBookControl.ascx");
gridControl.DivisionNumber = DivisionNumber;
Controls.Add(gridControl);
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
}
* This source code was highlighted with Source Code Highlighter.
Контрол JqGridPhoneBookControl.ascx практически не содержит серверного кода, за исключением значения скрытого тега <input>, с помощью которого через свойства веб-части передается номер подразделения для начальной фильтации.
<input type="hidden" id="divisionNumberFromWebpart" value='<%=DivisionNumber %>'/>
<table id="phoneBookGrid"></table>
<div id="phoneBookPager"></div>
<div id="employeeInfoContainer">
...
</div>
* This source code was highlighted with Source Code Highlighter.
Сервис, предоставляющий данные, имеет следующий интерфейс.
[ServiceContract]
public interface IPhoneBookService
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json)]
JqGridResult<Employee> GetAllRecordsForJqGrid();
[OperationContract]
[WebInvoke(Method = "GET")]
Stream GetPhoto(int employeeId);
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json)]
Employee GetRecord(int employeeId);
}
* This source code was highlighted with Source Code Highlighter.
JqGridResult – класс, возвращаемый методом GetAllRecordsForJqGrid и принимаемый reader’ом jqGrid.
[DataContract]
public class JqGridResult<T>
{
[DataMember]
public int CurrentPage { get; set; }
[DataMember]
public int TotalPages { get; set; }
[DataMember]
public int TotalRecords { get; set; }
[DataMember]
public List<T> Records { get; set; }
}
* This source code was highlighted with Source Code Highlighter.
Код сервиса разворачивается в GAC вместе с остальными сборками wsp-решения для SharePoint, svc-файл веб-сервиса помещается в подпапке папки c:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\ вместе с файлом web.config, в котором необходимо указать webHttpBinding
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="IPGG.IntegrationSystem.Web.Services.PhoneBookService" behaviorConfiguration="serviceBehavior">
<endpoint address="" binding="webHttpBinding" contract="IPGG.IntegrationSystem.Web.Services.IPhoneBookService" behaviorConfiguration="endpointBehavior"/>
</service>
</services>
</system.serviceModel>
</configuration>
* This source code was highlighted with Source Code Highlighter.
Для корректной работы WCF-сервисов в приложении SharePoint, необходимо установить SPWCFSupport, либо реализовать подобный код у себя в решении. Подробности зачем это нужно – здесь.
jqGrid и jQueryUI
Содержимое таблицы phoneBookGrid (см. JqGridPhoneBookControl.ascx) формируется с помощью jqGrid, плагина к jQuery, который берет данные из WCF-сервиса, описанного выше, посредством ajax-запроса, в формате json.
$("#phoneBookGrid").jqGrid({
url: "/_layouts/IPGG.IntegrationSystem/PhoneBookService.svc/GetAllRecordsForJqGrid",
datatype: "json",
jsonReader: gridJsonReader,
colNames: columnNames,
colModel: columns,
width: 850,
height: 460,
shrinkToFit: false,
pager: "#phoneBookPager",
rowList: [20, 50, 100, 1000],
onSelectRow: GetEmployeeDetails,
loadComplete: gridLoaded
}).navGrid("#phoneBookPager", { add: false, edit: false, del: false, search: false, refresh: true }).filterToolbar();
$("#phoneBookGrid").jqGrid("navButtonAdd", "#phoneBookPager", {
caption: "Отобразить/Скрыть столбцы",
title: "Изменить порядок столбцов",
onClickButton: function() {
$("#phoneBookGrid").jqGrid("columnChooser");
}
});
$("#employeeInfoContainer").dialog({
bgiframe: true,
modal: true,
autoOpen: false,
width: 550,
resizable: false,
close: ClearDialog,
buttons: {
Ok: function() {
$(this).dialog('close');
}
}
});
* This source code was highlighted with Source Code Highlighter.
jqGrid принимает json-объект с определенными именами свойств, однако можно настроить jsonReader и передавать какой угодно json-объект. В нашем случае – это JqGridResult.
var gridJsonReader = {
root: "Records",
page: "CurrentPage",
total: "TotalPages",
records: "TotalRecords",
repeatitems: false,
id: "Id"
};
* This source code was highlighted with Source Code Highlighter.
При щелчке по строке таблицы вызывается функция GetEmployeeDetails, которая отправляет еще один ajax-запрос к веб-сервису и отображает данные в модальном диалоге jQueryUI
function GetEmployeeDetails(id) {
$("#employeeInfoContainer").dialog("open");
$.ajax({
url: "/_layouts/IPGG.IntegrationSystem/PhoneBookService.svc/GetRecord",
data: "employeeId=" + id,
success: ProcessInfo,
error: ProcessError
});
GetImage(id);
...
}
* This source code was highlighted with Source Code Highlighter.
Фотография сотрудника берется из веб-сервиса с помощью GET-запроса
function GetImage(employeeId) {
$("#employeeImage").attr("src", "/_layouts/IPGG.IntegrationSystem/PhoneBookService.svc/GetPhoto?employeeId=" + employeeId);
}
* This source code was highlighted with Source Code Highlighter.
Вместо заключения
В данной статье не приведен код, реализующий доступ к данным. Могу лишь сказать, что это простая реализация паттерна Репозиторий на Linq To SQL. Для фильтрации, поиска и постраничного вывода использован интерфейс IQueryable<T>.
Как оказалось, нет ничего сложного в реализации и развертывании решений на jQuery иWCF под SharePoint.
Однако, если SharePoint работает на IIS7, то может возникнуть проблема, на решение которой мне пришлось потратить некоторое время.
После развертывания решения на production-сервере, выдавалась 404 ошибка при запросах на URL типа “~/_layouts/IPGG.IntegrationSystem/PhoneBookService.svc/GetRecord”.
Решение состоит в том, что необходимо зарегистрировать обработчник запросов для .svc. По умолчанию, в IIS7 для SharePoint-сайта этого не сделано.

P.S. Спасибо хабраюзерам Atreides07 и Atv за помощь в освоении Хабра.
UPD: Перенес в блог «Веб-разработка».
UPD2: Пост в моем блоге