вторник, 14 октября 2014 г.
Документирую SharePoint JSGrid
Скриншот процесса:
суббота, 11 октября 2014 г.
Ускоряем SharePoint-разработку
Однако, тестировать SharePoint-решения не так-то просто. Их ведь надо еще задеплоить. А пока деплоишь, заснуть можно!... И потом сиди вспоминай, что вообще протестировать-то хотел...
И ведь все знают, что нередко бывает так: правишь пару символов - это занимает буквально секунды, и потом деплоишь пять минут, чтобы посмотреть результат. Потом видишь что не помогло - и по новой, еще пять минут деплоя... И не думайте, что концентрация от этого не теряется. Еще как.
Смешно сказать, но я до сих пор вспоминаю SPVisualDev, расширение для Visual Studio для работы с SharePoint 2007. Мгновенное развертывание в 12 hive, отличная поддержка удаленной разработки и отладки, всё удобно, быстро и просто. Этого в SharePoint Developer Tools нет и в помине даже сейчас, в 2014!...
Да, к сожалению, SharePoint с точки зрения скорости разработки сильно далеко от того, что сейчас происходит во "внешнем мире". А происходит, к слову, там, вот что:
Тем временем во "внешнем мире"...
- Проект IDE Light Table набрал $300k на Kickstarter. Основная идея проекта - в "живой разработке", когда IDE всегда находится в режиме отладки (!). Т.е. обычного режима не-отладки просто не бывает. Только начали писать код - тут же можно смотреть состояние переменных, и т.д.
- Многие современные IDE поддерживают режим "live development". Пример - опенсурсная IDE Brackets. Вот одноминутная демка:
- Visual Studio и ASP.Net развивают проект "Browser Link". Идея состоит в том, чтобы инжектить Signal-R скрипт на страницу, что позволяет в дальнейшем этой страницей управлять: релоадить целиком или частями, изменять, и т.д. "Browser Link" можно расширять с помощью Visual Studio extensions. В составе Web Essentials есть несколько крайне интересных расширений такого плана. Скотт Хансельман в этом 4х-минутном видео подробно все это демонстрирует:
- Visual Studio "14" CTP 4 по умолчанию использует open source компилятор Roslyn, который имеет расширенные средства для работы с неполными синтаксическими деревьями, благодаря чему им удалось сделать on-fly incremental compilation. Идея в том, чтобы пропустить шаг Build. Т.е. меняем код, сохраняем, переходим в браузер, F5 - и смотрим изменения.
- Помимо общеизвестного jsFiddle, появляется все больше и больше других Fiddle'ов, крайне интересных! Например, проект dotNetFiddle не только позволяет писать C# код онлайн, но также предоставляет режим, в котором можно тестировать полномасштабное ASP.Net MVC приложение (выберите Project Type: MVC)!
И т.д., список можно продолжать. Но я думаю вы поняли идею: все осознают, что чем меньше задержка между написанным кодом и увиденным результатом, тем лучше. И к этому идут. Но что можем сделать мы, SharePoint-разработчики?
Оказывается, и в SharePoint тоже можно сделать немало!
Для начала, продолжая мысль из поста про скрытую прелесть provider-hosted Apps, ну как бы все современные вещи можно там использовать без ограничений :) Так что всё что выше описано, нам по большому счету тоже доступно. Надо просто не забыть воспользоваться.
Во-вторых, кратко вернусь к SPVisualDev. Это open source расширение было написано ОДНИМ человеком в 2008 году. Получается, не настолько уж это и сложно?... ;)
Дальше. Давайте подумаем про BrowserLink. Можно ли что-то похожее использовать в SharePoint? Я думаю - да. Ведь BrowserLink это ни что иное, как банальный SignalR. Если заинжектить аналогичный SignalR скрипт в masterpage SharePoint-сайта, то мы получим точно такое же "живое" соединение Visual Studio и SharePoint. Я думаю можно просто выдрать из страницы то, что Visual Studio инжектит, и запихнуть в SharePoint, и оно будет работать. Не успел проверить пока.
Дальше. А что насчет jsFiddle и ASP.Net MVC fiddle? Можно ли сделать собственный SharePoint fiddle? ДА БЕЗ ПРОБЛЕМ! Вот например оцените, проект JSON fiddle:
- Блог пост: Introducing JSOM fiddle SharePoint app
- В Office Store: JSOM fiddle app
- Исходный код на Github
Моя попытка: CamlJs-Console
Вообще, на удивление, многие вещи не настолько сложные, как кажутся. Примерно в середине сентября я начал работать над крайне интересным проектом под названием CamlJs-Console. Это расширение для хрома, позволяющее создавать CamlJs-запросы в режиме live development, очень похожем на пример выше про Brackets. Пишешь - и сразу же видишь результат.
Оказалось, что Chrome Extensions пишутся на HTML+CSS+JavaScript. Очень просто. При желании можно даже создавать Chrome Extension + Web App (или даже SharePoint App) из одного исходного кода...
Общий вид:
Мне даже удалось подцепить туда интеллисенс (на основе TypeScript Language Service - ведь как известно, TS является расширением над JS, т.е. любой валидный JS-код является также и валидным TS-кодом):
И более того, мне даже удалось сделать интеллисенс лучше, чем TS-интеллисенс в Visual Studio! Потому что ведь у меня были живые списки. И их поля... :)
Мне кажется, получилось здорово! И заняло каких-то пару недель, причем большую часть времени возился с интеллисенсом.
Преимуществ у Chrome Extension немало: не надо аутентифицироваться, не надо ничего ставить на сайт (в отличие от того же SharePoint App). Можно тестировать и онлайн сайты, и на dev виртуалке, и что особенно ценно - зайти на production и там проверить запрос, на реальных данных (!). Интеграция работает через JSOM, но можно реализовать вариант на веб-сервисах (через SPServices например), и тогда будет работать даже с SharePoint 2007 (сейчас только SP2010 и SP2013).
Что дальше?
Ооооо... у меня полно идей, просто миллион! Можно делать extension'ы наподобие CamlJs - для работы с конкретным API, решения конкретных проблем. Можно делать более широкопрофильные, наподобие JSOM fiddle, но именно в виде Chrome extension. Можно копать в сторону интеграции Visual Studio и SharePoint, создания чего-то похожего на BrowserLink. Можно подумать о гибридных решениях, Visual Studio -> Chrome Extension -> SharePoint.
И это все может значительно улучшить наш процесс работы с SharePoint. Значительно!
Коллега с работы делает сейчас потрясающую штуку, как раз под впечатлением от CamlJs-Console. Крайне легко делается, но возможности открываются потрясающие. Смысл в том, что в Chrome через F12 можно редактировать файлы, подгруженные на страницу. Но конечно эти изменения никуда не сохраняются. Ну а вот он сделал так, что сохраняются, обратно в SharePoint :) И кода там - 50 строк :))). Проект пока на супер-начальной стадии, поэтому если захочется потестировать, тестируйте аккуратно и только на dev-окружении please :) Ссылка вот.
Заключение
Я из всей этой истории главное вот что понял: все в наших руках. Не надо ждать MS или еще кого. Мы сами можем.
P.S. Если у кого есть идеи/мысли на этот счет, с удовольствием поучаствую в обсуждении - кидайте комментарии! :)
суббота, 20 сентября 2014 г.
3 простых способа подружить KnockoutJs и SharePoint
KnockoutJs как MVVM фреймворк на самом деле ничем не уступает тому же Angular'у. Единственная вещь, которую нужно очень хорошо изучить и вызубрить в Knockout - это когда скобки () нужны, а когда нет, и как этот ko observable wrapping вообще работает. Преодолев этот порог, больше с Knockout проблем не возникает. Angular конечно помощнее, более фундаментальный, но и всякой магии в нем намного больше, траблшутить крайне сложно, и модель освоения Angular действительно вот такая (проверено на собственном опыте):
Так что, в своих SharePoint-проектах я преимущественно использую именно Knockout. И в этом посте я опишу три простых техники использования Knockout совместно с SharePoint.
среда, 19 марта 2014 г.
Web scraping с помощью XmlWebPart
На всякий случай, Web scraping - это способ интеграции с внешним ресурсом, при котором нужная информация выпарзивается прямо из HTML-кода некой внешней страницы в интернете. Обычно такое требуется, когда внешний сайт не предоставляет нормального API и нет никакой возможности его сделать (на самом деле, по моему опыту, такие ситуации возникают постоянно).
Безусловно это ужасный способ интеграции, хрупкий и ненадежный, и я искренне надеюсь, что он вам никогда не понадобится. Но если все-таки такое случится, то оказывается иногда это можно сделать даже без серверного кода.
понедельник, 26 августа 2013 г.
CamlJs 2.0
В этом посте я расскажу, что это за улучшения и как CamlJs может облегчить запросы к спискам в ваших проектах.
Зачем нужен CamlJs
Итак, проект CamlJs позволяет формировать строку Caml Query на стороне клиента (в JavaScript) для использования совместно с SP.CamlQuery или SPServices. CamlJs привносит следующие основные преимущества перед использованием просто строк с XML:
- Читаемость. Захардкоженный XML в коде выглядит жутковато и громоздко, запросы написанные на CamlJs читать намного проще и они гораздо компактнее.

- Защита от опечаток. XML - это по сути "magic string", в котором очень легко сделать опечатку. SharePoint в этом случае возвращает, как водится, совершенно безумные, вводящие в заблуждение сообщения об ошибках. Чтобы понять, что на самом деле ты где-то просто забыл закрывающую кавычку или что-то в этом духе, приходится тратить кучу времени...
- Intellisense. CamlJs дает intellisense и inline подсказки, благодаря чему даже люди, не в совершенстве знающие CAML Query, могут составлять сложные запросы без ошибок.
- Обработка SharePoint-овских "багофич". Например, многие ли из вас знают, что при попытке применить условие <Includes> для MultiLookup полей указывающих на поле типа DateTime SharePoint вернет ошибку? :) А CamlJs знает это - и еще кучу тонкостей, которые я собрал за долгие годы работы с CAML Query...
Что нового в версии 2.0
Версия 2.0 существенно улучшена по сравнению с 1.0. При этом, синтаксически версии оставлены максимально совместимыми.
- Поддержка скобочных выражений и генерации частей запросов. CamlJs 1.0 не поддерживал возможности создания скобочных выражений. Все операции в запросах генерились строго в порядке справа налево, и более-менее сложную логику с множеством вложенных Or и And реализовать было, к сожалению, невозможно :( Теперь это исправлено за счет введения операторов All и Any.
- Значительно улучшен Intellisense. Теперь набор возможных сравнений для поля зависит от типа этого поля, т.е. например к полям Integer нельзя применить сравнение Contains и т.д.
- Изменена обработка Lookup-полей. Теперь элемент LookupField поддерживает методы Id() и ValueAs<type>(), которые уже в свою очередь возвращают корректный набор операций. Например, если у вас есть Lookup-поле "City", которое ссылается на текстовое поле "Title" в таблице "Cities", то чтобы получить все записи у которых город начинается на "М", надо использовать запрос LookupField("City").ValueAsText().BeginsWith("M").
- Улучшен элемент UserField. Помимо изменений связанных с изменениями Lookup-полей (а User-поле это как известно частный случай Lookup-а), функционал элемента Membership теперь реализуется несколькими функциями элемента UserField. Кроме того, для удобства, была добавлен метод "EqualToCurrentUser()", который функционально эквивалентен конструкции "EqualTo(CamlBuilder.CamlValues.UserId)".
- Улучшен элемент DateRangesOverlap. К сожалению, выяснилось что этот элемент невозможно использовать вместе с SP.CamlQuery, поскольку CSOM игнорирует тэг QueryOptions, а для DateRangesOverlap нужно задавать QueryOptions/CalendarDate и QueryOptions/ExpandRecurrence. Однако, остается возможность использовать DateRangesOverlap хотя бы вместе с SPServices.
- Добавлен новый элемент BooleanField для полей типа Boolean.
- Добавлен новый элемент UrlField для полей типа URL.
- DateField и DateTimeField теперь конвертируют дату (Date) к правильному формату.
Наконец, проект теперь доступен через Nuget (добавляет camljs.js в папку Scripts вашего проекта):
PM> Install-Package CamlJs
Определения для TypeScript также доступны через Nuget:
PM> Install-Package camljs.TypeScript.DefinitelyTyped
Миграция с версии 1.0 на 2.0
Логика работы системы осталась в основном прежней, никакие старые элементы не были удалены или переименованы, поэтому как правило, миграция НЕ требуется. Однако, т.к. синтаксис стал немного более строгим, возможно потребуется что-то подкорректировать. Если у вас возникли проблемы с запросами из версии 1.0, смело пишите в комментарии, решим.
Также, обратите пожалуйста внимание на то, что элементы Membership и LookupIdField теперь помечены как "deprecated" и будут убраны в следующем большом релизе. Рекомендуется переписать запросы с их использованием на эквивалентные конструкции LookupField("...").Id() и UserField("...").IsInCurrentUserGroups() и т.д.
Заключение
В SharePoint 2013 роль JavaScript неимоверно возросла. Появилась необходимость создания комплексных JavaScript-решений, работающих полностью на стороне клиента. В этих условиях, приобретают повышенное значение всевозможные инструменты и расширения для клиентских операций, в том числе и разрабатываемый мною opensource-проект CamlJs.
Надеюсь, CamlJs поможет вам улучшить ваши JavaScript-решения и упростить реализацию задач, связанных с генерацией сложных запросов к спискам. Удачи!
понедельник, 19 августа 2013 г.
Про TypeScript и Unit-test'ы
CamlJs
На всякий случай, для тех кто возможно не в курсе, суть этого проекта - удобное формирование XML Caml-запросов для SP.CamlQuery и/или для SPServices, с интеллисенсом и подсказками. Выглядит это примерно так:
Особенность проекта в том, что он действительно не только бережет от опечаток, но практически основной целью при создании CamlJS являлось создание качественного интеллисенса и подсказок, чтобы не требовалось быть экспертом в Caml Query чтобы написать средний запрос.
Проект, даже на самой ранней стадии, представлял довольно внушительную гору JavaScript, довольно тяжеловесную в поддержке. В общем, переход на TypeScript был очень логичным шагом и значительно облегчил поддержку проекта и его дальнейшую разработку. Собственно благодаря переходу на TypeScript какая-то дальнейшая работа и стала возможной - сейчас как раз веду работы по новому синтаксису и готовлюсь релизить... :)
При всём этом, проект практически идеален для юнит-тестов: XML-запросы, которые должен генерировать билдер - это просто строки, которые очень легко проверить. Сначала я проводил тестирование "кустарно" - написал файлик и напулял туда простых сравнений. Конечно же со временем этого стало не хватать и проект дозрел до введения уже нормальных юнит-тестов.
tsUnit
Простенький движок для юнит-тестов на TypeScript существует - это tsUnit. Пользоваться примерно так: наследуемся от класса tsUnit.TestClass, используем методы класса-родителя для assert'ов, и плюс потребуется простенький код инициализации.
Пример использования (с сайта проекта):
/// <reference path="tsUnit.ts" /> /// <reference path="Calculations.ts" /> class SimpleMathTests extends tsUnit.TestClass { private target = new Calculations.SimpleMath(); addTwoNumbersWith1And2Expect3() { var result = this.target.addTwoNumbers(1, 2); this.areIdentical(3, result); } addTwoNumbersWith3And2Expect5() { var result = this.target.addTwoNumbers(3, 2); this.areIdentical(4, result); // Deliberate error } } // new instance of tsUnit var test = new tsUnit.Test(); // add your test class (you can call this multiple times) test.addTestClass(new CalculationsTests.SimpleMathTests()); // Use the built in results display test.showResults(document.getElementById('results'), test.run());
Неудобства
Однако мне хотелось заточить движок под CamlJs так, чтобы с тестами было работать проще, и в случае проваленного теста было бы проще найти ошибку. Я видел следующие 2 основных неудобства при работе с тестами:
Во-первых, CamlJs генерирует неформатированный XML, и как следствие строка-образец тоже должна быть неформатированной. В итоге в тесты приходилось запихивать километровые строки с XML, что выглядело очень некрасиво и совершенно нечитабельно.
Во-вторых, в случае больших комплексных запросов с кучей вложенных скобочных выражений, когда тест падал, чрезвычайно сложно было найти, в чем же проблема. Например, попробуйте выяснить, что именно здесь не так :)
Идеальным решением в этом случае была бы, безусловно, подсветка отличий (diff).
Давайте рассмотрим, как я решил эти проблемы.
Сравнение форматированного и неформатированного XML в JavaScript
После нескольких попыток, я предпочел решить проблему хранения XML-простыней в коде тестов путем перегонки обоих кусков XML в объекты и затем в JSON через JSON.stringify и сравнения уже JSON-строк. Таким образом, я смогу хранить XML-фрагменты в коде тестов в форматированном виде, что улучшит читабельность кода.
Когда-то давно я писал про то, что в SharePoint есть встроенная функция для превращения XML-строки в dom element. Эта функция прекрасно работает, однако оказалось, что получившийся dom-объект нельзя сериализовать через JSON.stringify (вообще никакие dom-элементы через JSON.stringify не выводятся)! Готового решения этой проблемы я не нашел, но быстро написал простенькую функцию на основе фрагмента из интернетов, которая dom-элемент перегоняет в объект JavaScript. Функция эта выглядит следующим образом:
function elementToObject(el) { var o = {}; var i = 0; if (el.attributes) { for (i; i < el.attributes.length; i++) { o[el.attributes[i].name] = el.attributes[i].value; } } var children = el.childNodes; if (children.length) { i = 0; for (i; i < children.length; i++) { if (children[i].nodeName == '#text') o['#text'] = children[i].nodeValue; else o[children[i].nodeName] = elementToObject(children[i]); } } return o; }
Дальше элементарно:
var domElement = CUI.NativeUtility.createXMLDocFromString("<root>" + xml + "</root>"); var obj = elementToObject(domElement); return JSON.stringify(obj.root, undefined, 2);
В результате XML из скриншота выше преобразуется вот в такой кусок более ёмкого и читабельного JSON'а:
{
"Where": {
"And": {
"In": {
"FieldRef": {
"Name": "Category" },
"Values": {
"Value": {
"Type": "Integer",
"#text": "10"
}
}
},
"Leq": {
"FieldRef": {
"Name": "ExpirationDate"
},
"Value": {
"Type": "Date",
"Now": {}
}
}
}
},
"OrderBy": {
"FieldRef": {
"Name": "ExpirationDate",
"Ascending": "False"
}
}
}
Обратите внимание: неважно, что этот JSON не идеальный. Нет задачи сделать идеальный JSON, есть задача сравнить 2 XML'а.
Итоговый код моих юнит-тестов для проекта CamlJs (включая хелпер для реализации преобразования XML->Json) можно найти на Codeplex.
Возвращаясь к неудобствам: даже имея результаты выполнения в форматированном JSON, сравнение запросов в случае проваленного теста по прежнему являлось серьезной проблемой, т.к. запросы большие и приходилось постоянно скроллить туда-сюда, пытаясь найти отличия. Так что давайте теперь рассмотрим, как я сделал diff :)
Визуальный Diff на JavaScript
На самом деле, тут всё банально, есть готовая библиотека, которая делает diff на js. Мне пришлось только немного пропатчить tsUnit, чтобы обеспечить интеграцию. В итоге получилась вот такая картинка:Сразу видно, что происходит, удобно, работа ускорилась в разы.
Библиотека не идеальная и ищет отличия в словах (т.е. если у вас текст без пробелов, толку от неё мало). Лично мне хватило, т.к. я использую форматирование JSON и как следствие, везде где надо пробелы стоят. Однако есть более крутые библиотеки, и их довольно много. В частности, есть очень хорошая либа google-diff-match-patch от Neil Fraser, я на неё поздновато наткнулся, но алгоритм там более подходящий для моих целей, и библиотека сама очень развитая.
Выводы
Если подумать серьезно о том, что я делал выше, можно сказать следующее:Скорость и качество разработки непосредственно зависят от того, насколько хорошо вы подогнали под себя свою рабочую среду. Если вы постоянно будете терять много времени на выполнении какой-то пусть даже несложной второстепенной задачи, внимание начнет рассеиваться, а эффективность - падать. Так что заботьтесь о том, чтобы работать было удобно.
С другой стороны, очень важно здесь не переусердствовать. Нельзя писать систему чтобы писать систему :) Есть простое решение проблемы - отлично, применяем быстро, и продолжаем работать над основным проектом. Быстро не получилось? Значит без этого "удобства" можно обойтись.
Так что, удобной разработки, и до скорых встреч!
пятница, 16 августа 2013 г.
XSLT мертв. R.I.P. Да здравствует CSR!
Да, конечно, мы всё также можем использовать веб-части с XSLT и почти всегда это будет работать. Но есть нюансы:
- Изъят мощнейший инструмент для разработки XSLT - SharePoint Designer. Точнее, изъята та его часть, которая относилась к генерации XSLT. Кое-что можно по-прежнему выжать из местами оставшихся кнопок на Ribbon в Code View, но даже для простейших представлений код веб-частей генерируется нередко нерабочий. Иными словами, это уже неподдерживаемый функционал, которым пользоваться нереально.
- XSLT-преобразования очевидно уже стоят в низком приоритете для команды тестирования SharePoint. Как следствие - уже сейчас, в SP2013, начинают накапливаться баги, связанные с работой XSLT. Пример: в js, который используется для свертывания/развертывания групп при наличии группировки в DataFormWebPart - в SP2013 содержит ошибку. Т.е. группировку в этой веб-части невозможно использовать "OOTB", придется этот баг исправлять подменяя SharePoint'овскую js-функцию. Я наткнулся на этот баг, когда пытался мигрировать свой английский блог, который у меня сделан на SharePoint 2010 :(
- Представлен концептуально новый подход к отображению данных (Client Side Rendering ака CSR), и сейчас ни одна веб-часть в SharePoint по умолчанию более НЕ использует XSLT преобразования. Все списки показываются с помощью CSR, и результаты поиска - тоже.
- CSR действительно лучше, и нет ни единой причины не предпочесть его старому подходу. Это JavaScript templating engine, пусть корявый, но всё-таки JS как язык является гораздо более мощным, более развитым и более современным средством разработки, чем XSLT. Под JavaScript, словно грибы, растут фреймворки, создаются метаязыки (coffee script, TypeScript, Script#, etc.), пишутся библиотеки, расширяются его возможности (html5) и т.д. и т.п. А когда вы слышали последний раз про усовершенствования в XSLT? :)
И если в SP2010 еще можно поработать с XSLT (но без излишнего рвения, не забывайте что могут возникнуть проблемы с миграцией), то если вы начинаете новый проект на SP2013, забудьте об XSLT. Начинайте учить CSR.
CSR сыроват и не без своих тараканов. Но он уже лучше XSLT. Многие вещи на CSR делаются в 3 раза быстрее, чем на XSLT, а еще есть вещи на XSLT вообще нельзя было сделать, а на CSR они делаются на раз-два-три.
В проекте SPTypeScript мы сделали на момент написания этой статьи уже 6 примеров по использованию CSR:
- Разбиение формы на вкладки
- Lookup-поле с автокомплитом, работающим по данным поиска
- Кастомная валидация полей формы списка
- "Color coding" на CSR
- Custom Field на CSR (пример сделан на основе того что есть на MSDN, но внутри код гораздо более правильный и понятный)
- Custom List View (очень простой пример из серии "как начать")
Также, совсем недавно я читал доклад про CSR - очень полезно для старта и освещены некоторые моменты, которых вы врядли где-то еще найдете (да, CSR как это принято в SharePoint'е, документирован процентов на 15% отсилы - а в интернетах еще пока информации маловато).
Подводя итог: на мой взгляд, будущее рендеринга данных в SharePoint - это что-нибудь типа CSR + TypeScript + KnockoutJs. А XSLT, он мертв. И несмотря на человекомесяцы в обнимку с ним и целую пачку статей про него в этом блоге, наверное это и к лучшему :)
понедельник, 5 августа 2013 г.
Как я исправил невоспроизводимую ошибку SharePoint с помощью UX-паттерна
Дано
Есть некоторый существующий программный модуль, который позволяет массово создавать сайты. За раз создается обычно от 2х до 18ти сайтов. После создания сайты программно настраиваются. Клиенты используют этот модуль пару раз в месяц или даже реже .
Иногда один или несколько сайтов не создаются по неизвестной причине. Ошибка валится на Webs.Add и она плавающая: то есть, то нет. Более того, по закону подлости она возникает исключительно на production environment. И это еще не всё, когда ко мне попала эта задача, логи с последней ошибкой уже потерлись, поэтому я даже не располагал сообщением об ошибке!...
И тем не менее, я смог решить эту задачу. Прежде чем читать решение, подумайте, что бы вы сделали в такой ситуации?
Стандартный подход
Стандартным подходом было бы поочередное исследование кода решения и затем кода Webs.Add через рефлектор, и попытка понять в чем может быть дело (скорее всего неудачная, т.к. Webs.Add довольно быстро уходит в Unmanaged код).
Можно конечно пробовать разные варианты параметров или порядка или какие-нибудь задержки... Но поскольку ошибка не воспроизводится нигде кроме production, а production можно обновлять только раз в 1-2 месяца, да еще и используется этот модуль не интенсивно... В общем методом "тыка" можно было бы растянуть проблему на годы. Собственно, это и происходило - когда я взялся за проблему, она существовала уже больше года.
UX подход
Давайте попробуем подойти к проблеме с точки зрения UX. Итак, есть некий интерфейс, пользователь заполняет некоторые поля, выбирает сколько создавать сайтов и т.п. В конце концов, он жмет на кнопку "Создать" - и сайты создаются ...или не создаются, как повезет. Интерфейс в том виде, в котором он попал ко мне, просто глотал все ошибки, всвязи с чем даже непонятно было, какой конкретно сайт не был создан. В результате пользователь шел и проверял, все ли сайты создались, и если нет - то досоздавал их вручную.
В моих любимых Apple UX Guidelines есть следующий пункт:
Display an informative, actionable alert message when something goes wrong. An alert should clearly convey what happened, why it happened, and the options for proceeding. Describe a workaround if one is available and do whatever you can to prevent the user from losing any data. Avoid using an alert to deliver information that the user can’t act upon.
В этих четырех предложениях - буквально всё, что вам нужно знать об обработке ошибок! На всякий случай по-русски:
Отображайте информативное, активное сообщение об ошибке (алерт), если что-то пошло не так. Сообщение должно четко описывать что случилось, почему это случилось, и что делать дальше. Опишите воркэраунд, если он существует, и сделайте всё возможное, чтобы предотвратить потерю данных. Не используйте алерты для отображения информации, если пользователь не может ничего с ней сделать.
Что это означает в нашем случае?
- Нельзя "глотать" ошибки создания сайтов.
- При отображении ошибки, необходимо предоставить не только информацию о том, что случилось и почему, но также необходимо предоставить возможность действия.
Решение очень простое, но при этом надежное и удобное.
Во-первых, я добавил информацию об ошибке: в случае, если один или несколько сайтов не были созданы, отображается таблица со статусом по каждому из сайтов (Success/Failure).
Во-вторых, рядом с Failure я добавил кнопку Retry. ВСЁ!
Если нажать Retry, модуль попытается пересоздать соответствующий сайт. Если все сайты в итоге удалось создать - то внизу появится ссылка перехода на корневой сайт проекта. Если сайты не пересоздаются - вверху также написано, что делать в этом случае (обратиться к команде поддержки и передать такие-то детали ошибки).
Такие улучшения интерфейса безусловно повлекли за собой некоторое количество работы и немного рефакторинга, т.к. для того, чтобы сделать возможность Retry, пришлось отделить код создания сайтов в обособленный метод, но в любом случае вся работа заняла 2 дня - с большими перерывами на чай и бильярд. Стоит ли говорить, что investigation мог бы занять практически сколько угодно времени и ни к чему не привести?
Отдельно отмечу: это решение полностью закрывает задачу. ПОЛНОСТЬЮ. С точки зрения пользователя, проблемы больше нет. Нажать пару раз кнопку Retry, при том что интерфейс используется пару раз в месяц - да это вообще не вопрос, верно же?
Выводы
- Ошибку можно исправить, не исправляя ее
- Я уже писал, что знания по UX меняют сам подход к разработке. И к исправлению ошибок - тоже. Всё, что мы с вами видели выше - это просто подход к решению с другой стороны. Много раз я убеждался, что это реально работает и дает плоды.
пятница, 2 августа 2013 г.
Usability и UX в SharePoint
Клиенты нормально относятся к страшным и неудобным интерфейсам
Для публичных сайтов и для экстранетов еще можно что-то предлагать, но на интранетах все очень усердно пытаются сэкономить. Даже здесь, в Европе. Факт.
четверг, 7 марта 2013 г.
Динамическая форма списка в SharePoint Designer
<tr> <td width="190px" valign="top" class="ms-formlabel"> <H3 class="ms-standardheader"> <nobr>Title<span class="ms-formvalidation"> *</span> </nobr> </H3> </td> <td width="400px" valign="top" class="ms-formbody"> <SharePoint:FormField runat="server" id="FormField1" ControlMode="Edit" FieldName="Title" __designer:bind="{ddwrt:DataBind('u',concat('ff1',$Pos),'Value','ValueChanged','ID',ddwrt:EscapeDelims(string(@ID)),'@Title')}"/> <SharePoint:FieldDescription runat="server" id="FieldDescription1" FieldName="Title" ControlMode="Edit"/> </td> </tr>
Таких кусков в XSLT коде столько, сколько у вас на форме полей.
Этот подход приводит к очевидным неприятностям: если в список добавилось новое поле, форму придется перегенерить. Что неприятно: списки они ж ведь на то и списки, чтобы пользователи сами туда могли добавлять поля!…
Оказывается, решить эту проблему – очень легко.
среда, 6 марта 2013 г.
Способы кастомизации форм списков в SharePoint 2013
вторник, 26 февраля 2013 г.
Права доступа в SharePoint
среда, 24 октября 2012 г.
Вызов веб-сервиса без кода: DataFormWebPart!
В общем-то, скажете вы, веб-сервис дернуть ведь проблемы нет, берешь jQuery (или например уже заточенные под это SPServices) и дергаешь… К сожалению, не всегда всё так просто. Например, что если веб-сервис требует авторизацию (а пароли на клиенте хранить ну уж никак нельзя, согласитесь), или же доступ к веб-сервису ограничен и он доступен только для определенных IP-адресов (т.е. принципиально возможен только server-side вызов). А бывает и так, что даже стандартные веб-сервисы SharePoint из _vti_bin недоступны извне (их часто блокируют). Что делать в этом случае?
четверг, 26 июля 2012 г.
Обзор приемов работы с Site Pages
В одном из недавних постов я рассказал, почему я предпочитаю использовать Site Pages вместо Application Pages. Одна из важных мыслей в той статье касалась того, что для эффективного создания решений на основе Site Pages, нужно научиться по-другому думать и использовать совершенно другие блоки и инструменты.
Сегодня я хочу рассказать, какие существуют приемы разработки на основе Site Pages. Какие имеющиеся в SharePoint инструменты можно использовать, и как с их помощью можно очень быстро создавать гибкие и прекрасно кастомизируемые решения. Будет также реальный пример, как можно за 5 минут разбить поля на форме списка на вкладки.
А в свете выхода SharePoint 2013, ориентация на Site Pages особенно важна: ведь например возможности разрабатывать Application Pages в новых SharePoint Apps просто не существует…
Ну что ж, давайте начнем!
четверг, 28 июня 2012 г.
Как скрывать Ribbon в SharePoint
Во-первых, способов скрывать Ribbon – далеко не один. А в SharePoint, если есть несколько способов что-то сделать, скорее всего чем-то эти способы отличаются. И в одних ситуациях можно применять, в других нет.
Во-вторых, если скрывать через css как было обозначено выше, перестают работать статусы (SP.UI.Status) и оповещения (SP.UI.Notify). В общем, не всё так просто!
среда, 27 июня 2012 г.
Site Pages vs Application Pages
Еще буквально полгода назад я почти не задумываясь создавал Application Pages под каждый чих и был этим вполне счастлив. А что, пользоваться ими просто и привычно: это суть обычные ASP.Net страницы с обычным CodeBehind, и любой ASP.Net-чик чувствует себя с ними как дома.
Однако сейчас, я почти все формы, и даже странички настроек, создаю на основе Site Pages. И этому есть очень серьезные причины.
Чтобы понять, как я к такому пришел, нужно понять, чем Site Pages отличаются от Application Pages.
вторник, 26 июня 2012 г.
SharePoint и XSLT: область ссылок «быстрого доступа»
Все вы помните ссылку «Добавить новый элемент» внизу представлений списков SharePoint:
В связи с наличием этой ссылки возникает два естественных вопроса:
- Как поменять текст этой ссылки? (например вместо “Добавить новый элемент”, я хочу текст “Добавить товар” или “Добавить продукт” и т.д.)
- Как добавить еще пару ссылок рядом со ссылкой для создания нового элемента?
Например, некий список с заявками может иметь вот такие ссылки быстрого доступа:
, где ссылка Configure application route открывает редактор маршрута заявки (т.е. можно определить, кто и в каком порядке должен одобрять эту заявку).
В этой статье я расскажу, как реализовать такие изменения. Рассмотрим два способа:
- no-code — для частных решений, быстрых правок и кастомизаций Office365
- программно – что может потребоваться для создания коробочных решений
понедельник, 25 июня 2012 г.
«Вы уверены?» Confirmation-диалоги в SharePoint
Конечно, чаще всего обычного JavaScript’ового confirm’а вполне достаточно. Однако, иногда хочется показать более красивый диалог, предложить другие варианты выбора, добавить иллюстрацию или параграфы в текст... Многие для такой задачи используют jQuery UI, jQuery плагины или сторонние JS-библиотеки, но мне кажется, этого делать не стоит: в SharePoint’е уже есть диалоги, они прекрасно вписываются в общий дизайн и предоставляют массу настроек. Мне кажется, нет ни одной причины, чтобы их не использовать.
Ниже я представлю код класса Dialog, который позволяет легко отображать подобные диалоги, и выполнять какие-нибудь действия в случае, если пользовать нажал «Да». Пользоваться классом предельно просто:
My.Dialog.confirm(
'Вы уверены?',
'Вы уверены, что хотите отменить процесс подписи?',
function () {
alert('Пользователь нажал "Да"!');
});
При этом не потребуется создавать отдельную aspx-страницу: SharePoint’овские модальные диалоги имеют режим, в котором они создаются полностью динамически.
среда, 30 мая 2012 г.
Реализация паттерна UniqueURLs в SharePoint
Это означает, что когда вы нажимаете на некой странице на кнопку или ссылку, и состояние (внешний вид) страницы меняется (неважно, был ли это postback или callback) — то у этой страницы должен измениться и адрес (т.е. текст в строке браузера):У каждого состояния страницы должен быть уникальный адрес
Если каждый раз адрес менять не получается (например, в случае карт), то нужно предусмотреть кнопку «Получить ссылку» или что-то в этом духе.
Естественно, что если другой пользователь, или этот же, но позже — переходит по ранее запомненному уникальному адресу, то состояние страницы должно восстановиться:
Надеюсь, задача понятна, а теперь давайте рассмотрим реализацию, и некоторые тонкости при реализации этого паттерна в SharePoint.
вторник, 29 мая 2012 г.
PowerShell-скрипт для генерации задач
На основе этого скрипта, я верю, что вам удастся сформировать решения для генерации более реалистичных данных для ваших тестов.










