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

Конструктивный подход к разработке SharePoint

Моя задача в этой статье – предложить читателям конструктивные методики, используемые при разработке SharePoint. Учитывая, что Office 365 постоянно обновляется, полезно быть в курсе событий, чтобы иметь возможность вовремя проверять работу приложений в изменившихся условиях. Это позволит оповещать пользователей о том, что сервис был обновлен, а приложения проходят тестирование. Если же приложение вышло из строя в результате обновления, ваша осведомленность позволит уменьшить число обращений в службу поддержки, а также объем экстренных мер, принимаемых в подобных случаях. Конечным результатом станет экономия средств (за счет сокращения числа обращений в службу поддержки), снижение уровня стресса и реализация продуманного подхода к работе в условиях постоянно развивающейся «облачной» среды. Рассмотрим один из способов достижения такого результата.

Приложение SharePoint для контроля версий Office 365

Я создал небольшое приложение SharePoint, которое проверяет версию Office 365 при каждом запуске и сохраняет номер версии в списке SharePoint, который является частью сайта приложения. Обнаружив новую версию, приложение регистрирует ее в списке и генерирует сообщение об изменении номера версии с момента последнего запуска приложения. Если версия не менялась, то приложение просто сообщает об этом.

Возникает вопрос: как обеспечить запуск приложения с частотой, достаточной для обеспечения осведомленности об изменениях версии? Для этого требуется создать плановое задание запуска приложения SharePoint в веб-браузере на регулярной основе.

Как узнать об обновлении? Еще проще: подпишитесь на оповещения об изменениях списка версий, и вы будете получать сообщение по электронной почте всякий раз, как только версия будет зарегистрирована в списке.

На рисунке 1 приведена схема, иллюстрирующая этот процесс.

 

Схема проверки появления обновлений
Рисунок 1. Схема проверки появления обновлений

Как выглядит приложение SharePoint, которое я назвал «O365 SP Version Checker», во время рабочего цикла, показано на рисунке 2.

Рассмотрим подробнее информацию, выводимую приложением.

Приложение O365 SP Version Checker

С областью клиента SharePoint в Office 365 связаны три номера версий (по крайней мере, я обнаружил именно столько): Extender, Build и SPProductNumber. Приложение сначала определяет URL сайта приложения и добавляет _vti_pvt/service.cnf и _vti_pvt/buildversion.cnf suffixes для проверки версий Extender и Build, соответственно. На рисунке 2 адрес URL моей области клиента скрыт. Номер продукта (SPProductNumber) получен из строки запроса (QueryString).

 

Результаты работы O365 SP Version Checker
Рисунок 2. Результаты работы O365 SP Version Checker

Затем приложение вызывает два URL-адреса и выводит необработанные данные с информацией о версиях Extender и Build. На основе анализа необработанных полезных данных приложение определяет номера версий. Определив их, приложение запрашивает список версий. Если номера не зарегистрированы в списке, приложение регистрирует их и генерирует сообщение об обнаружении и регистрации новой версии.

Наконец, приложение выдает ссылку для перехода к списку версий, где можно проследить историю обновлений арендуемой службы Office 365. Список выглядит так, как показано на рисунке 3.

 

Список версий Office 365
Рисунок 3. Список версий Office 365

Приведенный список включает шесть записей. Первые три, «созданные 6 минут назад», я добавил вручную для целей тестирования. Первые три записи содержат номера, соответствующие номерам локальной версии SharePoint, которую я использовал для создания приложения. Эти номера версий приведены также для того, чтобы обратить ваше внимание на интересный факт: на локальном сервере разработки номера SPProductNumber и Build идентичны, тогда как у моей области клиента Office 365, с которой был сделан этот снимок экрана, они различаются.

На рисунке 4 видно, что после возврата на страницу приложения, проверяющего и регистрирующего версии, приложение сообщает о том, что новые версии не были обнаружены.

 

Сообщение о том, что новые версии не были обнаружены
Рисунок 4. Сообщение о том, что новые версии не были обнаружены

Приложение скоро будет доступно на SharePoint Marketplace. А теперь я расскажу вам о том, как работает код.

Как работает код

Посмотрим еще раз на схему, иллюстрирующую логику приложения, и подробно остановимся на каждом из ее элементов.

Сначала приложение определяет URL сайта приложения и добавляет _vti_pvt/service.cnf и _vti_pvt/buildversion.cnf suffixes для проверки версий Extender и Build, соответственно. Получение номера продукта (SPProductNumber) осуществляется через строку запроса (QueryString). Как это выглядит на языке JavaScript, показано в листинге 1. Заметим, что разбор URL, когда вы работаете с приложением в своей среде разработки, несколько отличается от случая, когда приложение развертывается в области клиента Office 365.

Затем приложение вызывает два URL-адреса и выводит необработанные данные с информацией о версиях Extender и Build. На основе разбора необработанных полезных данных приложение определяет номера версий. Как это выглядит на языке JavaScript, показано в листинге 2.

Следует отметить некоторую ненадежность этого шаблона разбора в деле определения фактического номера версии по полезным данным, возвращаемым службой, поскольку данный разбор основан на строковом парсинге. Я попробовал преобразовать возвращаемые службой полезные данные в JSON и XML с помощью атрибута ‘тип данных’ AJAX, но это не привело к желаемому результату, поэтому я буду продолжать доработку кода, чтобы сделать его менее уязвимым, и опубликую соответствующие обновления.

Определив номера, приложение запрашивает список версий. Если номера не зарегистрированы в списке, приложение регистрирует их и генерирует сообщение об обнаружении и регистрации новой версии. Я создал три варианта функции JavaScript, выполняющей эту операцию. Каждый вариант функции JavaScript предназначен для извлечения одного из трех номеров версии (Extender, Build, SPProductNumber). Для краткости здесь приведена только та функция, которая извлекает номера SPProductNumber из списка версий, см. листинг 3.

Код не содержит ничего необычного; с использованием клиентской объектной модели (CSOM) он осуществляет запрос CAML к списку версий и регистрирует соответствующие функции делегата в случае успеха и отказа. Если вам никогда не приходилось создавать запросы CAML, и даже если вы создаете их постоянно, я рекомендую вам загрузить инструмент CAML Designer (www.camldesigner.com/). Это сэкономит массу времени и позволит тестировать запросы, прежде чем помещать их в приложения SharePoint.

Функция-делегат JavaScript onSpProductNumberQuerySucceededsuccess оценивает, был ли зарегистрирован номер версии. Если версия еще не зарегистрирована, то вызывается функция addVersionListItem для занесения обнаруженного номера в список версий. Если версия уже зарегистрирована, на графическом экране появляется соответствующее сообщение, см. листинг 4.

Я еще не пробовал упростить логику разбора, но уверен в том, что это можно сделать, если вместо API CSOM для запроса к списку версий использовать API REST. Я планирую переработать код, чтобы в этом убедиться. Если API REST действительно упрощает разбор результатов, соответствующее обновление будет опубликовано.

Листинг 1. Получение номера продукта

context = SP.ClientContext.get_current();
web = context.get_web();
appWebUrl =
decodeURIComponent(
getQueryStringParameter(«SPAppWebUrl»)
);
var appWebSlashLocation = appWebUrl.lastIndexOf(«/");
spProductNumber =
decodeURIComponent(
decodeURIComponent(getQueryStringParameter(»SPProductNumber«))
);
//O365 Trim Logic
trimmedAppWebUrl = appWebUrl.substring(0,
appWebUrl.lastIndexOf(».sharepoint.com«) + 15);
//Local Developer Site Trim Logic
trimmedAppWebUrl = appWebUrl.substring(0,
appWebUrl.lastIndexOf(»/«));
extenderVersionUrl = trimmedAppWebUrl +»/_vti_pvt/service.cnf«;
buildVersionUrl = trimmedAppWebUrl +»/_vti_pvt/buildversion.cnf«;

Листинг 2. Вывод информации о версиях Extender и Build

getVersion(»extender«, extenderVersionUrl);
getVersion(»build«, buildVersionUrl);
function getVersion(type, url) {
var request = $.ajax({
url: url,
type: 'GET',
datatype: 'text'
});
request.done(function (data) {
var properties = data.split(»\r\n«);
for (t = 0; t < properties.length; t++) {
if (properties[t].lastIndexOf(»vti_«+ type +»version:«) == 0) {
versionlocation = properties[t].lastIndexOf(»vti_«+ type +
»version:«);
if (type ==»extender«) {
extenderVersion = properties[t].substring(versionlocation +
20);
retrieveExtenderVersionHistory(extenderVersion);
}
else {
buildVersion = properties[t].substring(versionlocation + 17);
retrieveBuildVersionHistory(buildVersion);
}
break;
}
}
if (type ==»extender") {
$('#extenderData').text(data);
$('#extenderVersion').text(extenderVersion);
}
else {
$('#buildData').text(data);
$('#buildVersion').text(buildVersion);
}
});
request.fail(function (msg) {
$('#error').append('
Error Status: ' + msg.status +
'
Error Status Text: ' + msg.statusText + '
');
});
}

Листинг 3. Извлечение номера SPProductNumber из списка версий

function retrieveSPProductNumberVersionHistory(version) {
var versionsList = context.get_web().get_lists().getByTitle('Versions');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(
'\'VersionType\'/>' +
'\'Choice\'>SPProductNumber
\'Title\'/>' +
'\'Text\'>' + version + '
100'
);
spProductNumberVersionsListItems = versionsList.getItems(camlQuery);
context.load(spProductNumberVersionsListItems,
'Include(Title, VersionType)');
context.executeQueryAsync(
Function.createDelegate(this, this.onSpProductNumberQuerySucceeded),
Function.createDelegate(this, this.onQueryFailed)
);
}

Листинг 4. Функция проверки регистрации версии

function onSpProductNumberQuerySucceeded(sender, args) {
var spProductNumberListItemInfo = '';
var spProductNumberEnumerator =
spProductNumberVersionsListItems.getEnumerator();
while (spProductNumberEnumerator.moveNext()) {
var spProductNumberListItem = spProductNumberEnumerator.get_current();
spProductNumberListItemInfo += '\nVersion: ' +
spProductNumberListItem.get_item('Title') +
'\nType: ' + spProductNumberListItem.get_item('VersionType');
}
if (spProductNumberListItemInfo == '') {
addVersionListItem('SPProductNumber', spProductNumber);
}
else {
$('#spProductNumberAlert').append(
'
SPProductNumber version has not changed.
');
$('#spProductNumberAlert').addClass('green');
}
}