понедельник, 22 апреля 2013 г.

Атрибуты ViewFields

С первого взгляда, ViewFields - это попросту список полей, которые отображаются в том или ином представлении списка. В C# API этот список полей представлен классом SPViewFieldCollection, который является по сути коллекцией строк, представляющих собой InternalName полей, отображаемых представлением.

Немногие знают, что каждое поле внутри ViewFields (элементы FieldRef) еще может иметь атрибуты, которые определяют, как именно это поле включается в представление списка. Большая часть этих атрибутов присутствует у исходных SPField (однако не все атрибуты SPField переопределяемы!), и установка их в FieldRef позволяет просто переопределить (override) настройки поля в рамках конкретного представления.

Меню элемента


Пожалуй, одни из самых известных и используемых атрибутов элементов ViewFields - это LinkToItem и ListItemMenu. Они позволяют "подцепить" соответственно ссылку на Display форму элемента и меню элемента на произвольное поле списка - без всяких там XSLT преобразований. Нередко эти атрибуты используются, например, в библиотеках документов, где вместо имени файла предпочтительнее отображать заголовок документа (Title) - и соответственно, меню элемента должно быть перенесено на заголовок.

<FieldRef Name="MyField" LinkToItem="TRUE" ListItemMenu="TRUE" />

В SharePoint 2013, к слову, добавился похожий атрибут "CalloutMenu", который позволяет отобразить более современное меню элемента вида "Callout".

Фильтрация и сортировка


Установка атрибутов Filterable и Sortable в FALSE поможет запрещать фильтрацию и сортировку выбранных полей в пределах вашего представления. Дополнительно, можно указать сообщение, которое будет отображаться, когда фильтрация недоступна (атрибут FilterDisableMessage).

Когда это требуется?

  1. Например, вы выносите фильтр по некоторому полю в отдельную панель наверху представления, и улучшаете этот фильтр, делаете его более "user friendly". Скажем, вместо фильтрации по конкретной дате, вы предлагаете пользователю выбрать месяц/год или даже лучше - выбрать конкретный диапазон дат. В этом случае фильтр в заголовке колонки становится ненужным, и почему бы его не запретить.
  2. В некоторых полях фильтрация просто выглядит глупо (яркий пример - поле "Title").
  3. Когда вы используете в запросе представления (тэг <Query>) параметры (значения из ParameterBindings), фильтры SharePoint ломаются, т.е. не выдают вариантов для фильтрации, как будто в колонке совсем нет данных. В этом случае я предпочитаю явно запрещать фильтрацию и определять сообщение об ошибке для всех полей, что-нибудь типа "В этом представлении фильтрация невозможна"...

Explicit


Исключительно полезный атрибут - Explicit. Значение его трудно переоценить, особенно если вы работаете с какими-то коробочными решениями или с большими проектами, где много пользователей с разными уровнями привилегий. Но даже и на маленьких проектах, этот атрибут часто важен!

Explicit делает две вещи:
  1. Скрывает поле из представления. Т.е. данные для поля будут запрашиваться и поступать в XSLT преобразование, но отображаться соответствующие поля не будут, пока вы их сами не отобразите.
  2. Не дает пользователю удалить поле из представления!!

Те люди, которые используют свойство Sealed у SPField, меня поймут! :)

Для остальных поясню: кастомизированное представление - вещь довольно хрупкая. Удалили критически важное поле из представления - представление разъехалось или начало глючить. А клиенты, они такие - любят удалять "ненужное" :) Нередко бывает, что ошибки замечают не сразу. Проходит несколько дней, в конце концов обнаруживают ошибку, но никто уже не помнит что он там когда-то давно что-то "чистил". В итоге, клиент с черной тучкой над головой звонит в вашу техподдержку....

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

SharePoint 2013


В SharePoint 2013, помимо уже упоминавшегося выше CalloutMenu, к ViewFields добавилось немало новых интересных атрибутов. В частности, это атрибуты для управления отображением полей типа "Пользователь": WithPicture, PictureSize, PictureOnly, и некоторые другие.

Например, добавление WithPicture="TRUE" к полю "Created By" (InternalName="Author") выдает вот такой результат:

А вот такой FieldRef:

<FieldRef Name="Author" PictureSize="Size_36px" PictureOnly="TRUE" />

Будет отображен следующим образом:
Таким образом, можно изменять отображение одного и того же поля в зависимости от представления. Это нередко пригождается на практике, чтобы "зафиксировать" режим отображения поля для кастомизированного представления, чтобы вне зависимости от того что пользователь может поменять некоторые настройки для поля через веб-интерфейс, это поле отображалось бы в нашем представлении именно так, как нужно.

Еще один атрибут, специфичный для SP2013, о котором хотелось бы упомянуть - это AllowGridEditing. Он позволяет сделать ту или иную колонку нередактируемой в режиме Grid в рамках данного представления. Выглядит полезно, но будьте осторожны: ограничение редактирования такого рода запретами ненадежно, всегда можно будет изменить это поле куском JS через Client Object Model. О том, как правильно управлять разрешениями и доступом в SharePoint, у меня есть отдельная статья.

Еще раз напомню: все эти атрибуты как правило доступны в виде свойств объекта SPField, т.е. их можно задавать и на уровне поля, для всех представлений сразу.

Другие атрибуты


Есть для элементов ViewFields/FieldRef и другие атрибуты. Например, атрибуты Len и MoreText, нужные для обрезания текста...

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


Иными словами, ссылки, помещенные внутрь текста, становятся кликабельными. Повторюсь, работает это только для обычных текстовых полей.

Как задать атрибуты ViewFields программно?


Как я уже упоминал выше, через SPView.ViewFields задать эти атрибуты нельзя. Что же делать?

Очевидно, решение - это попробовать как-то передать в SPView кусок View XML. К сожалению, напрашивающийся для этого метод SetViewXml попросту не работает - проглатывает любой поданный ему XML, но после Update изменения не сохраняются в БД.

Впрочем, если тщательно пошарить в MSDN, вы обнаружите, что конструктор SPView принимает XmlDocument, что практически то что нужно! Однако, метод Update на созданном таким образом SPView, как ни странно, не работает!... Решение этой проблемы можно найти там же в MSDN, в секции Remarks:

Use the Clone method on the constructed view object to add the view to collection of views for the list.

Кто бы мог подумать! Создаем SPView используя конструктор, а потом его клонируем, и вот тогда уже можно сохранять. Система нипель, блин :)

Вот вам рабочий код:

var doc = new XmlDocument();
doc.LoadXml("<View ...>...</View>"); // or load from file: doc.Load("myview.xml");
var tempView = new SPView(list, doc);
var view = tempView.Clone("My view", 30, true, false);
view.Update();

Заключение

Создание кастомизированного представления списка - это частая потребность. Поэтому важно уметь управлять представлением и его полями в полной мере. Надеюсь, описанные мной здесь, по большей части малоизвестные атрибуты элементов ViewFields помогут сделать ваши представления лучше. Удачи!

воскресенье, 24 марта 2013 г.

Scrum и Definition of Done

Во время работы в костромском Softline, мы не раз и не два сталкивались с проблемой неверно/неточно сформулированных задач. Нередко это приводило к многократным перепинываниям задач туда-сюда - что в свою очередь выливалось в конфликты и споры между разработчиками и тестировщиками. Это все было неприятно и значительно снижало продуктивность нашей команды.

В этом посте я хочу описать, как мы пытались решить эту проблему в Softline, и про мой опыт работы в Scrum-команде в Финляндии, где эта проблема была решена с помощью Definition of Done.

понедельник, 11 марта 2013 г.

Wait/loading диалоги в SharePoint

Пара недокументированных стандартных диалогов. Сделаны по тому же принципу, что я описывал для Confirmation-диалогов, но уже входят в состав SharePoint - что приятно :) Возможно, пригодятся.

Буду краток:


Этот диалог выводится следующим куском JS:

var dialog = SP.UI.ModalDialog.showWaitScreenWithNoClose("test", "test message", 190, 350)
// делаем что-нибудь асинхронное и долгое
// по завершении, вызываем
dialog.close(SP.UI.DialogResult.OK);

Можно использовать для каких-нибудь долгих асинхронных операций.

Если вы хотите дать пользователю возможность отменить вашу операцию (что желательно), используйте другой метод:

var dialog = SP.UI.ModalDialog.showWaitScreenSize("test", "test message", function (dialogResult) { alert(dialogResult); }, 150, 350);

Вы автоматически получите кнопку "Отмена" и при ее нажатии, сработает callback (dialogResult будет равен 0, т.е. SP.UI.DialogResult.cancel).


К слову, в клиентской объектной модели и SP.UI в SharePoint 2013 добавилось очень много всего недокументированного (те же callback'и чего стоят, или SP.Taxonomy). Надеюсь, задокументируют позже - все-таки лазать в сгенерированных Script#-ом исходных кодах SharePoint JavaScript API, где все локальные переменные заобфусцированы - то еще удовольствие :(

четверг, 7 марта 2013 г.

Динамическая форма списка в SharePoint Designer

Как известно, SharePoint Designer (далее SPD) генерит формы списков в “статическом” виде (Lists and libraries –> [ваш список] –> Forms –> New…). На каждое поле списка в XSLT разметке генерится отдельный тэг <tr>, в который захардкожены значения для заголовка и названия поля. Выглядит один такой <tr> следующим образом:
<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

Я уже писал про формы списков и про то, как их можно кастомизировать, однако в свете новых возможностей SharePoint 2013, а также накопившегося с того времени опыта (прошло уже 1.5 года, как время летит!), хочется еще раз пробежаться по этой теме.

вторник, 26 февраля 2013 г.

Права доступа в SharePoint

Сделать какие-то кастомные права доступа в SharePoint - это одна из самых частых задач в SharePoint, однако, к сожалению, 90% разработчиков решают эту задачу неоптимально.

среда, 24 октября 2012 г.

Вызов веб-сервиса без кода: DataFormWebPart!

No-code решения – очень полезны и нужны. Часто их использовать проще и правильнее, чем писать серверный код. А иногда, без них просто не обойтись: например, если портал хостится в облаке и нужно пройти 10 инстанций и ревью, чтобы что-то серьезное туда задеплоить. Я лично столкнулся с такой ситуацией на работе, в связи с чем и родился этот пост.
В общем-то, скажете вы, веб-сервис дернуть ведь проблемы нет, берешь jQuery (или например уже заточенные под это SPServices) и дергаешь… К сожалению, не всегда всё так просто. Например, что если веб-сервис требует авторизацию (а пароли на клиенте хранить ну уж никак нельзя, согласитесь), или же доступ к веб-сервису ограничен и он доступен только для определенных IP-адресов (т.е. принципиально возможен только server-side вызов). А бывает и так, что даже стандартные веб-сервисы SharePoint из _vti_bin недоступны извне (их часто блокируют). Что делать в этом случае?