reSP — great Resharper plugin from SubPoint Solutions.
Makes Your SharePoint solutions code more stable and robust:
http://subpointsolutions.com/resp/
reSP — great Resharper plugin from SubPoint Solutions.
Makes Your SharePoint solutions code more stable and robust:
http://subpointsolutions.com/resp/
я обновил Sharepoint EventHandlerExplorer 2013/2016, утилиту которая позволяет управлять обработчиками событий без использования SharePoint-фич. Позволяет привязывать ресиверы к вебам, спискам/библиотекам SharePoint, а также к контент-тайпам:
Описание:
Фичи позволяют привязывать обработчики только к известным типам контента(хрестоматийный пример – список “Announcements”):
<Receivers ListTemplateId="104">
Но такое декларативное развёртывание действует на весь scope и не позволяет к примеру привязать два обработчика к разным Announcement-спискам. Также не позволяет регистрировать обработчик для списка с неизвестным Id.
Изначально первая версия EventHandlerExplorer была создана Patrick Tisseghem и работала с третьей версией WSS. В Windows 2012(в .NET 4) появился новый глобальный кеш сборок, и исчезла возможность “перетягивать” мышью DLL-ки в GAC. Вместо drag-n-drop теперь рекомендуется использовать powershell:
[System.Reflection.Assembly]::Load('System.EnterpriseServices, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a'); $publish = New-Object System.EnterpriseServices.Internal.Publish; $publish.GacInstall('$fullpath');
я добавил обёртку для развёртывания через powershell и поддержку 2013 и 2016 версий SharePoint on-premise
Несмотря на то, что давно есть ITIL, бизнес и IT разговаривают на разных языках. Это иллюстрируется диалогом на одной из презентаций какого-то мега-супер-пупер SharePoint документооборота, когда на вопрос из зала
«Так а как в итоге мне сделать отчёт вот по вот этим вот карточкам, чтоб за месяц?..»
докладчик отвечает что
«Для отчётов у нас есть ‘БиАй-чик’, и кстати мы вынесли его на отдельные сервера. У нас распределённая ферма..»
Топология развёрнутой фермы интересует конечных пользователей в зале примерно также, как зайца интересует луна, а вот отчёт..
Но дело не только в этом. Недавно мне попался пост автора Bjørn Furuknap, я переведу его здесь так как на мой взгляд он является must-read для людей, которые хотели бы внедрить SharePoint, или просто посмотреть что это и как это может помочь их бизнесу, либо чувствуют что хотелось бы улучшить то что есть, но ещё сами не понимают как(и это imho вполне нормально)
В оригинале пост называется “SharePoint and Why It Always Depends”, но для русской аудитории я позволил себе немного переиначить название. Сам текст рассчитан на англоязычного читателя, но общий смысл будет донесён.
Когда вы задаёте SharePoint-консультанту вопрос, велики шансы что вы получите ответ “В зависимости от ситуации..”. Клиента подчас такой ответ разочаровывает, и мне кажется что в этом месте имеет смысл уточнить — почему же многое в SharePoint-е действительно — «В зависимости от ситуации..»
И раз уж быть честным, то на самом деле по большей части это ваша вина, как заказчика. Я здесь чтобы помочь вам разобраться, хотя мне тоже немного жаль что вы не услышите привычное «Клиент всегда прав!». Ну – по крайней мере в этом абзаце точно не услышите..
Реальность заключается в том, что вы делаете свою работу, а мы, как SharePoint разработчики – должны иметь возможность делать свою. Единственная вещь которую действительно надо выстроить – это понять каким образом мы можем быть вам полезны.
А что же для этого нужно? Ну, как вам сказать..
В зависимости от ситуации!..
Задавая неверные вопросы, вы гарантированно не получите нужный ответ. Так как же задать правильный вопрос и не попасть во всё сдувающую турбину однотипных «в зависимости от ситуации»?
Дело в том, что SharePoint – чрезвычайно массивный продукт, и в нём существует великое множество мелочей, без учёта которых невозможно выдать клиенту действительно хороший ответ. Эти мелочи — напрямую зависят от вопроса, поэтому чем более точно поставлен вопрос, и чем больше информации вы дадите о вашей текущей ситуации — тем больше шансов на то что вы получите ответ, который потом позволит вам выжать из SharePoint максимальную выгоду.
Я осознаю что вы, как клиент, вполне можете не знать о факторах, которые влияют на возможность дать вам хороший ответ. И это нормально; даже многие SharePoint-профессионалы не могут сразу определить, что будет иметь влияние именно в вашей ситуации. Вот почему когда вы спрашиваете специалиста по SharePoint — профессионал скорее всего задаст вам несколько смежных вопросов, которые помогут ему понять – что именно вы хотите получить в итоге.
В этом заключается ключ в понимании как задавать правильный вопрос. Спрашивая, чаще всего имеет смысл сфокусироваться на том, чего вы хотите достичь, нежели на том как сделать что-то определённым образом. Это проще понять на примере.
"А как мне сделать X в SharePoint?"
Вы могли бы подумать, что задаёте вполне конкретный вопрос, спрашивая к примеру – «Как мне добавить пользователей на сайт-коллекцию SharePoint?». Мысля с колокольни разработчика, я в свою очередь могу подумать о минимум трёх разных способах добавления пользователей на сайт-коллекцию SharePoint, и каждый имеет право на жизнь в определённом сценарии. Добавляете ли вы юзеров через веб-интерфейс, или в вашем случае нужно написать код чтобы делать это через веб-часть; а возможно права раздаются пользователям в обработчике событий?
Скорее всего, если вы не разработчик SharePoint -то наиболее вероятно что вы предполагаете использовать веб-интерфейс. Это хорошо, но тогда в этом случае берут ответвления новые вопросы, зависящие от того, как именно устроена ваша SharePoint security. Возможно в вашем решении пользователи получают доступ не напрямую, а через группы Active Directory, в таком случае вообще не затрагивая SharePoint.
Даже убедившись, что вы просто хотите дать пользователю доступ на сайт, и отметя прочие, нестандартные сценарии – всё равно нужно знать – какой Уровень разрешений вы собираетесь назначить пользователю, выдавая ему доступ. Он может варьироваться от уровня администратора сайт-коллекции, до прав читателя без возможностей редактирования и изменения чего-либо в этой сайт-коллекции, а может до прав только на определённый список — или вообще: просто иметь возможность скачать единственный документ в некоем списке вашей сайт-коллекции. И это приводит нас к разговору о том, какой набор уровней разрешений использовать в вашей фирме..
Вышеописанный пример применим и ко всем прочим вопросам из категории «А как мне сделать X в SharePoint». Теперь вы примерно представляете себе, как на первый взгляд легко решаемая задача -становится практически невозможной без получения дополнительных сведений конкретно о вашем случае и том, что же вам в итоге нужно получить на выхлопе.
Разновидность, подвид от вопроса выше — «А как мне наилучшим способом реализовать X в SharePoint?» – ещё более труден для ответа и является даже более вероятным кандидатом на получение в ответ “В зависимости от ситуации…”. В отсутствие соответствующих исследований вашей ситуации, «лучшесть», «хорошесть» — да или просто вообще возможность реализовать что-либо — в данном случае будет зависеть от слишком многих факторов-мелочей, на которые требуется ответ.
Вместо того, чтобы спрашивать как сделать то или это наилучшим образом – сфокусируйтесь на том чего вы в итоге хотите добиться. Для примера выше это может формулироваться например в таких строках: «у нас есть сотрудник из отдела бухгалтерии, и ему нужно ознакомиться с несколькими отчётами на нашем SharePoint сайте. У нас в компании есть настроенные группы Active Directory, но в данном случае нам нужно только единоразово выдать им доступ, да и прямого доступа изменять состав групп на домен-контроллере у меня нет. Как мне убедиться что бухгалтера получат эти отчёты?»
Даже с таким, довольно подробным объяснением, всё ещё возможен ответ «В зависимости от». Вдруг всё-таки лучшим решением для вас — будет выкачать эти отчёты и переслать их бухгалтерам по e-mail, на случай если, к примеру, вас сильно беспокоит что они получат «лишний» доступ к неким остальным частям сайта. Так или иначе – с дополнительной информацией уже намного проще дать вам нормальное предложение, которое в итоге реализует то чего вы хотите достичь. А это в свою очередь увеличивает ваши шансы получить от SharePoint больше.
По факту, это общий совет который можно дать когда речь заходит о SharePoint. Фокусируйтесь на том, чего вы хотите, нежели на том, как это реализовать. И если второе здесь –только для SharePoint-разработчика, то первое – только для вас.
.b
ссылка на оригинал
Есть вебчасть, содержащая внутри себя меню PeopleEditor. Столкнулся с тем что для анонимных пользователей она недоступна:
Спасибо Karel Hájek, который раскопал рефлектором что дело в дополнительной проверке, которая происходит на этапе рендеринга. Для отображения не хватает пермишна “BrowseUserInfo”, который по умолчанию выключен для анонимных юзеров, а веб-интерфейс не позволяет его выдать.
Karel предлагает подредактировать базу данных контента и разрешить анонимные запросы в Active Directory.
Намного проще это сделать через Powershell-скрипт, тем более что редактирование базы контента напрямую это очень bad-practice 🙂
Подключаем консоль, получим текущие разрешения и добавим к ним SPBasePermissions.BrowseUserInfo:
$sharePointSnapin = Get-PSSnapin | Where-Object { $_.Name -eq "Microsoft.SharePoint.PowerShell"} if($sharePointSnapin -eq $null) { Add-PSSnapin Microsoft.SharePoint.PowerShell } $web = Get-SPWeb http://portal/web/ $web.AnonymousPermMask64; # output: ViewListItems, ViewVersions, ViewFormPages, Open, ViewPages, UseClientIntegration $web.AnonymousPermMask64 = "$rights, BrowseUserInfo"; $web.Update();
Теперь PeoplePicker рендерится для всех пользователей(для того чтобы он ещё и работал надо включить запросы в AD):
iLoveSharepoint Power Webpart для Sharepoint 2010 позволяет вставлять скрипты на Powershell прямо в страницы портала.
Так как за powershell-ом лежит практически весь .NET(и не только), то это позволяет создавать интересные решения, без открытия студии и развёртывания wsp.
Для иллюстрации создадим какую-нибудь функцию на Powershell, к примеру будем приветствовать юзера в зависимости от времени суток. Имя текущего пользователя выведем в дружественном формате.
# подключим SharePoint безопасным образом $sharePointSnapin = Get-PSSnapin | Where-Object { $_.Name -eq "Microsoft.SharePoint.PowerShell"} if($sharePointSnapin -eq $null) { Add-PSSnapin Microsoft.SharePoint.PowerShell } # поздороваемся с пользователем, в зависимости от времени суток Function GetGreeting() { # текущее время $actualtime = (Get-Date).TimeOfDay; # выберем интервал $evening = New-TimeSpan -Hours 17 -Minutes 30 -Seconds 0 $morning = New-TimeSpan -Hours 8 -Minutes 0 -Seconds 0 $dinner = New-TimeSpan -Hours 11 -Minutes 0 -Seconds 0 $night = New-TimeSpan -Hours 22 -Minutes 0 -Seconds 0 if ($actualtime -ge $morning -and $actualtime -le $dinner) { return "Доброе утро"; } if ($actualtime -ge $dinner -and $actualtime -le $evening) { return "Добрый день"; } if ($actualtime -ge $evening -and $actualtime -le $night) { return "Добрый вечер"; } if ($actualtime -ge $night -or $actualtime -le $morning) { # дикий случай, но допустим return "Доброй ночи :)"; } } # для получения Display Name можно каждый раз делать запрос к домен-контроллеру, # но так как формат имени в домене имеет стандартный вид "Домен\Имя.Фамилия", # то выведем friendly-имя прямо тут же в вебчасти Function GetDisplayName($name) { if($name -match "\\") { $name = $name.remove(0, $name.LastIndexOf('\')+1); } if($name -match ".") { $name = $name.replace("."," "); } return $name } # соберём всё вместе $hello = (GetGreeting)+ ", " + (GetDisplayName -name ([Environment]::UserName)) # вывод html function Render($writer) { $writer.Write("<h1>$hello</h1>") }
Результат работы такого кода:
Таким образом мы получаем динамическое поведение в Sharepoint, но без создания «полновесного» решения с кодом
Есть система багтреккинга, сделанная на Sharepoint с использованием Infopath-форм.
Формы имеют несколько копий и в зависимости от принадлежности сотрудника в AD, ему подставляется своя копия и люди из разных отделов могут редактировать только свои поля:
Задача:
После экспорта узла из вида пропало поле «Статуса». То есть на форме оно есть, а в колонках видов и настройках списка уже нет.
Решение:
— Переопубликовать форму в Infopath Designer(колонка должна появиться, но без значений).
— Обновить все элементы списка, чтобы значения появились в видах SharePoint.
Программно это можно сделать например так
using (SPSite site = new SPSite("http://server/webName ")) using (SPWeb web = site.OpenWeb()) { SPList list = web.GetList("http://server/webname/libName/"); foreach (SPListItem item in list.Items) { try { Console.Write("Updating " + item.ID.ToString()); item.SystemUpdate(); Console.WriteLine(".. Ok."); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } }
или через Powershell:
$web = Get-SPWeb -identity "http://server/webName" $list = $web.Lists["listName"] $list.Items | ForEach-Object { Write-Host "Updating " $_.Name; $_.SystemUpdate(); } $web.Dispose()
p.s.: при этом пользовательские виды с участием этих “пропавших и возвращённых” полей возможно прийдётся пересоздать
В интернетах есть несколько вариантов как к примеру обновить элемент без старта привязанных к нему событий, но они либо не очень работают, либо работают в определённых контекстах. Вот ниже способ, который работает при обращении к элементам даже из консольного приложения.
Это extension-класс для SPListItem. Основа это пост на stackoverflow , я лишь добавил от себя ещё пару функций для работы с файлами — добавление файла и функцию CopyTo, для «тихого» копирования файла.
using Microsoft.SharePoint; public static class SPListItemExtensions { /// <summary> /// Provides ability to update list item without firing event receiver. /// </summary> /// <param name="item">list item</param> /// <param name="doNotFireEvents">Disables firing event receiver while updating item.</param> public static void Update(this SPListItem item, bool doNotFireEvents) { SPItemEventReceiverHandling rh = new SPItemEventReceiverHandling(); if (doNotFireEvents) { try { rh.DisableEventFiring(); item.Update(); } finally { rh.EnableEventFiring(); } } else { item.Update(); } } /// <summary> /// Provides ability to update list item without firing event receiver. /// </summary> public static void SystemUpdate(this SPListItem item, bool incrementListItemVersion, bool doNotFireEvents) { SPItemEventReceiverHandling rh = new SPItemEventReceiverHandling(); if (doNotFireEvents) { try { rh.DisableEventFiring(); item.SystemUpdate(incrementListItemVersion); } finally { rh.EnableEventFiring(); } } else { item.SystemUpdate(incrementListItemVersion); } } /// <summary> /// Provides ability to copy file from list item without firing event receiver. /// </summary> public static void CopyTo(this SPFile file, string strNewUrl, bool bOverWrite, bool doNotFireEvents) { SPItemEventReceiverHandling rh = new SPItemEventReceiverHandling(); if (doNotFireEvents) { try { rh.DisableEventFiring(); file.CopyTo(strNewUrl, bOverWrite); } finally { rh.EnableEventFiring(); } } else { file.CopyTo(strNewUrl, bOverWrite); } } /// <summary> /// Provides ability to update list item without firing event receiver. /// </summary> public static void SystemUpdate(this SPListItem item, bool doNotFireEvents) { SPItemEventReceiverHandling rh = new SPItemEventReceiverHandling(); if (doNotFireEvents) { try { rh.DisableEventFiring(); item.SystemUpdate(); } finally { rh.EnableEventFiring(); } } else { item.SystemUpdate(); } } /// <summary> /// Provides ability to add file to sharepoint library without firing event receiver. /// </summary> public static SPFile AddFile(SPFileCollection spFileCollection, string destPathToFile, byte[] binFileData, bool overwrite, bool doNotFireEvents) { SPItemEventReceiverHandling rh = new SPItemEventReceiverHandling(); if (doNotFireEvents) { try { rh.DisableEventFiring(); SPFile addedFile = spFileCollection.Add(destPathToFile, binFileData, overwrite); return addedFile; } finally { rh.EnableEventFiring(); } } else { SPFile addedFile = spFileCollection.Add(destPathToFile, binFileData, overwrite); return addedFile; } } private class SPItemEventReceiverHandling : SPItemEventReceiver { public SPItemEventReceiverHandling() { } new public void DisableEventFiring() { #pragma warning disable 612,618 base.DisableEventFiring(); #pragma warning restore 612,618 } new public void EnableEventFiring() { #pragma warning disable 612,618 base.EnableEventFiring(); #pragma warning restore 612,618 } } }