четверг, 21 октября 2010 г.

SharePoint 2010 Ribbon: как удалять и заменять OOTB кнопки на риббоне

Всё-таки, Ribbon в шарепойнте - штука полезная. К примеру, Content Editor Web Part именно благодаря Ribbon'у выглядит подобно Word'у, и оттого так нравится многим (в том числе и мне). И естественно, что частенько требуется кастомизировать этот самый Ribbon.


Чаще всего задача заключается в добавлении какой-нибудь кнопки для собственного списка. Это делается довольно просто, и описано в MSDN. А вот недавно мы столкнулись с необходимостью удаления трех кнопок из риббона библиотеки документов - "Создать", "Отправить" и "Создать папку". Вместо них планировалось добавить одну собственную кнопку, которая должна была перенаправлять пользователей на страницу, где на основе вводимых данных документ для библиотеки генерировался бы программно.

Однако, стандартный в таких случаях HideCustomAction элемент, несмотря на несколько часов попыток, не помог удалить искомые кнопки из Ribbon'а. В этом посте я расскажу, как нам удалось обойти эту проблему и добиться желаемого.

Решение очень простое. Оказывается, кроме добавления, кнопки и группы кнопок на Ribbon'е можно изменять. Причем, не только кнопки, но даже табы. В итоге, мы просто заменили целую группу кнопок, которая ответственна за создание новых элементов, на собственную группу с одним-единственным элементом.

Кстати, при операциях с Ribbon'ом очень поможет файлик C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\GLOBAL\XML\CMDUI.xml, в котором описаны все OOTB-риббоны, поставляемые вместе с шарепойнтом.

Ну просто ОЧЕНЬ полезный файлик.

Именно в нем можно подсмотреть, что есть такая группа контролов, "Ribbon.Documents.New", в котором видим 3 элемента - те самые "Создать", "Отправить" и "Создать каталог":


На самом деле вы можете изменить любой из этих элементов или всю группу, причем можно создать кнопки с раскрывающимся меню. Уже готовый xml для этого можно обнаружить в том самом CMDUI.xml.

Полный xml-код для группы из этих трех элементов:
<Group
  Id="Ribbon.Documents.New"
  Sequence="10"
  Command="DocumentNewGroup"
  Description=""
  Title="$Resources:core,cui_GrpNew;"
  Image32by32Popup="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32PopupTop="-32" Image32by32PopupLeft="0"
  Template="Ribbon.Templates.Flexible2"
        >
  <Controls Id="Ribbon.Documents.New.Controls">
    <SplitButton
      Id="Ribbon.Documents.New.NewDocument"
        Sequence="10"
      Command="NewDefaultDocument"
      CommandMenuOpen="NewDocumentMenuOpen"
      LabelText="$Resources:core,cui_ButNewDocument;"
      Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-48" Image16by16Left="-16"
      Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="0" Image32by32Left="-64"
      PopulateDynamically="true"
      PopulateQueryCommand="PopulateNewDocumentMenu"
      ToolTipTitle="$Resources:core,cui_ButNewDocument;"
      ToolTipDescription="$Resources:core,cui_STT_ButNewDocument;"
      TemplateAlias="o1"
            >
    </SplitButton>
    <SplitButton
      Id="Ribbon.Documents.New.AddDocument"
      Sequence="20"
      Command="UploadDocument"
      CommandMenuOpen="UploadDocumentMenuOpen"
      LabelText="$Resources:core,cui_ButUploadDocument;"
      ToolTipTitle="$Resources:core,cui_ButUploadDocument;"
      ToolTipDescription="$Resources:core,cui_STT_ButUploadDocument;"
      Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-24" Image16by16Left="-32"
      Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-32" Image32by32Left="0"
      TemplateAlias="o1"
            >
      <Menu Id="Ribbon.Documents.New.AddDocument.Menu">
        <MenuSection Id="Ribbon.Documents.New.AddDocument.Menu.Upload" DisplayMode="Menu32">
          <Controls Id="Ribbon.Documents.New.AddDocument.Menu.Upload.Controls">
            <Button
              Id="Ribbon.Documents.New.AddDocument.Menu.Upload.Upload"
              Sequence="10"
              Command="UploadDocument"
              Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-24" Image16by16Left="-32"
              Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-32" Image32by32Left="0"
              LabelText="$Resources:core,cui_ButUploadDocument;"
              ToolTipTitle="$Resources:core,cui_ButUploadDocument;"
              ToolTipDescription="$Resources:core,cui_STT_ButUploadDocument;"
              Description="$Resources:core,cui_STT_ButUploadDocument;"
                    />
            <Button
              Id="Ribbon.Documents.New.AddDocument.Menu.Upload.UploadMultiple"
              Sequence="20"
              Command="UploadMultipleDocuments"
              Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="0" Image16by16Left="-184"
              Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-320" Image32by32Left="-224"
              LabelText="$Resources:core,cui_ButUploadMultipleDocuments;"
              ToolTipTitle="$Resources:core,cui_ButUploadMultipleDocuments;"
              ToolTipDescription="$Resources:core,cui_STT_ButUploadMutipleDocuments;"
              Description="$Resources:core,cui_STT_ButUploadMutipleDocuments;"
                    />
          </Controls>
        </MenuSection>
      </Menu>
    </SplitButton>
    <Button
      Id="Ribbon.Documents.New.NewFolder"
      Sequence="30"
      Command="NewFolder"
      LabelText="$Resources:core,cui_ButNewFolder;"
      Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-16" Image16by16Left="-248"
      Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-448" Image32by32Left="-320"
      ToolTipTitle="$Resources:core,cui_ButNewFolder;"
      ToolTipDescription="$Resources:core,cui_STT_ButNewLibraryFolder;"
      TemplateAlias="o1"
            />
  </Controls>
</Group>
Т.о., если хочется сделать кнопку с раскрывающимся меню, очевидно, что следует использовать SplitButton, с вложенным тэгом Menu, внутри которого уже размещаются обычные Button'ы.

Однако, просто так этот элемент в фичу подпихнуть нельзя. Следует указать, что именно мы изменяем и для каких списков. Чтобы изменить искомую группу для всех стандартных библиотек, следует использовать элемент CustomAction следующего содержания:
<CustomAction Id="TestModifyDocumentsNew" RegistrationType="List" RegistrationId="101" Location="CommandUI.Ribbon">
  <CommandUIExtension>
    <CommandUIDefinitions>
      <CommandUIDefinition Location="Ribbon.Documents.New">
        <!-- Здесь начинается тэг Group -->
      </CommandUIDefinition>
    </CommandUIDefinitions>
  </CommandUIExtension>
</CustomAction>
В случае, если вас интересуют только собственные библиотеки документов, вместо 101 в атрибуте RegistrationId укажите его id (например, 10001). Не менее важен атрибут Location у CommandUIDefinition, он указывает местоположение в структуре риббонов, которое мы будем изменять. Например, чтобы изменить аналогичную группу для обычных списков (не библиотек документов), следует в обозначенном атрибуте Location указать "Ribbon.ListItem.New".

В конечном итоге, мы использовали для замены стандартной группы кнопок риббона "Создать", примерно следующий код:

<CustomAction Id="RibbonGenerateButton" RegistrationType="List" RegistrationId="101" Location="CommandUI.Ribbon">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition
          Location="Ribbon.Documents.New">

          <Group Id="Ribbon.Documents.New" Sequence="10" Command="DocumentNewGroup" Description="" Title="$Resources:core,cui_GrpNew;" Image32by32Popup="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32PopupTop="-32" Image32by32PopupLeft="0" Template="Ribbon.Templates.Flexible2">
            <Controls Id="Ribbon.Documents.New.Controls">
              <Button 
                Id="Ribbon.Documents.New.Controls.GenerateButton" 
                Alt="Генерация нового документа для этой библиотеки" 
                Sequence="1" 
                Image32by32="/_layouts/path/to/your/image/here.png" 
                Command="Demo_HelloWorld" 
                LabelText="Генерация нового документа" 
                TemplateAlias="o2"/>
            </Controls>

          </Group>
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler
          Command="Demo_HelloWorld"
          CommandAction="javascript:alert('Hello World!');" />
      </CommandUIHandlers>
    </CommandUIExtension>
  </CustomAction>

Результат:

3 комментария:

  1. Добрый день, а подскажите как удалить кнопку? Скажем я открываю ОЧЕНЬ полезный файл, а как мне в нем найти нужную кнопку? Например мне нужно удалить кнопку "синхронизация с workspace".

    ОтветитьУдалить
  2. Добрый день, Кирилл!

    Если имеется в виду кнопка Sync to SharePoint Workspace, то для библиотек документов - она имеет Id "Ribbon.Library.Actions.TakeOfflineToClient", а для списков - "Ribbon.List.Actions.TakeOfflineToClient".

    P.S. Сам не знал, нашел - поиском слова "Sync" в файлике CMDUI.XML.

    P.P.S. Самое главное правило - нельзя напрямую редактировать тот самый ОЧЕНЬ полезный файл :) Рекомендую об этом помнить прежде всего!

    ОтветитьУдалить
  3. Добрый день. может вы расскажите чуть подробнее о "First of all, you will need to provide QueryCommand attribute of DropDown element, and return the correct current value manually to the properties["Value"]" что именно нужно передать в Value?

    http://sharepoint.stackexchange.com/questions/26301/selected-value-in-ribbon-dropdown-combobox-is-not-displayed/35105#35105

    ОтветитьУдалить

Внимание! Реклама и прочий спам будут беспощадно удаляться.