Полгода прошло с тех пор как я опубликовал первую часть статьи. Как раз полгода назад произошло смещение приоритетов веб-разработки Microsoft  в сторону HTML5, и злые языки начали пророчить скорую смерть Silverlight. Тем не менее вышла пятая версия SL и, скорее всего, будет еще и шестая. Тем не менее фокус больше смещается в сторону HTML\JS, и по слухам в следующей версии SharePoint будет гораздо больше javascript и гораздо меньше SL.

Поэтому данная статья будет больше обзорной.

Варианты развертывания Silverlight приложений

Как обычно их три:

  1. В sandbox решении в виртуальную файловую систему sharepoint.
  2. В fulltrust решении в физическую файловую систему.
  3. Приложение на внешнем сервере, с использованием fluid application model.
Размещение Silverlight на портале.

Самый часто используемый способ – веб-часть silverlight. Её можно найти в категории Media. Также возможно создание своих веб-частей, которые отображают silverlight приложение, чтобы передать дополнительные параметры и\или сделать fallback. Но лучше такие веб-части не создавать, а воспользоваться расширением для visual studio. А в следующей версии visual studio такая веб-часть доступна “изкаропки”.

Еще один вариант – создание field control на silverlight. Пример можно посмотреть в статье на msdn.

Демо-приложение

Для демонстрации всех способов развертывания напишу простое приложение, при запуске оно будет выводить Title узла, на котором запущено приложение.

Сначала надо добавить в проект SL сборки Microsoft.SharePoint.Client.Silverlight  и Microsoft.SharePoint.Client.Silverlight.Runtime. Их можно найти в папке {SharePointRoot}\TEMPLATE\LAYOUTS\ClientBin.

Теперь можно написать немного кода:

var ctx = ClientContext.Current;
var web = ctx.Web;
ctx.Load(web, w => w.Title);

ctx.ExecuteQueryAsync(
(o, args) => //success
{
    Dispatcher.BeginInvoke(() =>
    {
        tbTitle.Text = web.Title;
    });

},
(o, args) => //failure
{
    Dispatcher.BeginInvoke(() =>
        {
            MessageBox.Show(args.Exception.ToString());
        });
});

 

Для тех кто не знаком с клиентской объектной моделью SharePoint краткий ликбез. Точка входа в клиентскую объектную модель – класс ClientContext, в SL приложении можно получить “текущий контекст”, если приложение развернуто в SharePoint.

Все объекты в клиентской объектной модели являются”обещаниями” (promise), то есть на момент получения они не содержат значений, а только общение что значения когда-нибудь там будут.  Чтобы загрузить свойства объекта с сервера надо выполнить метод ClientContext.Load. В методе Load можно указать с помощью лямбда-выражений какие свойства загружать.

Другая особенность клиентской объектной модели заключается в в том что команды не выполняются сразу, а складываются в очередь и отправляются на сервер при вызове ExecuteQuery\ExecuteQueryAsync. Так как в SL нельзя блокировать поток UI, то воспользуемся асинхронным вариантом.

Третья особенность заключается в том что коллбеки завершения асинхронного вызова клиентской объектной модели не маршалятся в поток UI (сколько непонятных слов, сам в шоке). Поэтому надо вызывать Dispatcher.BeginInvoke чтобы поменять что-либо в UI или вывести Message Box.

На первый взгляд код выглядит сложным, но при некоторой сноровке пишется “на автомате”.

Развертывание на уровне фермы

Это самый простой и прямолинейный вариант. Он вполне может быть оправдан если вы используете Silverlight приложение как field control для кастомного поля, в остальных случаях я бы не рекомендовал.

Чтобы развернуть приложение в физическую файловую систему надо создать  один пустой элемент (SPI), даже не нужен манифест (Element.xml) и фича, их можно просто удалить.

image

Далее в необходимо добавить в элемент project output из проекта silverlight, обязательно указав Deployment Type=TemplateFile

image

После этого надо добавить элемент в package (не в фичу).

Последний шаг перед развертыванием – включить отладку Silverlight.

image

После этого можно жать F5 на проекте SharePoint, добавлять на страницу веб-часть Silverlight и ставить точки останова в проекте Silverlight.

image

Развертывание в sandbox

Для развертывания решения в sandbox надо выполнить те же шаги, что и для fulltrust решения, но вместо пустого элемента создать модуль, указать Deployment Type=ElementFile в project output references и использовать фичу для развертывания.

image

image

И все, в манифест модуля автоматически попадет элемент с xap файлом.

Далее как обычно: F5, веб-часть, отладка.

Развертывание на внешнем хосте

Для начала надо включить поддержку внешних приложений в SharePoint. Это можно сделать в powershell небольшим скриптом.

$cs = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$cs.ExternalApplicationSettings.Enabled = $true
$cs.Update()

Я не проверял включены ли внешние приложения в office 365, очень надеюсь что включены. Иначе я зря это все пишу.

Чтобы разместить внешнее приложение в SharePoint надо передать веб-части silverlight манифест приложения (applicationXml). При добавлении веб-части появится запрос манифеста (после включения внешних приложений).

<?xml version="1.0" encoding="utf-8" ?>
<applicationParts xmlns='http://schemas.microsoft.com/sharepoint/2009/fluidapp'>
  <applicationPart>
    <metaData>
      <applicationId>F0F0E6C1-5B42-4277-9EFF-777F1330BCD8</applicationId>
      <applicationUrl>http://localhost:21351/HostWebSite/ClientBin/SlApp.xap</applicationUrl>
      <principal>contoso\sp-app</principal>
      <sharepointRequestHandlerUrl>/HostWebSite/sp.ashx</sharepointRequestHandlerUrl>
    </metaData>
    <data>
      <webPartProperties>
        <property name='Title'>SlApp</property>
        <property name='Height'>400px</property>
        <property name='Width'>300px</property>
        <property name='MinRuntimeVersion'>3.0</property>
      </webPartProperties>
      <customProperties>
      </customProperties>
    </data>
  </applicationPart>
</applicationParts>

 

До того как создавать веб-часть необходимо дать доступ пользователю, который указан в разделе principal, с помощью метода AddApplicationPrincipal. Лучше всего это делать в коде активации фичи.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var web = properties.Feature.Parent as SPWeb;
    var principal = web.AddApplicationPrincipal("contoso\\sp-app", true, false);
    web.RoleAssignments.Add(new SPRoleAssignment(principal)
    {
        //Don't do this in podution code
        RoleDefinitionBindings = { web.RoleDefinitions.GetByType(SPRoleType.Administrator) }
    });           
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
    var web = properties.Feature.Parent as SPWeb;
    var principal = web.AddApplicationPrincipal("contoso\\sp-app", true, false);
    web.RoleAssignments.Remove(principal);
}

После деплоя решения и активации фичи можно попробовать добавить на страницу веб-часть Silverlight и указать application xml. Приложение отобразится но сразу будет падать с ошибкой.

Небольшой ликбез про кросс-доменные вызовы в silverlight. По умолчанию приложение на SL может обращаться только к тому домену, откуда оно загружено. В модели external application в sharepoint приложение silverlight находится в другом домене и ему кросс-доменные вызовы запрещены.

Иногда может помочь файл clientaccesspolicy.xml, который разрешает silvelight обращаться за пределы домена, но там надо явно прописать куда можно обращаться или указать что обращаться можно везде. Но это небезопасно, так как  в случае sharepoint приложение на Silverlight будет передавать пароль по сети для аутентификации.

Поэтому сделана такая система. В манифесте приложения указывается куда silverlight приложение будет отправлять запросы при вызове клиентской объектной модели (элемент sharepointRequestHandlerUrl). Обработчик по указанному url будет определять какой сайт sharepoint обращается и передавать логин и пароль именно для этого сайта.

Чтобы создать этот обработчик надо в сайте, где хостится silverlight создать обычный ashx-хендлер.

public class sp : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {
        RequestForwarder forwarder = new RequestForwarder(context);
        if (!String.IsNullOrEmpty(forwarder.Url))
        {
            forwarder.WebRequest.Credentials
                = new System.Net.NetworkCredential("sp-app", "P@ssw0rd", "contoso");
            forwarder.ProcessRequest();
        }
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
}

 

Класс RequestForwarder находится во “взрослой” клиентской объектной модели.

После добавления хендлера приложение перестает выдавать ошибку и работает нормально.

Заключение

Как видите довольно много возможностей развертывания  приложений Silverlight в SharePoint. При этом само приложение не поменялось ни в одном сценарии.

Теги : Silverlight, SharePoint