SharePoint и Ajax

Как вы думаете, сколько способов сделать ajax запрос в SharePoint? А без jQuery и дополнительных библиотек? Нет, XMLHttpRequest руками писать на надо.

Sys.Net.WebRequest \ Sys.Net.WebRequestExecutor

Эти классы находятся в библиотеке MicrosoftAjax. Она подгружается на каждой странице SharePoint и вы можете использовать её на своих страницах.

Класс Sys.Net.WebRequest описывает параметры запроса, а Sys.Net.WebRequestExecutor, вернее его наследник, выполняет запрос с указанными параметрами. Можно создавать свои реализации WebRequestExecutor, например для тестирования или для проксирования запросов.

Недостаток этих классов в чрезмерной многословности.

var request = new Sys.Net.WebRequest();         
request.set_url(url);
request.set_httpVerb("GET");         
request.add_completed(function(executor, eventArgs) {             
    if(executor. get_responseAvailable()) {
        //do stuff
    }
});

request.invoke(); 

Вот так придется писать на каждый запрос. Семантически похоже на то, что есть в C#, но после jQuery выглядит страшно. Тем не менее этот api является базовым для всего, что работает в SharePoint.

Ссылки на MSDN:
Sys.Net.WebRequest - http://msdn.microsoft.com/en-us/library/bb310979(v=vs.100).aspx
Sys.Net.WebRequestExecutor- http://msdn.microsoft.com/en-us/library/bb397434(v=vs.100).aspx

SP.PageRequest

Этот класс находится в библиотеке sp.js и доступен с SharePoint 2010.

У нем есть два статических метода doGet и doPost.

SP.PageRequest.doGet(
    url,'application/json', 
    function(o, args) {
        var webRequestExecutor = args.get_executor();
        //do stuff
    }, 
    function(o, args) {
        //args.get_errorMessage();
    });

Аналогично работает метод doPost, который может также передавать body.

Огромный недостаток этих методов в том, что они не позволяют указать загловки запроса. А второй параметр, который называется expectedContentType, сравнивается с Content-Type результата и выкидывает исключение при несовпадении.

Конечно можно создать экземпляр SP.PageRequest, из него получить объект Sys.Net.WebRequest, и через него выставить заголовки. Но это будет еще более многословно, чем при использовании Sys.Net.WebRequest напрямую.

Но есть и преимущество. SP.PageRequest обрабатывает RequestDigest, что поможет делать запросы к SharePoint.

Если вы не знаете, то в SharePoint  в post запросе к странице нельзя делать Update, если не передан корректный Request Digest. Аналогично нельзя обращаться к REST эндпоинту для изменения данных без RequestDigiest. Это должно защищать от CSRF атак.

Документация по SP.PageRequest - http://msdn.microsoft.com/en-us/library/ee547454(v=office.14).aspx

SP.WebProxy

Этот класс также находится в sp.js. В отличие от предыдущих, его предназначение – делать кросс-сайтовые запросы. К сожалению это доступно только для приложений, из обычного скрипта воспользоваться этим классом не получится.

Кстати он не менее многословен, чем Sys.Net.WebRequest, так что у вас и желания не будет его использовать для всего подряд.

var context = SP.ClientContext.get_current();
var request = new SP.WebRequestInfo();
request.set_url(
    "http://services.odata.org/Northwind/Northwind.svc/Categories"
    );
request.set_method("GET");

request.set_headers({ "Accept": "application/json;odata=verbose" });
var response = SP.WebProxy.invoke(context, request);
context.executeQueryAsync(successHandler, errorHandler);

function successHandler() {
    if (response.get_statusCode() == 200) {
        //Do stuff
    }
}

Но и это еще не все. Чтобы код выше заработал надо в манифест приложения вписать следующий элемент:

<RemoteEndpoints>
    <RemoteEndpoint Url=" http://services.odata.org" />
</RemoteEndpoints>

Подробно о применении класса SP.WebProxy по ссылке - http://msdn.microsoft.com/en-us/library/fp179895.aspx

SP.RequestExecutor

Этот класс находится в отдельном файле sp.requestexecutor.js, может работать как в SharePoint, так и вне его.

По-умолчанию на страницы SharePoint он не загружается и чтобы его использовать надо написать такой код:

SP.SOD.registerSod('sp.requestexecutor.js',
            '/_layout/15/sp.requestexecutor.js');
SP.SOD.executeFunc('sp.requestexecutor.js', 
            'SP.RequestExecutor', 
            function() {
                //Do stuff
            });

Если у вас html страница в SharePoint, то можете просто подключить скрипт на страницу и не использовать Script On Demand.

Использовать SP.RequestExecutor гораздо проще, чем предшествующие варианты:

var re = new SP.RequestExecutor(targetSiteUrl);
re.executeAsync({
    url: targetUrl,
    method: 'GET',
    success:function(response) {
        //console.log(response.body);
        //do stuff
    }
});

Также, как и SP.PageRequest, SP.RequestExecutor автоматически отправляет RequestDigiest. Так что руками передавать его, как написано во многих примерах, не обязательно.

SP.RequestExecutor может работать с бинарными данными, но из-за ошибки надо устраивать пляски с бубуном чтобы все заработало - http://techmikael.blogspot.ru/2013/07/how-to-copy-files-between-sites-using.html

В зависимости от того какой url вы передали в конструктор SP.RequestExecutor будет иметь разное поведение.

  • Если hostname в targerSiteUrl совпадает с hostname текущей страницы, то будут обычные ajax запросы.
  • Если не совпадает, то RequestExecutor будет пытаться отправлять запросы через AppProxy. Этот сценрий будет работать если у вас страница находится в SharePoint App.

Кроме того, можно передать вторы параметром в конструктор адрес хендлера, который использует класс Microsoft.SharePoint.Client.RequestForwarder (ни разу не видел чтобы им кто-либо пользовался). Это позволит обращаться к серверу SharePoint из javascript, расположенного на внешнем сайте и не использующем модель приложений. Ранее я про эту возможность писал в статье http://gandjustas.blogspot.com/2012/03/silverlight-sharepoint-2.html.

Кстати Microsoft.SharePoint.Client.RequestForwarder использует класс HttpContext, который создает ссылку на сборку System.Web. Из-за этого сборку Microsoft.SharePoint.Client нельзя использовать в Windows 8 приложениях.

Примеры SP.RequestExecutor по ссылке - http://msdn.microsoft.com/en-us/library/jj164022.aspx

SP.ProxyWebRequestExecutor и SP.ProxyWebRequestExecutorFactory

Эти два класса дополняют SP.RequestExecutor. SP.ProxyWebRequestExecutor является наследником Sys.Net.WebRequestExecutor и использует SP.RequestExecutor для выполнения запросов. Вы можете использовать его в любом коде, работающим с Sys.Net.WebRequest (интересно есть у вас такой), например чтобы заработало в Apps.

Класс SP.ProxyWebRequestExecutorFactory необходимо использовать если вы собираетесь использовать JSOM на не-SharePoint страницах.

var factory = new SP.ProxyWebRequestExecutorFactory(targetUrl);
var ctx = new SP.ClientContext(targetUrl);
ctx.set_webRequestExecutorFactory(factory);

Документации по этим классам нет, но в проект SPTypeScript я недавно залил дефинишены (описания типов) для всех полезных классов sp.requestexecutor.js  - http://sptypescript.codeplex.com/SourceControl/latest#Definitions/SP.RequestExecutor.d.ts
API небольшое и понять семантику несложно.

Рекомендации

Если вы делаете javascript для работы с SharePoint на страницах SharePoint, то используйте JSOM. Она достаточно мощная в 2013 и покрывает почти все сценарии. Для обращения к таким вещам как ExcelRest используйте SP.PageRequest.

Если вы делаете не-SharePoint, или в других сложных случаях, страницы используйте SP.RequestExecutor и JSOM с помощью SP.ProxyWebRequestExecutorFactory.

Если вас пугает многословный API для JSOM, то используйте TypeScript и дефинишены из проекта http://sptypescript.codeplex.com/, они почти полностью покрывают JSOM.

А как же jQuery?

В SharePoint 2013 почти не нужно. Ajax запросы можно делать и без нее, а для манипуляций с DOM есть библиотека mQuery. Про анимацию в SharePoint 2013 напишу в следующий раз.



Облачная vs on-premise экономика

Последнее время очень активно развиваются облачные платформы и все сильнее возрастает потребность развертывания новых решений. При этом многие продолжают планировать и проектировать облачные решения так, как-будто они будут размещены на собственном железе. Хотя основное преимущество облака, по сравнению с обычным хостингом, это  эластичность, именно про него люди почему-то забывают.

Как планируются решения on-premise

Кстати on-premise переводится как “на предприятии”, что означает использование собственных ресурсов, вместо арендованных.

Основная проблема при использовании своих ресурсов – их ограниченность в настоящее время. Даже если взять крупную организацию, со своим датацентром, то обычно ресурсы этого ДЦ не простаивают, и уже применяются для приложений и сервисов. Таким образом развернуть еще одну систему для нескольких тысяч сотрудников, без влияния на другие системы, практически невозможно.  Всяческие ухищрения, связанные с виртуализацией, не сильно помогают, так как создает конкуренцию за реальные ресурсы, особенно процессоры и диски. Для ДЦ необходимо иметь большой резерв реальны физических ресурсов чтобы можно было быстро разворачивать новые системы.

Для организаций, не имеющих мощного ДЦ, эта проблема еще более актуальна. Развертывание новых серверов может занимать от нескольких недель до нескольких месяцев.

Поэтому для on-premise решения очень важно заранее заложить достаточное количество ресурсов, чтобы оно не рухнуло под нагрузкой. При этом надо постоянно держать в уме, что увеличение количества ресурсов в два раза потребует два месяца.

Такой подход влияет как на сайзинг (планирование железа), так и на архитектуру решений, так и на лицензии программного обеспечения.

Почему в облаке это неправильно

Как я писал выше, основное преимущество облака – эластичность. Можно добавить или убрать серверы в несколько кликов. Более того, возможны сценарии автоматического масштабирования под нагрузкой. Это в корне меняет подход к планированию и архитектуре.

Если вы в облаке заранее резервируете в 10 раз больше ресурсов, чем необходимо сейчас, то вы платите в 10 раз больше, хотя по факту большая часть ресурсов простаивает. Тоже самое касается лицензий на ПО. Нет смысла покупать 8 процессорных лицензий для SQL Server за несколько миллионов, если ваше решение сможет три месяца бегать на двух процессорах, а еще девять месяцев на четырех. Суммарные затраты на три года аренды лицензий и серверов в Azure будут ниже стоимости одних лицензий для on-premise.

Что касается архитектуры решений, то в облаке гораздо важнее не максимизировать скорость работы, грубо говоря латентность системы, а улучшать масштабируемость, чтобы пропускная способность росла линейно с увеличением количества серверов. Причем важна также масштабируемость “вниз”, чтобы система не потребляла деньги когда ничего не делает.

Как работает облачная экономика

В облаке всегда начинайте с малого. Вам не нужно три воркера и распределенный кеш, вам не нужно 4 WFE для SharePoint, вам не нужна A7 виртуалка с SQL Server. Скорее всего вы можете обойтись в 2-3 раза меньшими ресурсами. Если вы в начале пути, создаете новое веб-приложение или разворачиваете ферму SharePoint – выделяйте минимум ресурсов.

Когда ваше решение начинает масштабироваться – следите за профилем нагрузки. Нет смысла все время держать кучу воркеров, если они ничего не делают. Отключайте виртуалки, если средняя загрузка ниже 50%, а всплески не поднимают её выше 75%. Добавляйте новые диски в виртуалку и SQL Server, так как именно в IO упирается производительность SQL, а платите вы все равно за место.

Когда ваше решение получит более-менее стабильный профиль нагрузки, тогда занимайтесь оптимизацией. Нет смысла оптимизировать облачное решение, когда вы платите за него 10,000 рублей в месяц. Даже если вы сэкономите 10%, то это будет незначительная сумма. Когда ваше решение будет стоить 100,000 в месяц – уже стоит задуматься об оптимизации.

При всех расчетах стоимости обязательно надо учитывать такой “жизненный цикл” облачных решений, а не подбирать фиксированную конфигурацию, с которой можно выдерживать требуемую нагрузку.

Когда перестает работать облачная экономика

Кроме чисто экономических факторов развертывания решений в облаке есть политико-экономические. Например все купленные железки и лицензии являются активами компании, которые увеличивают капитализацию. А все арендованное является обязательствами и стоимость компании фактически снижается. Кроме того есть еще циклы бюджетирования в организациях, которые приводят к необходимости потратить деньги в определенном периоде. Эти факторы делают закупку железа для организаций более выгодной, чем использование облаков.

Ну и как обычно очень остро стоит вопрос доверия к облакам, особенно в свете последних разоблачений. Но этот вопрос за рамками данной статьи.

Заключение

Статья получилась в стиле Капитана “Очевидность”, но я каждый день сталкиваюсь с непониманием сути облачной экономики. Может кто-нибудь найдет эту статью, прочитает и начет думать по-другому.



SPTypeScript 1.1

Рад сообщить, что недавно был выпущен новый релиз SPTypeScript версии 1.1

Основные нововведения

Совместимость с TypeScript 0.9

TypeScript 0.9 имеет несколько ломающих изменений, которые коснулись большинства описаний библиотек, в том числе SPTypeScript. В релизе 1.1 все определения совместимы с TypeScript 0.9

Применение generic-типов для коллекций

SharePoint в JSOM использует аналог типов IEnumerable\IEnumerator из языка C#. Ранее это приводило к слаботипизированному и громоздкому коду. В новом релизе добавлены типы параметры в интерфейсы IEnumerable\IEnumerator и все типы-коллекции. Теперь код обработки коллекций стал более типизированным, но не менее громоздким.

image

Добавлены описания типов для mQuery

Ранее я писал про библиотеку mQuery в SharePoint 2013. SPTypeScript 1.1 включает полное описание этой библиотеки.

Пример использования:

SP.SOD.executeFunc("mQuery.js","m$", function() {
    m$.ready(function() {
        var d = new Date();
        var month = d.getMonth();
        var date = d.getDate();
        var year = d.getFullYear();
        m$('#pageTitle').append("<div style='float:right'>" + month + "/" + date + "/" + year + "</div>"); 
    }); 
});

Также в проекте SPTypeScript вы найдете пример использования mQuery.

Добавлены описания для класса SPClientAutoFill

Этот класс реализует функциональность autocomplete (typeahead) и имеет очень простой API, доступный в любом решении для SharePoint.

image

В прошлом посте я описывал применение этого визуального компонента. В проект включен пример использования.

Добавлены примеры кастомизации форм

  • Добавление вкладок на форме элементов списка
  • Кастомное поле списка с валидаторами
  • Кастомизация обычного lookup поля с помощью поиска и SPClientAutoFill

Кроме того

  • Добавлены описания классов SP.Utilities
  • Улучшены описания типов Client Side Rendering и SP.UI

Доступность на NuGet

Самое главное улучшение совершенно не связано с определениями или примерами. SharePoint.d.ts теперь доступен в NuGet http://nuget.org/packages/sharepoint.TypeScript.DefinitelyTyped/

Достаточно в Package Manager консоли выполнить команду:
PM> Install-Package sharepoint.TypeScript.DefinitelyTyped

Заключение

Качайте дефинишены, используйте TypeScript, задавайте вопросы, пишите замечания  и пожелания. Ваши фидбек очень важен для нас.



Автокомплит в SharePoint 2013: пошаговое руководство

Данный функционал был найден Антоном Вишняковым и описан в посте в его блоге. Большая часть контента данного поста взята из его блога с его разрешения.

Вы уже видели новый элемент управления для выбора людей в SharePoint 2013? Этот элемент управления был сильно улучшен по сравнению с предыдущей версией SharePoint. Теперь этот элемент можно использовать в клиентском коде и спокойно внедрять на своих страницах. Кроме того появился отличный функционал автозаполнения.

070713_0434_LeveragingS1[1]

Замечательная новость: вы можете использовать функционал автозаполнения с любым текстовым полем. Эту функцию реализует контрол  SPClientAutoFill. Он находится в файле autofill.js в _layouts/15. Давайте посмотрим как сделать решение с его помощью.

Шаг первый: разметка и стили

Для того чтобы использовать SPClientAutoFill вам потребуется следующая разметка:

<div style='position: relative;'>
    <input type='text' id='autofillElement' />
    <div class='sp-peoplepicker-autoFillContainer' 
            id='autofillContainer' />
</div>

Обязательно position:relative для внешнего элемента, иначе будут проблемы с позиционированием подсказок.

Второй шаг: подключение библиотеки

По-умолчанию autofill.js не зарегистрирован как Script On Demand. Зарегистрировать скрипт можно тремя способами:

  • Вызовом функции SP.SOD.registerSod в JavaScript
  • Серверным контролом <SharePoint:ScriptLink /> на странице
  • В свойстве JSLink

Внимание. Не рекомендуется добавление непосредственно тега script в разметку в SharePoint.

Далее необходимо загрузить autofill.js с помощью Script On Demand.

SP.SOD.executeFunc('autofill.js', 'SPClientAutoFill',
   function() {
       //Do work here
   });

Шаг третий: логика autocomplete

//Создание контрола
var autofill = new SPClientAutoFill('autofillElement', 
                                    'autofillContainer',
                                    OnPopulate);


Функция OnPopulate принимает параметр – элемент input, к которому привязан контрол.

Внутри функции необходимо сформировать массив объектов, описывающих элементы подсказки, и вызвать SPClientAutoFill.prototype.PopulateAutoFill.

Элементы могут иметь один из 4 типов:

  • SPClientAutoFill.MenuOptionType.Option – элемент выбора, для него необходимо указывать ключ и отображаемый текст.
  • SPClientAutoFill.MenuOptionType.Separator – Разделитель групп.
  • SPClientAutoFill.MenuOptionType.Footer – Футер, содержащий текст.
  • SPClientAutoFill.MenuOptionType.Loading – Индикатор загрузки, нужен в случае загрузки элементов с сервера.
function OnPopulate(el) {
    var result = [];
    var item = {}
 
    item[SPClientAutoFill.KeyProperty] = 'key';
    item[SPClientAutoFill.DisplayTextProperty] = 'Title';
    item[SPClientAutoFill.MenuOptionTypeProperty] = SPClientAutoFill.MenuOptionType.Option;
    result.push(item);

    item = {};
    item[SPClientAutoFill.DisplayTextProperty] = 'Footer';
    item[SPClientAutoFill.MenuOptionTypeProperty] = SPClientAutoFill.MenuOptionType.Footer;
    result.push(item);

    SPClientAutoFill.GetAutoFillObjFromInput(el).PopulateAutoFill(result, OnSelect);
}

Шаг четвертый:  логика выбора элемента

Функция OnSelect из предыдущего блока кода принимает два параметра – id элемента input и выбранный элемент autocomplete.

Так как выбрать можно только элементы типа SPClientAutoFill.MenuOptionType.Option, то у вас обязательно есть ключ и отображаемый текст выбранного элемента.

function OnSelect(targetInputId, item) {
    var targetElement = document.getElementById(targetInputId);
    targetElement.value = item[SPClientAutoFill.DisplayTextProperty];
}

Шаг пятый: изучение API и TypeScript

Полное описание класса SPClientAutoFill для TypeScript есть в проекте SPTypeScript и доступно по ссылке: https://sptypescript.codeplex.com/SourceControl/latest#Definitions/autofill.d.ts

Кроме того в проекте SPTypeScript есть примеры использования SPClientAutoFill и кастомизации поля на форме элемента списка с помощью этого класса.



Применение mQuery в SharePoint 2013

Нет, в заголовке не опечатка. Я действительно имею ввиду mQuery, а не jQuery. mQuery – аналог библиотеки jQuery, включенный в состав SharePoint 2013. Как обычно эта библиотека недокументированна и известна очень малому количеству людей. Исследовал её Антон Вишняков и опубликовал обзор с примером в своем блоге.

Что такое mQuery

Группа разработчиков SharePoint нашла критический недостаток в jQuery – её написали не они. И поэтому решили сделать свой jQuery с пасьянсом и куртизанками. Это конечно шутка.

Правда заключается в том, что jQuery – фактически стандарт для веб-разработки, но её довольно проблематично использовать в SharePoint. Часто возникают конфликты с другими расширениями, которые тоже хотят использовать jQuery других версий. Кроме того переопределение переменной $ в клиентском скрипте ломает Asset Picker и еще некоторые функции в SharePoint 2010 (в 2013 тоже ломается asset picker).

Поэтому было решено включить подмножество jQuery в SharePoint 2013, которое назвали mQuery. Точка входа – переменная m$, доступное API во многом повторяет jQuery, но есть отличия.

mQuery-intellisence[1]

Вы спросите – а почему было не включить саму библиотеку jQuery в SharePoint? Увы, ответа на этот вопрос я не знаю, как и не знаю планов на будущее mQuery.

Как использовать mQuery

На всех страницах SharePoint 2013 библиотека mQuery уже присутствует как OnDemand, то есть загружаемая о требованию. Для использования mQuery надо написать такой код:

SP.SOD.executeFunc('mQuery.js', 'm$', function() {
    // DO STUFF
}

Если вы разрабатываете с использованием TypeScript, то можете взять определения из проекта SPTypeScript, и получите не только intellisence в студии, но и проверку типов при компиляции.

Если же пишете JavaScript в VisualStudio, то в начале js файла добавьте строку:

/// <reference path=”C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\mQuery.debug.js” />

Далее вы можете использовать знакомые методы

//Код выполняется после загрузки страницы
m$.ready(function() { }) 

//Создание обработчика событий для списка
m$("selector").click(function (e) { }) 

//Сокрытие элементов
m$("selector").css("display", "none") 

//и многое другое

Полный перечень функций можно посмотреть тут.

Ограничения mQuery

  • Работает только на страницах SharePoint (использует некоторые особенности SharePoint и MicrosoftAjax).
  • Не содержит множество shortcut-функций, например отсутствуют функции show и hide, text, val.
  • Не содержит функцию ajax и производные, предполагается использование JSOM для общения с сервером.
  • Не содержит анимацию, в SharePoint 2013 своя библиотека для анимации.
  • Нет интерфейса плагинов.
  • Нет возможности конструирования html.

Пример

Антон Вишняков в своем блоге сделал пример использования mQuery. В примере создается динамическая таблица, в которой можно добавлять и удалять строки.

Выглядит примерно так:

image

Этот пример я сконвертировал в app на TypeScript и вы можете скачать его и запустить в Office 365, не имея установленного SharePoint на вашем компьютере.

Этот пример (и множество других) можно скачать с сайта проекта SPTypeScript.

Заключение

Очень рекомендую ознакомиться с возможностями mQuery, возможно вам не придется тащить в своих решениях jQuery и бороться с проблемами совместимости.

Также рекомендую подписаться на блог Антона и начать использовать TypeScript для проектов на SharePoint.