SharePoint 2016. Кастомизация меню SuiteBar
Продолжаем тему кастомизации SuiteBar. В прошлом посте я показал как можно изменить SuiteBar с помощью PowerShell. На этот раз изменение меню и App Launcher.
Начнем с того как работает меню SuiteBar в SharePoint 2016.
Как это работает
В SharePoint 2016 SuiteBar формируется и на стороне клиента и на стороне сервера (меню настройка, справка, пользователь). Для управления элементами, формируемыми на стороне сервера можно использовать CustomActions, как это было в предыдущих версиях SharePoint.
Формирование SuiteBar на стороне клиента появилась в SharePoint 2016. В коде страницы можно увидеть следующий JavaScript-код, который инициализирует рендеринг:
SuiteNavRendering.RenderSuiteNav(
{
top:'suiteBarTop',
version:2,
**dataEndpoint:'Microsoft.SharePoint.Portal.SuiteNavData.GetSuiteNavData',**
culture:'en-US',
signInLink:'/_layouts/15/Authenticate.aspx?Source=\u00252FSitePages\u00252FHome\u00252Easpx',
brandingText:'Ай-Теко',
brandingLogo:'http://i-teco.ru/source/images/logo_2014_h.png',
brandingLogoLink:'/',
brandingLogoTitle:'Ай-Теко'
});
Некоторые параметры в этом скрипте (имена, начинающиеся с branding) задаются с помощью PowerShell. Как это делается можно посмотреть в моём предыдущем посте о кастомизации SuiteBar.
Важным здесь является параметр dataEndpoint, он указывает где брать данные для формирования SuiteBar. Если мы откроем указанный URL вида:
http://spapp**/_api/Microsoft.SharePoint.Portal.SuiteNavData.GetSuiteNavData**
то увидим данные для SuiteBar в виде JSON:
JavaScript-код формируется серверным контролом Microsoft.SharePoint.WebControls.SuiteNavControl. Он же берет параметры веб-приложения для формирования branding*-свойств.
Теперь ясно что надо сделать для кастомизации SuiteBar:
- Создать свой сервис, расширив REST API своим endpoint'ом
- Подменить клиентский код, указав ссылку на свой сервис
API для SuiteBar
Как расширить стандартный REST API в SharePoint 2016 я уже писал. Здесь описывать сам сервис я не буду - исходные коды решения опубликованы, можно посмотреть там.
От сервиса нам нужен единственный метод, который будет возвращать строку, содержащую JSON-данные. Метод (или свойство) сервиса должно называться GetSuiteNavData. Это важно, иначе решение не будет работать.
Данные для SuiteBar
Классы, описывающие SuiteBar, есть в сборке SharePoint, но они приватные. Логики они не содержат, только набор свойств. Поэтому просто опишем три класса как показано на диаграмме, просто скопировав одноименные приватные классы SharePoint:
Теперь переходим к формированию данных для SuiteBar. Все предельно просто: вот так выглядит метод в сервисе, который возвращает данные для SuiteBar:
private SuiteNav GetSuiteNav()
{
return new SuiteNav
{
DoNotCache = false,
SPSuiteVersion = 2,
NavBarData = new SuiteNavBarData
{
ClientData = null,
CurrentMainLinkElementID = "ShellSharepoint",
StringsOverride = null,
IsAuthenticated = true,
UserDisplayName = "UserDisplayName",
AboutMeLink = new SuiteNavLink("ShellAboutMe", "About Me", null, "#ShellAboutMe"),
HelpLink = new SuiteNavLink("ShellHelp", "Help", null, "#ShellHelp"),
SignOutLink = new SuiteNavLink("ShellSignOut", "Sign Out", null, "#ShellSignOut"),
CurrentWorkloadHelpSubLinks = new[] { new SuiteNavLink("HelpSubLink", "HelpSubLink", null, "#HelpSubLink") },
CurrentWorkloadSettingsSubLinks = new[] { new SuiteNavLink("SettingsSubLink", "SettingsSubLink", null, "#SettingsSubLink") },
CurrentWorkloadUserSubLinks = new[] { new SuiteNavLink("UserSubLink", "UserSubLink", null, "#UserSubLink") },
WorkloadLinks = new List<SuiteNavLink>
{
new SuiteNavLink("ShellDocuments", "ShellDocuments", null, "#ShellDocuments"),
new SuiteNavLink("ShellFAQ", "ShellFAQ", null, "#ShellFAQ"),
new SuiteNavLink("ShellChat", "ShellChat", null, "#ShellChat"),
new SuiteNavLink("ShellVideo", "ShellVideo", null, "#ShellVideo"),
new SuiteNavLink("ShellCRM", "ShellCRM", null, "#ShellCRM"),
new SuiteNavLink("ShellStar", "ShellStar", null, "#ShellStar"),
new SuiteNavLink("ShellProject", "ShellProject", null, "#ShellProject"),
new SuiteNavLink("ShellYammer", "ShellYammer", null, "#ShellYammer"),
new SuiteNavLink("ShellExcel", "ShellExcel", null, "#ShellExcel"),
new SuiteNavLink("ShellPowerBI", "ShellPowerBI", null, "#ShellPowerBI"),
new SuiteNavLink("ShellCheck", "ShellCheck", null, "#ShellCheck"),
new SuiteNavLink("ShellSearch", "ShellSearch", null, "#ShellSearch"),
}
}
};
}
Полученный объект надо сериализовать в JSON и вернуть в виде строки. В моем случае метод сервиса с сериализацией выглядит вот так::
[ClientCallableMethod]
public string GetSuiteNavData()
{
var suiteNav = GetSuiteNav();
var serializer = new DataContractJsonSerializer(typeof (SuiteNav));
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, suiteNav);
return Encoding.Default.GetString(ms.ToArray());
}
}
Некоторые свойства класса SuiteNav:
- DoNotCache - флаг, указывающий разрешено ли кеширование;
- SPSuiteVersion - версия SuiteBar, для SharePoint 2016 OnPrem указываем 2;
Свойства класса SuiteNavBarData:
- CurrentMainLinkElementID - просто указываем "ShellSharepoint";
- UserDisplayName - имя текущего пользователя, можно указать любое значение;
- IsAuthenticated - авторизован ли пользователь;
- CurrentWorkloadHelpSubLinks - дополнительные пункты меню Справка;
- CurrentWorkloadSettingsSubLinks - дополнительные пункты меню Настройка;
- CurrentWorkloadUserSubLinks - дополнительные пункты меню Пользователя;
- WorkloadLinks - App Launcher;
Дополнительные пункты меню и имя пользователя в интерфейсе выглядят примерно так:
Свойства класса SuiteNavLink (используется для всех ссылок) понятны без описания.
Данные для SuiteBar сформированы, сервис, который их возвращает есть. осталось подменить endpoint.
Регистрация Endpoint для SuiteBar
Чтобы зарегистрировать созданный endpoint необходимо подменить свойство SuiteNavRestMethod контрола SuiteNavControl. Для этого переопределяем его привязку к делегат-контролу SuiteBarDelegate:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control Id="SuiteBarDelegate"
Sequence="50"
ControlClass="Microsoft.SharePoint.WebControls.SuiteNavControl"
ControlAssembly="Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
xmlns="http://schemas.microsoft.com/sharepoint/">
<Property Name="SuiteNavRestMethod">CustomSuiteNav/GetSuiteNavData</Property>
</Control>
</Elements>
Вот и всё. Мы создали новый сервис для формирования SuiteBar и указали ссылку на него в SuiteNavControl. Результат в браузере:
AppLauncher CSS
Для создаваемых плиток на стороне клиента формируется атрибут id по правилу:
O365_AppTile_**[SutieNavLink.Id]**
Таким образом, для ссылки:
new SuiteNavLink
{
Id = "ShellFAQ",
Title = "ShellFAQ",
Url = "#ShellFAQ"
}
На клиенте будет сформирована плитка с id = O365_AppTile_ShellFAQ. Это пригодится для описания стилей.
Также можно использовать стандартный шрифт для иконок office365icons. В моём случае CSS для кнопки ShellFAQ:
#O365_AppTile_ShellFAQ .o365cs-nav-appTileIcon:before {
content: '\e006';
}
Пользуемся!
Исходные коды
Исходные коды доступны на сайте http://code.msdn.microsoft.com.