Интеграция надстроек с корпоративными событиями в SharePoint
Пример BusinessApps.CorporateEventsApp демонстрирует, включение централизованной системы управления корпоративными событиями в виде размещенной у поставщика надстройки, которая интегрирована в существующие бизнес приложения.
В частности, в примере BusinessApps.CorporateEventsApp показано, как внедрять веб-приложения ASP.NET, которые взаимодействуют с SharePoint, выступая как хранилище бизнес-записей. Он также демонстрирует, как включить несколько этапов бизнес-задачи в одну надстройку, размещенную у поставщика.
Этот пример приложение реализует централизованную систему управления, которая включает объекты SharePoint (списки и типы контента). Для каждого нового типа контента она создает соответствующие бизнес-объекты в веб-приложении ASP.NET. Компоненты веб-приложения запускаются как части размещенной удаленно надстройки в интерфейсе SharePoint, а также как страницах на базе исключительно удаленного веб-узла. Надстройка переопределяет используемую по умолчанию страницу приветствия для сайта SharePoint, чтобы она могла отображать брендированный пользовательский интерфейс на домашней странице сайта.
Использование примера BusinessApps.CorporateEventsApp
Настройка примера
При первом запуске примера приложения BusinessApps.CorporateEventsApp, домашняя страница предложит настроить пример. Она также предоставит ссылки на большое количество ресурсов с дополнительной информацией.
При выборе конфигурации запуска, перейдите на страницу конфигурации, как показано на приведенном ниже рисунке. При выборе опции Инициализация хранилища данных на странице конфигурации пример развертывает объекты SharePoint и образец данных, которые поддерживает пример.
После инициализации хранилища данных вы сможете вернуться на сайт, чтобы просмотреть новую приветственную страницу (страница EventsHome.aspx), которая заполнена двумя веб-частями, которые развернула надстройка, как показано на приведенном ниже рисунке. В левом столбце вы увидите четыре новых списка, установленные с помощью надстройки. Список корпоративных событий будет заполняться примером данных.
Каждая веб-часть содержит ссылки на каждое отображаемое событие, пройдя по которым, вы сможете просмотреть сведения о событии. Когда вы выбираете ссылку, страница с подробностями о событии запускается отдельно на удаленном узле, как показано на приведенном ниже рисунке. Вы можете нажать кнопку Назад на сайте на странице, чтобы вернуться на сайт SharePoint, а затем зарегистрироваться в событии.
Страница регистрации запускается отдельно на удаленном узле и содержит обратную ссылкой на хост-сайт SharePoint. Когда вы закончите регистрацию в событии, ваше имя появится в только что установленном списке регистрации в событии.
Файл Models/DataInitializer.cs
Файл Models/DataInitializer.cs содержит код, который запускается при выборе этой кнопки. Код в этом файле создает и размещает четыре новых списка SharePoint вместе с четырьмя соответствующими типами контента:
- Корпоративные события
- Регистрация в событии
- Докладчики события
- Сеансы события
Код в этом файле использует метод, похожий на тот, который используется в примере Core.ModifyPages, чтобы добавить настраиваемую страницу на сайт.
// Create default wiki page.
web.AddWikiPage("Site Pages", "EventsHome.aspx");
AddWikiPage is an extension method from the Core.DevPnPCore project to add a new page to the site. This new page also becomes the new WelcomePage for the site. It also prepares to add the web parts to this page.
var welcomePage = "SitePages/EventsHome.aspx";
var serverRelativeUrl = UrlUtility.Combine(web.ServerRelativeUrl, welcomePage);
File webPartPage = web.GetFileByServerRelativeUrl(serverRelativeUrl);
if (webPartPage == null) {
return;
}
web.Context.Load(webPartPage);
web.Context.Load(webPartPage.ListItemAllFields);
web.Context.Load(web.RootFolder);
web.Context.ExecuteQuery();
web.RootFolder.WelcomePage = welcomePage;
web.RootFolder.Update();
web.Context.ExecuteQuery();
Файл Models\DataInitializer.cs также определяет XML-код для обеих веб-частей, которые отображаются на новой странице приветствия, а затем добавляет каждую веб-часть на страницу. В следующих примерах показано, как это работает для веб-части «Избранные события».
Определение XML веб-части
var webPart1 = new WebPartEntity(){
WebPartXml = @"<webParts>
<webPart xmlns='http://schemas.microsoft.com/WebPart/v3'>
<metaData>
<type name='Microsoft.SharePoint.WebPartPages.ClientWebPart, Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' />
<importErrorMessage>Cannot import this web part.</importErrorMessage>
</metaData>
<data>
<properties>
<property name='Description' type='string'>Displays featured events</property>
<property name='FeatureId' type='System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'>3a6d7f41-2de8-4e69-b4b4-0325bd56b32c</property>
<property name='Title' type='string'>Featured Events</property>
<property name='ProductWebId' type='System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'>12ae648f-27db-4a97-9c63-37155d3ace1e</property>
<property name='WebPartName' type='string'>FeaturedEvents</property>
<property name='ProductId' type='System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'>3a6d7f41-2de8-4e69-b4b4-0325bd56b32b</property>
<property name='ChromeState' type='chromestate'>Normal</property>
</properties>
</data>
</webPart>
</webParts>",
WebPartIndex = 0,
WebPartTitle = "Featured Events",
WebPartZone = "Rich Content"
};
Добавление веб-частей на страницу
var limitedWebPartManager = webPartPage.GetLimitedWebPartManager(Microsoft.SharePoint.Client.WebParts.PersonalizationScope.Shared);
web.Context.Load(limitedWebPartManager.WebParts);
web.Context.ExecuteQuery();
for (var i = 0; i < limitedWebPartManager.WebParts.Count; i++) {
limitedWebPartManager.WebParts[i].DeleteWebPart();
}
web.Context.ExecuteQuery();
var oWebPartDefinition1 = limitedWebPartManager.ImportWebPart(webPart1.WebPartXml);
var oWebPartDefinition2 = limitedWebPartManager.ImportWebPart(webPart2.WebPartXml);
var wpdNew1 = limitedWebPartManager.AddWebPart(oWebPartDefinition1.WebPart, webPart1.WebPartZone, webPart1.WebPartIndex);
var wpdNew2 = limitedWebPartManager.AddWebPart(oWebPartDefinition2.WebPart, webPart2.WebPartZone, webPart2.WebPartIndex);
web.Context.Load(wpdNew1);
web.Context.Load(wpdNew2);
web.Context.ExecuteQuery();
В каталоге моделей вашего веб-проекта вы могли заметить, что веб-приложение MVC ASP.NET содержит четыре имени класса, которые соответствуют спискам и типам контента, которые надстройка установила:
- Event.cs (Корпоративные события)
- Registration.cs (Cобытие регистрации)
- Session.cs (Сеансы события)
- Speaker.cs (Докладчики события)
Эти четыре класса, а также их соответствующие типы контента SharePoint, вместе составляют четыре бизнес-объекта, используемых в этой надстройке.
Файл DataInitializer.cs добавляет пример данных для списка корпоративного события, создав пример объекта Событие, который соответствуют типу контента Корпоративные события, и который надстройка добавляет в список Корпоративные события.
При регистрации в событии надстройка создает объект регистрации, который соответствует типу контента регистрации в событии и который надстройка добавляет в список регистрации в событии. Для примера еще полностью не реализованы объекты Сеанса и Докладчик, поэтому надстройки и в настоящее время не поддерживают работу с ними.
В таблице ниже перечислены свойства, которые должны быть реализованы классами, которые наследуют от абстрактного класса BaseListItem.
Member | Описание |
---|---|
ContentTypeName | Получает тип контента, который связан с элементом. Если значение null, тип контента библиотеки по умолчанию будет назначен для элемента при его сохранении. |
FieldInternalNames | Список имен полей, которые могут быть кэшированы для повышения производительности при использовании для проверки данных в полях перед сохранением. |
ListTitle | Получает название списка (с учетом регистра). |
В таблице ниже перечислены методы, которые должны быть реализованы классами, которые наследуют от абстрактного класса BaseListItem. Эти методы возвращают параметры, которые должны иметь непреобразуемый тип, чтобы они могли использоваться на различных платформах.
Метод | Описание |
---|---|
ReadProperties(ListItem) | Считывает свойства из объекта ListItem с помощью методов BaseGet и BaseGetEnum и назначает их для свойств подкласса. |
SetProperties(ListItem) | Установка свойств для объектов ListItem с помощью методов BaseSet и BaseSetTaxonomyField абстрактного класса. |
В таблице ниже перечислены вспомогательные методы из класса BaseListItem, которые требуются подклассам для реализации методов ReadProperties и SetProperties.
Вспомогательный метод | Описание |
---|---|
BaseGet(ListItem item, string internalName) | Получает свойство, определяемое параметром internalName из ListItem и возвращает универсальный тип T. |
BaseSet(ListItem item, string internalName, object value) | Задает свойство ListItem, определяемое параметром internalName. |
BaseSetTaxonomyField(ListItem item, string internalName, string label, Guid termId) | Задает поле таксономии ListItem, определяемое параметрами internalName и termId. |
BaseGetEnum(ListItem item, string internalName, T defaultValue) | Получает значение свойства перечисления, определяемое параметром internalName. Возвращает значение параметра defaultValue, если не задано свойство. |
Файл Event.cs файл содержит следующие реализации методов ReadProperties и SetProperties.
ReadProperties
protected override void ReadProperties(ListItem item) {
RegisteredEventId = BaseGet<string>(item, FIELD_REGISTERED_EVENT_ID);
Description = BaseGet<string>(item, FIELD_DESCRIPTION);
Category = BaseGet<string>(item, FIELD_CATEGORY);
EventDate = BaseGet<DateTime?>(item, FIELD_DATE);
Location = BaseGet<string>(item, FIELD_LOCATION);
ContactEmail = BaseGet<string>(item, FIELD_CONTACT_EMAIL);
Status = BaseGetEnum<EventStatus>(item, FIELD_STATUS);
var imageUrl = BaseGet<FieldUrlValue>(item, FIELD_IMAGE_URL);
if (imageUrl != null)
ImageUrl = imageUrl.Url;
}
SetProperties
protected override void SetProperties(ListItem item) {
BaseSet(item, FIELD_REGISTERED_EVENT_ID, RegisteredEventId);
BaseSet(item, FIELD_DESCRIPTION, Description);
BaseSet(item, FIELD_CATEGORY, Category);
BaseSet(item, FIELD_DATE, EventDate);
BaseSet(item, FIELD_LOCATION, Location);
BaseSet(item, FIELD_CONTACT_EMAIL, ContactEmail);
BaseSet(item, FIELD_STATUS, Status.ToEnumDescription());
BaseSet(item, FIELD_IMAGE_URL, ImageUrl);
}
Указанны ниже примеры кода показывают, как базовые методы BaseGet и BaseSet определены в BaseListItem.cs.
BaseGet
protected T BaseGet<T>(ListItem item, string internalName){
var field = _fields[internalName.ToLowerInvariant()];
var value = item[field.InternalName];
return (T)value;
}
BaseSet
protected void BaseSet(ListItem item, string internalName, object value) {
if (_fields.ContainsKey(internalName)) {
var field = _fields[internalName.ToLowerInvariant()];
if (field is FieldUrl && value is string) {
var urlValue = new FieldUrlValue() {
Url = value.ToString()
};
value = urlValue;
}
}
item[internalName] = value;
}
Класс BaseListItem также содержит метод Save, который используется для сохранения каждой сущности LOB, которые надстройка создает и управляет. Этот метод, загружает список и определяет, есть ли у текущего элемента идентификатор, который имеет значение больше нуля. Если идентификатор не больше 0, предполагается, что значение неверно и создается новый элемент списка. Используется метод SetProperties для определения свойств в ListItem, а затем выполняется установка свойств для подкласса с помощью методы ReadProperties.
public void Save(Web web) {
var context = web.Context;
var list = web.GetListByTitle(ListTitle);
if (!IsNew && Id > 0) {
ListItem = list.GetItemById(Id);
}
else {
var listItemCreationInfo = new ListItemCreationInformation();
ListItem = list.AddItem(listItemCreationInfo);
}
// Ensure that the fields have been loaded.
EnsureFieldsRetrieved(ListItem);
// Set the properties on the list item.
SetProperties(ListItem);
BaseSet(ListItem, TITLE, Title);
// Use if you want to override the created/modified date.
//BaseSet(ListItem, CREATED, Created);
//BaseSet(ListItem, MODIFIED, Modified);
ListItem.Update();
if (!string.IsNullOrEmpty(ContentTypeName)) {
var contentType = list.GetContentTypeByName(ContentTypeName);
if (contentType != null)
BaseSet(ListItem, "ContentTypeId", contentType.Id.StringValue);
}
ListItem.Update();
// Execute the batch.
context.ExecuteQuery();
// Reload the properties.
ListItem.RefreshLoad();
UpdateBaseProperties(ListItem);
ReadProperties(ListItem);
}