Все вы помните ссылку «Добавить новый элемент» внизу представлений списков SharePoint:
В связи с наличием этой ссылки возникает два естественных вопроса:
- Как поменять текст этой ссылки? (например вместо “Добавить новый элемент”, я хочу текст “Добавить товар” или “Добавить продукт” и т.д.)
- Как добавить еще пару ссылок рядом со ссылкой для создания нового элемента?
Например, некий список с заявками может иметь вот такие ссылки быстрого доступа:
, где ссылка Configure application route открывает редактор маршрута заявки (т.е. можно определить, кто и в каком порядке должен одобрять эту заявку).
В этой статье я расскажу, как реализовать такие изменения. Рассмотрим два способа:
- no-code — для частных решений, быстрых правок и кастомизаций Office365
- программно – что может потребоваться для создания коробочных решений
No code
Как обычно в случае XSLT, самый простой и быстрый способ найти нужный шаблон и произвести нужные изменения — это использовать SharePoint Designer 2010 (далее SPD). Откройте ваш список в SPD, щелкните по названию нужного представления, и вы увидите визуальный дизайнер страницы.Однако, ссылки «Add new item» вы не увидите, даже несмотря на то, что на исходной странице в браузере она присутствует.
Чтобы её увидеть, необходимо сделать следующее: щелкнуть куда-нибудь внутрь таблицы, в контекстной ленте в группе вкладок List View Tools щелкнуть Design, и выбрать Options –> Summary Toolbar.
Дальше всё очень просто, щелкаем на надпись “Add new item”:
, и в правой панели в списке свойств видим:
В это свойство можно вписать ваш текст (обратите внимание на апострофы):
Жмем Enter, сохраняем страницу (кнопка сверху слева), переходим в браузер, обновляем страницу с представлением списка, вуаля!
Чтобы добавить еще одну ссылку рядом с New memo, желательно всё-таки использовать XSLT-разметку.
Для этого, давайте выделим всю строку, содержащую ссылку New memo. Кстати, вместо того чтобы тыкать 10 раз и пытаться попасть в нужный тэг <tr>, существует простой способ выделить строку, к которой принадлежит текущее выделение:
Теперь, не снимая выделения, переключаемся на вкладку Code:
Нам будет отображен фрагмент XSL-преобразования, ответственный за отображение как раз той самой строки с ссылкой.
Соответственно, этот кусок можно скопировать, вставить копию чуть ниже, и подкорректировать.
Во-первых, нужно заменить картинку, за которую ответственен вот этот кусок кода:
<span style="height:10px;width:10px;position:relative;display:inline-block;overflow:hidden;" class="s4-clust">
<img src="/_layouts/images/fgimg.png" alt="" style="left:-0px !important;top:-128px !important;position:absolute;" /> </span>
Подойдет любая картинка 10х10 пикселов. Если вы берете картинку не из спрайта, код можно упростить примерно до такого вида:
<img src="/_layouts/images/wpedit.gif" alt="" style="height:10px;width:10px;" />
Далее, нужно подкорректировать саму ссылку, за которую ответственен вот этот кусок XSLT-кода:
<a class="ms-addnew" id="{$ID}" href="{$Url}" onclick="javascript:NewItem2(event, "{$Url}");javascript:return false;" target="_self"> <xsl:value-of select="'New memo'" /> </a>
Здесь совет: когда это возможно, используйте JavaScript, и открывайте модальный диалог с нужной вам страницей. Это более привычное поведение для пользователей, т.к. ссылка New item открывает именно модальный диалог.
В остальных случаях, очень полезно использовать функцию GoToPage, которая описана у Alejo El Norte. Она добавит параметр Source к адресу целевой страницы, и позволит вам по нажатию кнопки «Отмена» или «Назад» на целевой странице, легко вернуться туда, откуда пользователь пришел.
В любом случае, открываете ли вы некую страницу в модальном диалоге или напрямую, как правило для формирования ссылки требуются некоторые типовые сведения:
Все эти сведения можно взять из глобальных параметров XSLT. Они довольно хорошо описаны на MSDN.
Чтобы не искать, самые интересные параметры:
$List | GUID списка |
$ListUrlDir | Адрес корневого каталога списка, аналог SPList.RootFolder.ServerRelativeUrl |
$ServerRelativeUrl | Адрес текущего узла относительно адреса сервера, аналог SPWeb.ServerRelativeUrl |
$RootSiteUrl | Адрес коллекции узлов, которой принадлежит текущий узел |
$LCID | Строка, содержащая номер текущей локали, аналог Thread.CurrentThread.CurrentUICulture.LCID |
$Userid | ID текущего пользователя |
Также, иногда хочется отобразить ту или иную ссылку по некоему условию. Наиболее частое условие — наличие у пользователя каких-либо прав. В этом случае пригодится функция ddwrt:IfHasRights.
Например, если я хочу отобразить ссылку на переход на некую страницу с дополнительными настройками списка, только если у пользователя есть права на управление списками (SPBasePermissions.ManageLists), то я должен обернуть соответствующий <tr> в тег <xsl:if>, и передать в ddwrt:IfHasRights числовое значение, соответствующее ManageLists, т.е. 2048:
<xsl:if test="ddwrt:IfHasRights(2048)"> <!-- 2048 = SPBasePermissions.ManageLists --> <tr> <td class="ms-addnew" style="padding-bottom: 3px"> <img src="/_layouts/images/wpedit.gif" alt="" style="height:10px;width:10px;" /> <xsl:text disable-output-escaping="yes" ddwrt:nbsp-preserve="yes">&nbsp;</xsl:text> <a class="ms-addnew" href="javascript:GoToPage('{$HttpVDir}/_layouts/My/MyPage.aspx?List={$List}');"> Advanced settings </a> </td> </tr> </xsl:if>
Соответствие строковых значений перечисления числовым, можно легко узнать, написав в Visual Studio “SPBasePermissions”, и нажав F12. Конечно, этот список есть и в интернетах.
Программный способ
Даже если вы большой любитель всё делать только в Visual Studio, я бы всё равно порекомендовал сгенерить нужный XSLT код с помощью SPD. Используя SPD, вы получаете хорошую возможность оттестировать ваш код, добиться чтобы он работал и выглядел как должно. И главное – в SPD это можно сделать это быстро и минимальными усилиями.
Имея готовый отлаженный код, перейдите во вкладку Code в SPD, и скопируйте полностью содержимое тега <Xsl>. Этот код вставьте в отдельный xslt-файл, который нужно будет задеплоить куда-нибудь в Layouts.
Если вы всё-таки не хотите копаться в SPD, но хорошо чувствуете себя с XSLT… Что ж, минималистичный пример преобразования, которое заменяет стандартное “New item” на “New product”, приведен ниже:
<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal" xmlns:o="urn:schemas-microsoft-com:office:office"> <xsl:include href="/_layouts/xsl/main.xsl" /> <xsl:include href="/_layouts/xsl/internal.xsl" /> <xsl:template name="Freeform"> <xsl:param name="AddNewText"/> <xsl:param name="ID"/> <xsl:variable name="Url" select="$ENCODED_FORM_NEW"/> <xsl:if test="$ListRight_AddListItems = '1' and (not($InlineEdit) or $IsDocLib)"> <table id="Hero-{$WPQ}" width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td colspan="2" class="ms-partline"> <img src="/_layouts/images/blank.gif" width="1" height="1" alt="" /> </td> </tr> <tr> <td class="ms-addnew"> <span style="height:10px;width:10px;position:relative;display:inline-block;overflow:hidden;" class="s4-clust"> <img src="/_layouts/images/fgimg.png" alt="" style="left:-0px !important;top:-128px !important;position:absolute;" /> </span> <xsl:text disable-output-escaping="yes" ddwrt:nbsp-preserve="yes">&nbsp;</xsl:text> <a class="ms-addnew" id="{$ID}" href="{$Url}" onclick="javascript:NewItem2(event, "{$Url}");javascript:return false;" target="_self"> <xsl:value-of select="New memo" /> </a> </td> </tr> <tr> <td> <img src="/_layouts/images/blank.gif" width="1" height="5" alt="" /> </td> </tr> </table> </xsl:if> </xsl:template> </xsl:stylesheet>
Как и в случае с SDP, этот код нужно положить в отдельный файл, и задеплоить куда-нибудь в Layouts.
Файл есть, теперь XSLT-преобразование к представлению списка подключаются очень просто, например при активации вашей фичи:
public override void FeatureActivated(SPFeatureReceiverProperties properties) { var web = properties.Feature.Parent as SPWeb; var list = web.Lists["My list title"]; var view = list.DefaultView; view.XslLink = @"../My/MyTransform.xsl"; view.Update(); }
Здесь я предполагаю, что фича имеет Scope=Web, XSLT-преобразование лежит в файле Layouts/My/MyTransform.xsl (путь указывается относительно папки Layouts/Xsl), а у списка заголовок “My list title” (хотя получать списки по заголовку нежелательно, лучше по Url или по Guid).
P.S. Если вы не читали другие мои посты про XSLT и XsltListViewWebPart в SharePoint, очень рекомендую.
А я последнее время предпочитаю получать списки по RootFolder, как SPList tenderMembersList = web.Lists.Cast().Where(l => l.RootFolder.Name == "debug2Contractor").FirstOrDefault();
ОтветитьУдалитьвроде как InternalName списка, и узнать легко из URL, и привязки по Guid или Title не требует.
Александр, но ведь RootFolder.Name может совпадать для нескольких списков, если их положить в разные папки.
УдалитьЯ в основном получаю по URL, используя метод SPWeb.GetListFromWebPartPageUrl. При программном создании списков это, пожалуй, наиболее удобный вариант. Правда, если список не найден, вернет не null, а SPException, но обернуть в метод расширения - не проблема.
Андрей, не совсем понял Вашу мысль, приведите пример.
Удалить1. Насколько я знаю SPList.RootFolder == SPList.Folders[0] , т.е. имя корневой папки списка или библы. Ни о каких подпапках речи не идет. Или я ошибаюсь?
1.1 При создании списка/библы, его Title назначается именем корневой папке в списке, аля RootFolder, и становиться частью URL, в последствии title менять можно, а вот RootFolder нет (могу ошибаться).
1.2. На уровне узла, что для библы, что для списка RootFolder должен быть уникален. При попытке создать список или библу с указанием одного и тоже Title(=RootFolder) будет выдан Exception, что такой урл уже занят.
2. но нюансы такого подхода все же есть:
2.1) что для библы и списка RootFolder все может быть один и тот же. но можно же при выборе учитывать тип списка.
2.2) вопрос производительности при таком подходе? может на самом деле SPWeb.GetList будет и предпочтительнее, а урл например можно состряпать программно исходя из WEB_URL/ListType/RootFolder
3. собственного я не чего нового не придумал, а взял из коментариев http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.splist.rootfolder(v=office.12).aspx
Если создавать списки только через GUI - все списки кладутся в каталог Lists, и имеют соответствующий URL.
УдалитьОднако программно можно создать список по любому URL. В частности, 4 из 7 перегрузок SPListCollection.Add имеют параметр URL, т.е. можно задать URL списка.
Через CAML тоже можно создать список по определенному URL.
Например, спокойно могут существовать рядом следующие списки:
1. http://portal/Lists/News
2. http://portal/MySolution/News
3. http://portal/Company/News
Мдя, век живи век учись. Спасибо за ликбез. Будем учитывать такие нюансы...
УдалитьЗаменил по инструкции "Добавить новый элемент" на свой вариант. Все отлично, только все кнопки на вкладке "Список" стали по виду неактивными, но при этом работают как раньше... Почему так?
ОтветитьУдалитьСпасибо
Юрий, у меня всё ок, кнопки работают нормально и по виду тоже активные.
УдалитьПростая смена одного текста ссылки на другой абсолютно ни на что не может влиять.
А на странице нет никаких ошибок JS?
В этом представлении ранее условное форматирование прикрутили... мож и наложилось. Про ошибки JS ничего не могу сказать, ибо не программист.
УдалитьАндрей, можно подробнее показать как в новой ссылке на создание элемента заменить тип контента. Пример: список в нем могут создаваться 4 контента. хочу сделать 4 ссылки каждую на свой контент. По вашему описанию добавил еще одну кнопку, ее переименовал и ни как не могу заставить изменить создаваемый контент.
ОтветитьУдалитьАлексей, нужно передавать ID типа содержимого в Url.
УдалитьОбычно Url (т.е. значение переменной $Url) будет выглядеть как-то так: "http://portal/_layouts/listform.aspx?PageType=8&ListId={PUT-YOUR-GUID-HERE}&RootFolder="
Вам нужно туда добавить "&ContentTypeId={PUT-YOUR-GUID-HERE}".
Итого, XSLT-код будет выглядеть как-то так:
Вместо переменной Url (<xsl:variable name="Url" select="$ENCODED_FORM_NEW" />), нужно будет вставить 4 переменных для ваших 4х типов содержимого:
<xsl:variable name="Url1" select="concat($ENCODED_FORM_NEW,'&ContentTypeId={PUT-YOUR-GUID1-HERE}')"/>
<xsl:variable name="Url2" select="concat($ENCODED_FORM_NEW,'&ContentTypeId={PUT-YOUR-GUID2-HERE}')"/>
<xsl:variable name="Url3" select="concat($ENCODED_FORM_NEW,'&ContentTypeId={PUT-YOUR-GUID3-HERE}')"/>
<xsl:variable name="Url4" select="concat($ENCODED_FORM_NEW,'&ContentTypeId={PUT-YOUR-GUID4-HERE}')"/>
и соответственно в ссылках вместо $Url нужно будет использовать $Url1, $Url2, $Url3 и $Url4.
Обратите внимание, переменная в ссылке встречается дважды: в href и в onclick.
Подскажите как поместить кнопку добавления над списком? Достаточно ли будет соответствующий кусок XSL-преобразования перенести?
ОтветитьУдалитьИлья, здравствуйте!
ОтветитьУдалитьВам нужно переопределить шаблон match="/" примерно следующим образом:
<xsl:template match="/">
<!-- ваш код -->
<xsl:choose>
<xsl:when test="$RenderCTXOnly='True'">
<xsl:call-template name="CTXGeneration"/>
</xsl:when>
<xsl:when test="($ManualRefresh = 'True')">
<xsl:call-template name="AjaxWrapper" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates mode="RootTemplate" select="$XmlDefinition"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
т.е. вместо "ваш код" нужно будет вставить тот самый кусок преобразования, ну или лучше выделить этот кусок в отдельный шаблон и добавить просто вызов вашего шаблона (xsl:call-template).
Андрей, спасибо большое за пост! Долго искала how-to по редактированию ссылок. У меня еще вопрос: номер с переименованием ссылок (no code) не проходит со списком типа Discussion Board, там свои заморочки?
ОтветитьУдалитьПривет,Андрей статья действительно очень полезная!Спасибо. Хочу чтобы твои читатели знали и про другой вариант добавления xsl. Ты описал это в FeatureActivated.
ОтветитьУдалитьЯ это описываю в ListDefinition => schema.xml
..\yourfolderinLayouts\yourfile.xsl
И кстати, чуть ниже можно вставить это clienttemplates.js там же,в теге
И 2013 версии мы можем редактировать список в режиме "Быстрого редактирования", иначе кнопка будет неактивная.
Еще раз спасибо
Исправил предыдущий комент
ОтветитьУдалить<view BaseViewID="1" Type="HTML" ... >
<XslLink Default="TRUE">..\yourfolderinLayouts\yourfile.xsl</XslLink>
<JSLink>clienttemplates.js</JSLink>
<view>
Очень ценная информация, спасибо за ваш труд...
ОтветитьУдалить