понедельник, 20 октября 2014 г.

Выделение цветом важных данных… в JSGrid!

Пока дефинишены пишутся, решил написать про некоторые простые методы работы с JSGrid, на примере подсветки данных в гриде.

Также в статье приводится довольно много сведений общего плана про JsGrid, которые очень важны для понимания, как это все работает.

вторник, 14 октября 2014 г.

Документирую SharePoint JSGrid

Тружусь сейчас над TypeScript-дефинишенами для JsGrid. Работа долгая и тяжкая: для каждой-каждой функции в JS файле из 24.5k строк нужно определить типы параметров и возвращаемого значения, понять что эта функция делает, и все это описать. Потратил уже несколько дней, по 5+ часов, и пока еще только в начале пути... Между прочим, даже главный шарепойнтовский файл, SP.debug.js, имеет меньший размер - ~21k строк...

Скриншот процесса:


суббота, 11 октября 2014 г.

Ускоряем SharePoint-разработку

SharePoint особенно знаменит затыками "на последнем километре" и багами в совершенно неожиданных местах. Поэтому чем больше решение тестируется, и чем раньше начинаешь тестировать - тем лучше.

Однако, тестировать SharePoint-решения не так-то просто. Их ведь надо еще задеплоить. А пока деплоишь, заснуть можно!... И потом сиди вспоминай, что вообще протестировать-то хотел...

И ведь все знают, что нередко бывает так: правишь пару символов - это занимает буквально секунды, и потом деплоишь пять минут, чтобы посмотреть результат. Потом видишь что не помогло - и по новой, еще пять минут деплоя... И не думайте, что концентрация от этого не теряется. Еще как.

Смешно сказать, но я до сих пор вспоминаю SPVisualDev, расширение для Visual Studio для работы с SharePoint 2007. Мгновенное развертывание в 12 hive, отличная поддержка удаленной разработки и отладки, всё удобно, быстро и просто. Этого в SharePoint Developer Tools нет и в помине даже сейчас, в 2014!...

Да, к сожалению, SharePoint с точки зрения скорости разработки сильно далеко от того, что сейчас происходит во "внешнем мире". А происходит, к слову, там, вот что:

Тем временем во "внешнем мире"...

  1. Проект IDE Light Table набрал $300k на Kickstarter. Основная идея проекта - в "живой разработке", когда IDE всегда находится в режиме отладки (!). Т.е. обычного режима не-отладки просто не бывает. Только начали писать код - тут же можно смотреть состояние переменных, и т.д.
  2. Многие современные IDE поддерживают режим "live development". Пример - опенсурсная IDE Brackets. Вот одноминутная демка:

  3. Visual Studio и ASP.Net развивают проект "Browser Link". Идея состоит в том, чтобы инжектить Signal-R скрипт на страницу, что позволяет в дальнейшем этой страницей управлять: релоадить целиком или частями, изменять, и т.д. "Browser Link" можно расширять с помощью Visual Studio extensions. В составе Web Essentials есть несколько крайне интересных расширений такого плана. Скотт Хансельман в этом 4х-минутном видео подробно все это демонстрирует:

  4. Visual Studio "14" CTP 4 по умолчанию использует open source компилятор Roslyn, который имеет расширенные средства для работы с неполными синтаксическими деревьями, благодаря чему им удалось сделать on-fly incremental compilation. Идея в том, чтобы пропустить шаг Build. Т.е. меняем код, сохраняем, переходим в браузер, F5 - и смотрим изменения.
  5. Помимо общеизвестного 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:
Опять же. Сделано буквально на коленке, какой-то мужик посидел пару вечеров и написал. Вы, дорогой читатель, тоже могли бы такое написать, даже в одиночку. И вас, дорогие читатели, смею заметить, дофига.

Моя попытка: CamlJs-Console


Вообще, на удивление, многие вещи не настолько сложные, как кажутся. Примерно в середине сентября я начал работать над крайне интересным проектом под названием CamlJs-Console. Это расширение для хрома, позволяющее создавать CamlJs-запросы в режиме live development, очень похожем на пример выше про Brackets. Пишешь - и сразу же видишь результат.

Оказалось, что Chrome Extensions пишутся на HTML+CSS+JavaScript. Очень просто. При желании можно даже создавать Chrome Extension + Web App (или даже SharePoint App) из одного исходного кода...

БЛИН! Я когда только-только сделал самую первую версию у себя на локальной машине в 3 часа ночи :), я сидел тестировал и не мог остановиться. Это настолько отличалось от вот этого "поправил-5 минут деплой", что чувствовалось как магия какая-то. Серьезно :)

Общий вид:



А вот live preview данных из списка:


Мне даже удалось подцепить туда интеллисенс (на основе 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. Если у кого есть идеи/мысли на этот счет, с удовольствием поучаствую в обсуждении - кидайте комментарии! :)

понедельник, 22 сентября 2014 г.

Загадочно бесполезный SPList.GetDataTable

Копал SPQuery в dotPeek, и наткнулся на интересный кусок кода:

if (this.m_Query.ConsiderManagedPipe
    && this.m_Query.SafeArrayFlags == null 
    && (this.m_Query.CalendarDate == DateTime.MinValue && !this.m_Query.IncludeMandatoryColumns) 
    && (this.m_Query.ViewFieldsOnly 
        && (this.m_Query.DataTableOptions & SPListGetDataTableOptions.RetrieveLookupIdsOnly) != SPListGetDataTableOptions.None 
        && ((this.m_Query.DataTableOptions & SPListGetDataTableOptions.UseBooleanDataType) != SPListGetDataTableOptions.None 
        && (this.m_Query.DataTableOptions & SPListGetDataTableOptions.UseCalculatedDataType) != SPListGetDataTableOptions.None)) 
    && (!string.IsNullOrEmpty(this.m_Query.ViewFields) 
    && string.IsNullOrEmpty(this.m_Query.ViewAttributes) 
    && (this.m_List.BaseType != SPBaseType.DiscussionBoard && !this.QueryIncludesMultiValueLookup(this.m_Query.ViewFields)) 
    && !this.m_List.HasUniqueScopes))
{
      ULS.SendTraceTag(963012918U, (ULSCatBase) ULSCat.msoulscat_WSS_Database, ULSTraceLevel.Verbose, "SPListItemCollection.EnsureListItemData: Retrieving data through the managed pipe.");
      this.m_bUseManagedPipe = true;
}

Интересно, думаю. Неспроста! Сами посмотрите: условия все такие, логически связанные с performance. Может это какой-нибудь хитрый performance-boost такой?

Ну, начал разбираться...

суббота, 20 сентября 2014 г.

3 простых способа подружить KnockoutJs и SharePoint

Попал на старый проект, SharePoint 2010. Бывает, что тут сделаешь! Спасаюсь от скуки только благодаря KnockoutJs :) Всвязи с чем - этот пост.

KnockoutJs как MVVM фреймворк на самом деле ничем не уступает тому же Angular'у. Единственная вещь, которую нужно очень хорошо изучить и вызубрить в Knockout - это когда скобки () нужны, а когда нет, и как этот ko observable wrapping вообще работает. Преодолев этот порог, больше с Knockout проблем не возникает. Angular конечно помощнее, более фундаментальный, но и всякой магии в нем намного больше, траблшутить крайне сложно, и модель освоения Angular действительно вот такая (проверено на собственном опыте):



Так что, в своих SharePoint-проектах я преимущественно использую именно Knockout. И в этом посте я опишу три простых техники использования Knockout совместно с SharePoint.

пятница, 29 августа 2014 г.

Поток мыслей: про косячников, внешний мир, и скрытую прелесть Apps

Вот не понимаю, как можно в SharePoint'е кого-то называть косячниками? :)

Ошибка в SharePoint'е может возникнуть абсолютно в любом месте. В самом неожиданном! Самые безобидные действия могут повлечь за собой ужасающие последствия...

четверг, 20 марта 2014 г.

Введение в CSR

(даже если вы уже смотрели доклад, эта статья все равно может оказаться полезной для вас, т.к. некоторые моменты здесь рассмотрены намного более тщательно, и приводится гораздо больше примеров)

CSR - это способ отображения данных в SharePoint 2013, пришедший на смену скоропостижно скончавшемуся XSLT. С помощью CSR отображаются и представления списков, и формы списков, и результаты поиска. Хотя бы поэтому нужно знать, что такое CSR и как он работает (о чем, собственно, я и намерен рассказать в этой статье).

Ну и конечно немаловажно, что у CSR есть API, и его можно использовать для создания кастомизаций тех самых представлений списков, их форм, и результатов поиска. И об этом мы тоже поговорим чуть ниже.

Как устроен CSR

CSR - это подсистема отображения данных, главным отличием которой является тот факт, что она клиентская, т.е. реализована целиком на JavaScript. Иными словами, на стороне сервера рендерятся только данные в Json-формате, которые затем с помощью неких JavaScript-функций превращаются в HTML и затем этот HTML вставляется в DOM страницы.


Т.о. CSR представляет собой своеобразный конвертер, который принимает входящие данные, и возвращает HTML-строку на основе этих данных.

Для удобства CSR реализован не в виде одной огромной функции, а в виде множества javascript-функций, каждая из которых рендерит свой собственный кусочек HTML. Эти функции как правило вызывают одна другую, и таким образом как бы вложены друг в друга.

Исследовав исходный код CSR в файле /_layouts/15/clienttemplates.debug.js, я составил полную схему, которая демонстрирует порядок вызова этих функций:


OnPrePrender и OnPostRender отличаются от Render*-функций тем, что являются событиями, срабатывающими соответственно перед процессом генерации HTML и сразу после завершения этого процесса. Эти события служат в основном для расширений, в то время как Render*-функции выполняют основную работу по отображению.

Если взять в качестве примера представление произвольного списка, можно отметить области, которые были срендерены каждой из этих Render*-функций:


Ну что же, пока что довольно просто и логично, неправда ли?

Используем CSR API

Есть два основных способа кастомизации CSR:

  1. Подписывание на события OnPreRender и OnPostRender
  2. Подмена (override) функций-шаблонов Render*.
Технически оба способа доступны через единое API, а именно через функцию

SPClientTemplates.TemplateManager.RegisterTemplateOverrides(options)

Функция RegisterTemplateOverrides принимает в качестве единственного параметра объект, содержащий всю необходимую информацию для кастомизации и имеющий следующую структуру:

  • OnPreRender
  • Templates
    • View
    • Header
    • Body
    • Footer
    • Group
    • Item
    • Field
  • OnPostRender
В полях OnPreRender и OnPostRender можно указать одну функцию или же массив функций. Пример:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
  OnPreRender: function() { console.log('CSR OnPreRender'); },
  OnPostRender: [
    function() { console.log('CSR OnPostRender'); },
    function() { alert('CSR OnPostRender'); }
  ]
});

Для полей внутри составного поля Templates можно указывать функцию или строку. В случае строки внутри можно использовать токены, ссылающиеся на JavaScript-контекст:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
  Templates: {
    Footer: "Hello world from <#= ctx.ListTitle #>!"
  }
});

Результат работы такой функции будет следующий:


Как видите, в Footer представления списка добавилась наша строка, и токен был заменен значением, соответствующим заголовку списка.

Вместо строки-шаблона можно использовать функцию, вот как это выглядит:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
  Templates: {
    Footer: function(ctx) {
      return "Hello world from " + ctx.ListTitle + "!";
    }
  }
});

При генерации HTML-строк в CSR очень удобно использовать функцию String.format, которая автоматически уже есть на любой странице SharePoint'а и позволяет делать то же самое, что серверный C#-овский String.Format.

SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
  Templates: {
    Footer: function(ctx) {
      return String.format("Hello world from {0}!", ctx.ListTitle);
    }
  }
});

Обратите внимание, что обработчик должен принимать в качестве параметра объект ctx - это тот самый JSON-объект, который генерируется на стороне сервера. И естественно, возвращает функция обработчика HTML-строку.

Замечание: при регистрации override'а, исходный шаблон будет ЗАМЕНЕН, т.е. больше он вызываться не будет, а вместо него будет запускаться ваш шаблон. Поэтому, если в предыдущем примере заменить Footer на Body, то результат будет такой:


Как видите, записи списка более не отображаются. Т.о. важно понимать, что если вы хотите заменить шаблон не полностью, а только одну малую его часть, вам все равно придется генерировать каким-то образом все остальное.

Чтобы этого избежать, можно попробовать вместо замены шаблона использовать событие OnPostRender, и заменять/дополнять нужный вам элемент с помощью jQuery или селекторов.

Вызов других шаблонов

Как я уже упоминал, шаблоны CSR как бы вложены друг в друга, потому что друг друга вызывают. Поскольку вам придется подменять эти шаблоны в процессе кастомизаций, возникает естественный вопрос: а как вызывать низлежащие шаблоны?

Оказывается, когда JSON-объект сгенерированный на стороне сервера начинает использоваться в качестве контекстного объекта в CSR, этот объект дополняется всякими полезными при обработке полями и методами. И в частности, в нем можно найти все Render*-функции:



Таким образом, вызов низлежащих функций не представляет никаких проблем. Пример:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
  Templates: {
    View: function(ctx) {
      return ctx.RenderHeader() + ctx.RenderBody() + ctx.RenderFooter();
    }
  }
});

Подключение к представлению

Куда же нужно положить JavaScript-код с вызовом RegisterTemplateOverrides, чтобы кастомизация заработала? Очень просто: нужно положить этот код на страницу сразу после представления списка.

Представленный выше пример я сделал следующим образом: перешел в режим редактирования страницы, добавил веб-часть Script Editor, и добавил туда вышеозначенный код:



Чтобы код работал из любого места страницы, необходимо обернуть его в широко известный SP.SOD.executeFunc:

SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {

  SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
    // ...
  });

});


В этом случае можно подключать хоть в header, и делать это любым способом (через masterpage, через page layout, через script link и т.п.) - все сработает нормально.

Теперь давайте посмотрим, как сделать так, чтобы кастомизировался только нужный нам view (если на странице несколько view разных списков). Для этого в объекте опций предусмотрены 3 дополнительных поля: ViewStyle, ListTemplateType и BaseViewID.

  • ListTemplateType - номер шаблона списка (перечисление ListTemplateType). Например, для того, чтобы наш шаблон срабатывал только на списки типа "Задачи" ("Tasks"), нужно указать ListTemplateType=171:

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
      Templates: {
        Footer: function(ctx) {
          return String.format("Hello world from {0}!", ctx.ListTitle);
        }
      },
      ListTemplateType: 171
    });
    
  • ViewStyle - тип представления. Имеет смысл указывать один из существующих типов представления, который можно задать в настройках любого представления списка в SharePoint:
    ID для этих ViewStyle можно легко выдрать из того самого select'а на странице настроек представления:

  • BaseViewID - это значение свойства BaseViewID у View, его можно подсмотреть в PowerShell или в SharePoint Designer.
Используя эти три поля, можно "нацелить" кастомизацию строго на нужный список, чтобы никакие другие списки на странице не были затронуты.

Про JSLink

Я с удивлением замечаю, что JSLink для многих является практически синонимом CSR. Многие статьи про CSR имеют заголовок из серии "Новая фича шарепойнта - JSLink", и дальше повествование идет о CSR... Я считаю своим долгом рассеять эти заблуждения.

Правда заключается в том, что JSLink не имеет никакого отношения к CSR вообще. Они не связаны. Они могут использоваться вместе, но это совершенно необязательно. CSR прекрасно работает без всяких JSLink, как мы видели выше. А JSLink прекрасно работает без CSR-скриптов.

JSLink - это просто способ прицепить некий javascript к некоему объекту SharePoint. Свойство JSLink присутствует у многих объектов SharePoint:
  • SPContentType
  • SPField
  • SPForm (кстати, по непонятным причинам, мне не удалось заставить работать именно SPForm.JSLink, хотя согласно моим исследованиям через dotPeek, все должно прекрасно работать - не знаю в чем было дело, может быть у вас получится?...)
  • SPView
  • XsltListViewWebPart
  • ListFormWebPart
  • ...
Как только мы проставляем это свойство у некоего объекта SharePoint, везде где он появляется на портале, везде будут добавлены соответствующие js-файлы.

Фактически эти js-файлы добавляются через контрол ScriptLink, что кстати говоря гарантирует автоматическое решение проблемы дубликатов - если кто не знал, контрол ScriptLink не позволяет загрузить на страницу дважды файл с одинаковым путем. Т.е. если поместить 2 ScriptLink-контрола на одну страницу и в обоих прописать один и тот же файл, то будет загружен лишь один файл (но сильно не обольщайтесь, дубликаты отслеживаются только по url, а не по контенту).

Внутри свойства JSLink можно использовать некоторые токены и особый синтаксис:
  • Во-первых, можно загружать несколько файлов сразу, используя в качестве разделителя вертикальную черту ("|").
  • Во-вторых, можно использовать токены "~site" и "~sitecollection", которые будут заменены на соответствующие URL.
  • В-третьих, можно использовать "(d)" на конце, чтобы грузить файлы в режиме SOD (Script-On-Demand). Т.е. вместо того, чтобы быть загруженным на страницу сразу, скрипт будет зарегистрирован в SOD системе и будет действительно загружен только в том случае, если кто-то к нему обратиться, используя "SP.SOD.executeFunc" или подобный метод. Внимание! Только скрипты, поддерживающие SOD, могут быть загружены таким способом.
В общем, JSLink - это очень удобный способ загрузки скриптов в SharePoint'е, и безусловно этот способ можно и нужно использовать вместе с CSR. Однако, важно понимать, что JSLink никак не влияет на CSR, и например можно подцепить CSR-скрипт к объекту списка А, и этим скриптом применять кастомизации к совершенно другому списку В....

Также, надеюсь понятно, что через CSR можно грузить далеко не только CSR-скрипты, но и любые сторонние файлы - например, jQuery, knockout.js и т.п.

Заключение

На мой взгляд CSR - это отличная замена XSLT. Огромным преимуществом CSR является то, что CSR базируется на JavaScript. Сейчас столько всего происходит вокруг JavaScript! Буквально каждую неделю появляются какие-нибудь новые классные библиотеки и фреймворки, создаются инструменты, и т.д. Мне кажется, CSR - это отличный шанс привнести современные достижения web-технологий в SharePoint, и начать создавать по-настоящему удобные и качественные решения.

Конечно, как и везде в SharePoint, у CSR есть свои тараканы, и следует быть крайне осторожными в оценке трудозатрат, особенно если вы пока еще мало работали с CSR.