Используйте Web Money - это очень удобно

вторник, 30 декабря 2008 г.

Подборка полезных ссылок.

1
2

пятница, 26 декабря 2008 г.

Потеря части письма при отсылке через SPUtility.SendMail

При отсылке письма с разметкой HTML часто происходит обрезание письма, что заставляет использовать другое решение.
Недавно мой коллега(Блог) нашел решение.
Спешу его опубликовать и обсудить, если кто-то нашел более хорошее решение:

private void SendMessage(SPWeb currentWeb, string Recipient, string Subject, string Body)
{
SPUtility.SendEmail(currentWeb, false, false, Recipient, Subject, Body.Replace("><", (">" + (char)13 + (char)10 + "<")));
}

пятница, 5 декабря 2008 г.

четверг, 27 ноября 2008 г.

вторник, 30 сентября 2008 г.

Как убрать надпись "Новое" с элементов списка

MSDN
stsadm.exe -o setproperty -pn days-to-show-new-icon -pv 0 -url [Your Virtual Server's URL]

четверг, 28 августа 2008 г.

По поводу поиска

What's wrong with my people search code? : SharePoint - Search : SharePoint Products and Technologies : Microsoft TechNet Forums
Для руссифицированного shrepoint people нужно заменить на люди

пятница, 27 июня 2008 г.

Прячем меню от анонимных пользователей

function OnItemDeferCall(elm)
{
if (!IsMenuEnabled())
return false;
if (IsMenuOn())
{
StartDeferItem(elm);
return false;
}
if (itemTable !=null)
OutItem();
itemTable=elm;
currentItemID=GetAttributeFromItemTable(itemTable, "ItemId", "Id");
var createCtx=new Function("setupMenuContext("+itemTable.getAttribute("CTXName")+");");
createCtx();
var ctx=currentCtx;
вот это нужно добавить сюда:
if(ctx.CurrentUserId==-1)
{
return;
}
в core.js

понедельник, 16 июня 2008 г.

вторник, 10 июня 2008 г.

Настройки поиска. AdvancedSearchBox DataType

В схеме свойств мы добавляем управляемое свойство.
В теге есть три параметра:
PropertyDef Name="Title"
DataType="text"
DisplayName="Название"
Я долго искал перечень типов для настройки поиска и наконец нашел:
это "text" "boolean" "integer" "datetime" "decimal".
Надеюсь это сократит кому-то время разработки решения :)

вторник, 27 мая 2008 г.

Фильтр данных ListViewWebPart.

Ранее я написал об изменении перчня столбцов.
Изменение перечня столбцов в ListViewWebPart "на лету".
Сегодня хочу добавить об изменении схемы SPQuery на лету.
Это нужно для того, что бы отображать на одной странице релевантную информацию в зависимости от заданных параметров, или просто сделать расшириный фильтр с полной функциональностью SPQuery.
Для более стабильной работы необходимо:
Создать свойство с Guid View вебчасти.
Получив веб часть как указано в ссылке
Проверяем совпадение guid и меняем схему query.
if (lv.ViewGuid == _viewGuid)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.InnerXml = lv.ListViewXml;
XmlNode node;
node = xmlDoc.DocumentElement;
node["Query"].InnerXml = "<Where>" + "<Eq><FieldRef Name='ParentId'/><Value Type='Text'>" + _DocumentID.ToString() + "</Value></Eq>" + "</Where>";
lv.ListViewXml = xmlDoc.InnerXml;
}


PS. Таким же образом можно поменять и другие части схемы отображения.

понедельник, 26 мая 2008 г.

Управление свойствами полей.

Написал код для быстрого редактирования свойств полей списков и их удаления.
Если будет время и необходимость допишу функциональность.
Проект выполнен в виде веб приложения.
Запускать на сервере.
Можно прямо в дебаге.
Ссылка но солюшн

четверг, 15 мая 2008 г.

Ограничение на количество элементов.

Тестировал список:
>1 000 0000 элементов разбиты по 12 папкам. >84 000 элементов на папку
Результат:
Из кода доступ есть
К списку доступа нет. Только к свойствам.
Вывод данных только ограниченными объемами через ListViewWebPart.
Быстродействие заметно снизилось.
В списке 5 текстовых полей.
Объем списка примерно 300 MB.
Создать его шаблон с содержимым не удалось.

понедельник, 12 мая 2008 г.

Custom action. Праметры в строке.

Источник
MSDN основной
MSDN дополнительный

Параметры это ид элемента и guid списка.
<urlaction url="">?ID={ItemId}&List={ListId}"/></urlaction>

Кроме того можно использовать javascript:
<UrlAction Url="javascript:window.location= '{SiteUrl}/_layouts/mypage.aspx?List={ListId}&Source=' + window.location"/>

и коллекцию узлов:
<UrlAction Url="_Layouts/RedirectPage.aspx?Target={SiteCollectionUrl}_catalogs/masterpage" />

кроме того есть стандартные сокращения:
~site/_layouts/sampleurl.aspx
или ~sitecollection/_layouts/sampleurl.aspx.

четверг, 8 мая 2008 г.

Проблема одновременного доступа к элементу списка из кода.

Проблема заключается в том, что в отличии библиотеки документов в списке не реализован checkOut. И если вы напишете обработчик событий которы пишет в какой-то элемент списка есть вероятность(я на убедился на своем опыте что она велика) попытки записи в элемент одновременно двумя инстансами обработчика.
Решение простое:

static int count;
static int queue;
int myQueueNumber;
lock(count)
{
count++;
myQueueNumber= count;
}
if (queue==myQueueNumber)
{
//code
queue++;
}

И соответственно простая организация очереди для инстансов этого обработчика.
Если обработчиков много и они разного типа все выносится в отдельный класс объект которого присутствует во всех обработчиках. Или используете наследование.

Оптимизация кода 2

Источник...
Смысл в том, чтобы уничтожать объекты SPweb, SPSite и т.п. методом Dispose() принудительно. Думаю, что в некоторых случаях это должно сильно освобождать ресурсы - например при переборах узлов.

среда, 7 мая 2008 г.

Библиотека документов и документы word - скрытые от пользователей поля.

Обнаружил при неприятнейшую возможность приложения Word:
Создал библиотеку документов.
Создал несколько полей.
Часть из них заполняются автоматически,а потому для пользователя должны быть не доступны. Выставил соответствующие свойства (привожу все, которые нашел):

field.ShowInEditForm = true;
field.Hidden = true;
field.ReadOnlyField = true;
field.Update();

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

среда, 30 апреля 2008 г.

Оптимизация кода.

При выполнении запросов к большим спискам > 1000 элементов код запросов выполняется очень медленно.
Тем более, что этот код обычно кроме обращения содержит еще какие-нибудь действия с найденными элементами.
Часто приходится использовать foreach для элементов списка или коллекций если нужно проверить какой-то флаг.
Хочу поделиться проблемами и моей оптимизацией решения.

1. Односторонняя синхронизация списка с базой данных: Кол-во элементов > 8000
время синхронизации ~десятых долей секунды. (Зависит от быстродействия сервера, время чтения списка ~ 8 сек. из базы ~ 3-4)
Решение: использование метода SPList.Items.GetDataTable(); И работа с dataSource. Конечно же не забывая о кодировках - в базе и списке sharepoint некоторые символы различаются.

2. Большой список, нужно найти элемент, или несколько - < 1% от общего числа.
Решение: использовать поиск sharepoint FullTextSqlSearch - работает на порядок быстрее чем SPQuery и, тем более чем перебор.
Элементы для изменения получаем используя getItemById().
ИД должен быть одним из параметров запроса.

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

3. Если код настолько громоздкий, что пользователь отваливается по таймауту, то мне приходит в голову решение с использованием вебсервисов и ajax - постепенное выполнение кода разбитого на этапы.

Дискуссия по этому вопросу Приветствуется!!!!

четверг, 24 апреля 2008 г.

Развертывание портала для просмотра из интернет.

Недавно развернул портал для просмотра заказчика через интернет.
Делается это довольно просто:
1. Расширяем веб приложение желательно на 80 порт т.к. это проще.
2. Задаем альтернативное имя типа portal.domen.ru если под таким именем зарегистрирован в DNS.
3. Если хочется проверить работоспособности то можно прописать в hosts ip (внутренний) portal.domen.ru Например для уменьшения трафика или из-за политики безопастности.

среда, 23 апреля 2008 г.

Кастомизированная форма dispForm, editForm и newForm

Очень интересная возможность для ограничения возможностей пользователей и добавления своей скриптовой обработки заполнения формы.

Источник
Порядок действий:
1. Копируем необходимую нам страницу с формой.
2. Удаляем на ней веб часть listForm
3. Вставляем custom listForm
4. Редактируем как хотим.
Мои замечания:
1. Если поле не должно быть редактируемым пользователем, можно взять значение из формы dispForm.
2. Если вы хотите предоставить различный функционал для различных уровней разрешения используйте контрол SharePoint:SPSecurityTrimmedControl, внутрь него кладите то что вам нужно.
Ну и конечно вы можете добавить и свои веб части и свои скрипты. Как результат можно получить гибкий инструмент для просмотри и редактирования элементов зависящий от прав, и вашей производственной необходимости.
Есть одна проблема для библиотек: если в библиотеке несколько типов содержимого переназначить форму не удасться.
Но можно скрыть родную и вставить свою рядом - будет работать, к сожалению только для одного типа содержимого т.е. для каждого типа содержимого нужно сделать свою форму, вставить их на одну страницу, а видимость реализовать с помощью скрипта на основании ContentType из URL или из значения соответствующего поля + необходимо реализовать обработку изменения типа содержимого.
Это несомненно большой минус этой кастомизации.
Кроме того при изменении параметров списка необходимо переделывать форму.

понедельник, 21 апреля 2008 г.

Кастомизация полей: дополнительная инфа.

Источник - ограничение видимой ширины поля.
Смысл в том, что в схеме полей добавить свойство в определениях поля
<fields>
<field name="Title" readonly="TRUE" required="FALSE" hidden="TRUE">
<field name="Name" displayname="Name" type="Text" displaysize="30">
<field name="Address" displayname="Address" type="Text">
<field name="City" displayname="City" type="Text" displaysize="20">
<field name="Country" displayname="Country" type="Text" displaysize="15">
</fields>

Т.е. есть возможность настраивать видимость полей и их параметры прямо в схеме в RenderPattern я так понимаю - это глобальная настройка в C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\FLDTYPES.XML. Кроме того возможно эти свойства можно использовать для настройки других схемах - SCHEMA.XML для различных фич и типов данных - списков, библиотек - но это пока измышления проверю напишу или если кто-то проверит - напишите коммент и я поправлю блог.
Так же это доступно при определние своих полей на основе стандартных - просто копированием xml или в составе проекта - например рабочего процесса (workflow), когда мы создаем тип данных и наполняем его полями.

Или как я уже писал ранее можно настроить в стиле просмотра списка VWSTYLES.XML - в HTML тегах для конкретного представления.

Что больше подходит для вашего решения выбирайте сами.

четверг, 17 апреля 2008 г.

Hide Actions 2

Новая инфа:

Как убрать элементы в стандартном toolbar'е в формах изменения, создания, просмотра элемента
Как сделать кастомизированные dispForm и editForm
Соответвенно там в свойствах toolBar можно указать тип (New, Edit, Display) и получить различный набор кнопок.

К предыдущему посту про ECB Menu:
Пришлось лопатить core.js.
К сожалению сделать копию и прицепить к новой masterpage не удалось - Sharepoint цепляет core где-то еще.

Пример кода:

var mySiteURL = new String(); //мой код убираю меню на узле и ниже
mySiteURL = ctx.listUrlDir;
if (mySiteURL.search("sites/имяСемействаУзлов/имяУзла")!= -1)
{
//здесь измененное меню
}
else
{
//здесь без изменений
}

Вставить нужно в трех местах ищите:

AddManagePermsMenuItem
AddDocLibMenuItems(m, ctx)
AddListMenuItems(m, ctx)

среда, 16 апреля 2008 г.

Hide Custom Action

Actions list
Как убрать остальное
В общем придется убирать скриптами на странице. Т.к. core.js трогать не хочется.
И еще на закуску через веб часть

пятница, 11 апреля 2008 г.

Получение праметров для кастомизированного поля и что такое ONET.xml

Файл находится: C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\GLOBAL\XML\ONET.xml
Внутри BaseTypes находятся схемы стандартных полей sharepoint.
Наиболее интерсная информация которую можно оттуда почерпнуть это структура схемы и приемы использования ссылок на поля.
И самое интересное:
Права на просмотр содержимого - интуитивно понятно:

<DisplayPattern>
<IfHasRights>
<RightsChoices>
<RightsGroup PermEditListItems="required" />
</RightsChoices>
<Then>
<HTML>Есть права</HTML>
</Then>
<Else>
<HTML>Нет прав</HTML>
</Else>
</IfHasRights>
<CurrentRights />
</DisplayPattern>

Использование прав позволит вам отображать различное содержимое в столбце для различного типа пользователей - а это широкое место для маневра в кастомизации интерфейса.
А это получение permissionmask - <CurrentRights /> - текущие права пользователя
Важный параметр если вы используете стандартные javascript функции sharepoint.

четверг, 3 апреля 2008 г.

Кастомизируя стили...

Источники:MSDN (2003 sharepoint) и конечно google
Идеология...
Стиль по существу является XSLT преобразованием xml с содержанием списка.
Я уже писал по этому поводу.
Все что нам нужно это поправить html разметку в тегах CDATA.

Итак по адресу:

C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\GLOBAL\XML\VWSTYLES.XML
лежит этот замечательный файл в котором хранятся стили отображения для ListViewWebPart или проще говоря для библиотек и списков.

Хочу поделиться своими изысканиями в этой области:
1. Создать свой стиль проще всего через копирование.
Необходимо задать ему новый ИД больше 20.

2. Можно ограничить типы списков для которых будет работать ваш стиль:
например ID="34" DisplayName="Карточка(изменить)" BaseType="0" - для списков и библиотек или BaseType="1" для библиотек.

3. Когда вы создаете свой стиль необходимо внимательно посмотреть на ограничения по полям( особенно если вы пытаетесь переделать готовый стиль для библиотеки в стиль для списка.

Мои примеры:
1. Удалив содержимое <ViewHeader> и выравнив стиль (HTML теги которые открывались в этой области) вы получите отображение списка без заглавия.

2.
Добавив такую схему в <ViewBody> в каждой строке:
<HTML> <![CDATA[<a href="]]></HTML>
<ListProperty Select="DefaultViewUrl" HTMLEncode="TRUE"/>
<HTML><![CDATA[?FilterField1=ID&FilterValue1=]]></HTML>
<Field Name="ID" HTMLEncode="TRUE" />
<HTML><![CDATA[" >Подробнее...</a>]]></HTML>
Мы получим ссылку на основное представление списка в котором будет показываться только текущий элемент. Вместо DefaultViewUrl можно использовать страницу с вебчастью на которой отображается список.

Этот стиль предназанчен для перехода в представление в котором отображается только один элемент в таком стиле где нет элементов навигации по списку например для отображения многострочного поля с форматированием: мы видим только 1 поле во всей области. Таким образом можно отображать новости, страницы и т.п. HTML форматированную информацию.

Комбинируя изменение стиля и кастмизированное поле мы можем получить практически произвольный вид списка, с нашими скриптами и HTML контролами. Это богатый инструментарий как для дизайнера так и для разработчика.
Отдельно хочу обратить ваше внимание на заполнение значениями переменной ctx = new ContextInfo(); типа ContextInfo - из нее можно достать необходимую вам информацию о пользователе, списке и т.п.

В целом советую сначала прочитать про преобразование xml потом внимательно просмотреть схемы стилей (попытаться их прочитать) думаю через неделю вы сможете создать свой собственный стиль на базе существующего.

Удачи!

вторник, 25 марта 2008 г.

LinkTitle - используем скрипты Sharepoint

Собственно это попытка повторить функционал этого меню документа в списке.
Зачем:
В библиотеке есть документ 1 в той же библиотеке есть связанный с ним документ 2.
В карточке документа 1 отобразить LinkTitle документа 2.
Непосредственно разметка:

<TD Class="ms-vb-title" height="100%">
<table height="100%" cellspacing="0"
class="ms-unselectedtitle"
onmouseover="ggg();OnItem(this)"
CTXName="ctx1"
Id="4"
Url="/sites/test/DocLib2/Поиск.aspx"
DRef="sites/test/DocLib2"
Perm="0x400001f07fff1bff"
Type="SharePoint.WebPartPage.Document"
Ext="aspx"
Icon="icsmrtpg.gif|Microsoft Office SharePoint Designer|SharePoint.OpenDocuments"
OType="0"
COUId=""
SRed="1"
COut="0"
HCD=""
CSrc=""
MS="0"
CType="Документ"
CId="0x0101004C213AE6D661A548A1237AF7852AFA98"
UIS="512"
SUrl="">
<tr>
<td width="100%" Class="ms-vb">
<A onfocus="OnLink(this)"
HREF="/sites/test/DocLib2/Поиск.aspx"
onclick="return DispEx(this,event,'TRUE','FALSE','FALSE','','0','SharePoint.OpenDocuments',
'SharePoint.WebPartPage.Document','1','',
'1073741823','0','0','0x400001f07fff1bff')">
Поиск<img src="/_layouts/images/blank.gif" class="ms-hidden" border="0" width="1" height="1" alt="Для открытия меню воспользуйтесь сочетанием клавиш SHIFT+ВВОД (в новом окне).">
</A>
</td>
<td>
<img src="/_layouts/images/blank.gif" width="13" style="visibility:hidden" alt="">
</td>
</tr>
</table>
</TD>
<td>

И скрипт который заполняет объект тип ContextInfo который передается для форматирования меню:

<script>
var ctx1 = new ContextInfo();
function ggg()
{

ctx1.ContentTypesEnabled=false;
ctx1.ctxId=1;
ctx1.CurrentUserId=1073741823;
ctx1.displayFormUrl="/sites/test/DocLib2/Forms/DispForm.aspx";
ctx1.editFormUrl="/sites/test/DocLib2/Forms/EditForm.aspx";
ctx1.EnableMinorVersions=false;
ctx1.HttpPath="/sites/test/_vti_bin/owssvr.dll?CS=65001";
ctx1.HttpRoot="http://server/sites/test";
ctx1.imagesPath="/_layouts/images/";
ctx1.isForceCheckout=false;
ctx1.isModerated=false;
ctx1.isPortalTemplate=null;
ctx1.isVersions=0;
ctx1.isWebEditorPreview=0;
ctx1.listBaseType=1;
ctx1.listName="{AC2D4CC5-7971-4627-8E58-F6A40E80AA70}";
ctx1.listTemplate=101;
ctx1.ListTitle="Библиотека документов";
ctx1.listUrlDir="/sites/test/DocLib2";
ctx1.ModerationStatus=0;
ctx1.newFormUrl=null;
ctx1.OfficialFileName="";
ctx1.PortalUrl=null;
ctx1.recursiveView=false;
ctx1.RecycleBinEnabled -=1;
ctx1.rootFolderForDisplay=null;
ctx1.SendToLocationName="Lib";
ctx1.SendToLocationUrl="http://server/sites/test/DocLib/";
ctx1.serverUrl=null;
ctx1.SiteTitle="test2";
ctx1.verEnabled=1;
ctx1.view="{B810C9B4-434E-4EA3-BADB-C466199D3F74}";
ctx1.WorkflowsAssociated=true;
ctx1.WriteSecurity="1";
}
</script>

У меня возник ряд сложностей с разбором кода: не совсем понимаю откуда берутся некоторые параметры и что это за параметры:

onmouseover="OnItem(this)" - это вызов js функции она строит меню.
CTXName="ctx1" - ContextInfo данные об объекте.
Id="4" - это ИД элемента в списке
Url="/sites/test/DocLib2/Поиск.aspx" - БК
DRef="sites/test/DocLib2" БК
Perm="0x400001f07fff1bff" - ? я так понимаю необходимый уровень разрешений - где его брать пока не знаю
Type="SharePoint.WebPartPage.Document" -БК
Ext="aspx" - БК
Icon="icsmrtpg.gif|Microsoft Office SharePoint Designer|SharePoint.OpenDocuments" - БК
OType="0" - ?
COUId="" -?
SRed="1" -?
COut="0" -?
HCD="" -?
CSrc="" -?
MS="0" -?
CType="Документ" - БК
CId="0x0101004C213AE6D661A548A1237AF7852AFA98" - ИД типа содержимого
UIS="512" - ?
SUrl="" -?

Отдельный вопрос как раздобыть ИД текущего пользователя.
ctx1.CurrentUserId=1073741823;
Собственно решение:
Создаем свой тип поля (наследуя MultiColumn) в котором сохраняем все необходимые переменные.
Далее повторяем HTML разметку меню в схеме отображения нашего поля.
Создаем контрол/вебчасть которая записывает ИД текущего пользователя в скрытое поле с известным идом.
Добавляем скрипт на мастер-страницу (либо на нашу страницу) который задает значение ctx1.CurrentUserId из скрытого поля.

Если кто-то может что-то дополнить буду благодарен.

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

Веб часть основные результаты поиска - использование фиксированных запросов.

В url страницы где размещается веб часть основных результатов можно добавить параметры по следующему принципу:
?k=contentType%3A%D0%AD%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82
k - строка запроса
(%3A - ":") (%D0%AD%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82 - Элемент)
&u=http%3A%2F%2Fserver%3Aport
u - ограничение по url (узел)
%3A%2F%2 - :\\ %3A - :
Удобно использовать в случае необходимости показать пользователю результаты поиска без заполнения пользователем формы запроса
Т.е. тогда когда запрос известен заранее или известна формула а пареметр берется в зависимости от (пользователя, группы, узла, страницы, элемента и т.п.) Можно реализовать как скрипт или через redirect(url).
Учитывая большие возможности форматирования таким образом можно отображать сводную информацию с различных узлов в любом удобном формате.
Статья на сайте микрософт

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

Мой фильтр для списка.

Ранее я написал как можно подступисться к внешнему виду listViewИзменение перечня столбцов в ListViewWebPart "на лету".
Хочу более подробно остановиться на использовании фильтра.
Стандартные фильтры неудобны тем, что работают только по равно и только по одному значению. Т.Е. их нужно набирать из веб частей долго настраивать и т.п.
Для тех кто умеет создавать веб части предлагаю пойти другим путем:
Создать свою веб часть фильтрации - поиска в списке.
Итак входящие параметры:
Название списка.
Перечень полей для фильтрации.
Сразу хочу сказать чтов качестве пробы сил реализовал фильтрацию только для строки и даты, но думаю и для других типов это не сложно.
Для каждого текстового поля создаем динамические элементы: Надписи - label, условие - DropDownList - содержит\равно\не равно\начинается с (в соотвествии с возможностями CALM) тексбокс со значением фильтра и DropDownList соединение (и\или)если полей несколько. Можно реализовать любой метод постбека я разместил кнопку.
Для даты создаем надписи и DateTimeControl в моем случае это с значение по значение - поиск в диапазоне.
Далее ищем нашу веб часть по параметру имя списка:

SPList list = SPContext.Current.Web.Lists[_listName];
String guid = "{" + list.ID.ToString().ToUpper() + "}";
ListViewWebPart lv = (ListViewWebPart)wp;
if (lv.ListName == guid)// guid списка (c {})

Читаем xml схему:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.InnerXml = lv.ListViewXml;
XmlNode node;
node = xmlDoc.DocumentElement;

Итак запрос(фильтр) лежит в теге Query.
node["Query"].InnerXml - сюда запишем нашу строку запроса.
Сам запрос легко собирается из значений формы запроса типов полей списка. Самый простой способ - две строки на этапе формирования запроса для простоты в первую часть
тег соединения() + тег запроса
во вторую тег закрытия соединения(/And)).

В конце соединяем строки и записываем xml в веб часть.

Кастомизация поиска. - Дополнение.

В дополнение хочу сказать что в схеме параметров можно ограничить результаты поиска query выражениями типа:
Contains('Имя свойства','Значение') OR Title!='' AND ContentType!='Папка'
1 - очень удобно использовать для свойства "Path" - получам результат как с областями.
указывая урл билиотеки или списка мы ограничиваем поиск этой библиотекой списком.
2 - понятно
3 - тип содержимого - как мне кажется довольно удобно ограничивать поиск среди своего портала по своим типам содержимого - очень удобно!
Удачи!
P.S.
1. Когда будете настраивать схему результатов не забудте указать параметры в перечне столбцов основных результатов поиска.
2. Имена столбцов пишутся маленькими буквами поэтому в XLS схеме загалвные нужно перевести в маленькие.

четверг, 28 февраля 2008 г.

SPQuery DateTime правильный синтаксис.

Вот так ма получаем переменную:
SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.UtcNow)

Кастомизация поиска.

Мой источник для начала.
Все оказалось довольно просто.
1. Создаем столбец в списке по которому назначаем индексирование.
после индексирования в настройках поиска в общих службах
Создаем свойство обхода содержимого на основании этого столбца.(Обязательно из группы sharepoint)
Еще раз индексируем.
Создаем страницу поиска и страницу результатов на которых размещаем соответсвующие вебчасти. Веб части: AdvancedSearch и основные результаты поиска+ по желанию обрамление.
Далее модифицируем схему вебчастей поиска изменяем Свойства (схема дополнительных параметров) и указываем свою страницу результатов.
Получается

Далее модифицируем схему результатов как это делается написано в источнике.
Кроме того меняем выбранные столбцы добавляя туда свое свойство.
Получается

В результате имеем произвольно настраиваемую параметризированную форму поиска.
И также произвольно настраиваемую страницу результатов.

четверг, 14 февраля 2008 г.

Меняю дизайн :)

Пока криво не могу понять как перенести подложку для меню.

вторник, 12 февраля 2008 г.

Custom field - отрисовка HTML

Думаю многим приходилось создавать свои поля, особый интерес представляет в этом процессе формирование HTML отрисовки значения поля в xml схеме его определения.
используя тег CDATA мы можем нарисовать любое окружение вокруг информации в нашем поле.
Например можно создать поле из нескольких колонок.
Используя различную HTML разметку и информацию из колонок можно создать интересный пользовательский интерфейс: свои меню, кнопки, радио, чекбоксы, подсказки и т.п.
Как результат можно обогатить интерфейс пользователя при работе со списками и отображении информации - подсветку строк, ячеек, и т.п.
Совместив этот механизм с веб частями и скриптами на мастер странице можно преобразить интерфейс пользователя до неузнаваемости очень скромным объемом кода.

Изменение перечня столбцов в ListViewWebPart "на лету".

Доброго времени суток!
Итак передо мно возникла проблема: отображать различную информацию на одной странице веб частей в зависимости от пользователя, параметра или состояния процесса(проекта) информация о котором отображается.
Задача возникла из необходимости придерживаться одного интерфейса для редактирования и просмотра элементов списка (редактирование косвенное через мой интерфейс). Причем со всеми возможностями Sharepoint.
Итак имеем: список, набор полей которые отображаются всегда, + поле редактирование(мое кастомизированное поле) + набор столбцов - соответствующих пользователям.
Каждый пользователь должен видеть либо мой столбец, либо свой результат.
Или при передаче параметра результат другого человека.
Решение:
Создаем свою веб часть. В ней обращаемся к SPWebPartManager.
SPWebPartManager spwp = (SPWebPartManager)this.WebPartManager;
Ищем необходимую веб часть и меняем у нее listViewWebPart.ListViewXml через XmlDocument xmlDoc = new XmlDocument();
xmlDoc.InnerXml = listViewWebPart.ListViewXml
node = xmlDoc.DocumentElement;
Меняем схему - она довольно сложна поэтому я просто создал веб часть с необходимым мне представлением (со столбцом результата вместо моего кастомного поля) скопировал эту схему вместо текущей схемы.
Заменил в ней параметры:
node.Attributes[0].Value = viewName;//View Name веб части
и название поля на мое - обязательно внутренне(internalName)( для примера Название-Title)
Присвоил listViewWebPart.ListViewXml = xmlDoc.InnerXml;
Все.
Кроме того что я указал также можно написать веб часть поиска, т.к. в схеме указывается CAML запрос. Т.е. можно изменить не только перечень столбцов но и перечень элементов, группировку и т.п. смотрите xml схему.

пятница, 1 февраля 2008 г.

Создание своей веб части представления списка. Проблемы дизайна.

У меня возникла необходимость создать свое представление списка - веб часть с расширенной функциональностью.
Главная задача - правильное отображение - дизайн(причем при смене темы должна измениться и моя вебчасть).
1. Приходится использовать таблицу т.к. заголовки при отображении это тоже таблицы
2. Приходится переопределять стили названия шрифтов - самое тонкое место пока просто поставил такие же как и в стандартной вебчасти.
Проблема в том, что для моя кастомная вебчасть
обрамляется div с указанием класса - ms-WPBody - который и меняет шрифты (для стандартной вебчасти этот div пустой).

Желательно конечно получать перечень шрифтов динамически, что бы они правильно изменялись.

четверг, 31 января 2008 г.

Копирование семейства узлов через backup/restore.

Думаю каждый сталкивался с необходимостью перенести семейство узлов из одного приложения в другое или даже на другой сервер.
Я делаю это так:
Простой backup (можно написать простой командный файл и запускать его по расписанию с помощью Scheduled Tasks):
Stsadm -o backup -url http://server:port/SITES/name/ -filename name.bak -overwrite
Stsadm -o restore -url http://server:port/SITES/name/ -filename name.bak -overwrite
Перенос на другой сервер возможен при условии, что версия сервера (sharepoint) не ниже чем у исходного:

Stsadm -o restore -url http://server2:port/SITES/name/ -filename name.bak -overwrite
А вот теперь более интересное:
Хочу размножить семейство узлов у себя на сервере...

Stsadm -o restore -url http://server:port/SITES/name2/ -filename name.bak -overwrite
При попытке это сделать выдается ошибка - нужно создать базу данных содержимого - в центре администрирования, управление приложениями, базы данных содержимого.
Это происходит по причине прямого копирования семейства узлов - включая уникальные идентификаторы в базе данных.

пятница, 18 января 2008 г.

Наследование : BaseFieldControl - проблема с чтением Value.

Итак имеем следующий код:
public override object Value
{
get
{
EnsureChildControls();
SPFieldValue fieldValue = new SPFieldValue();
count = 0;
_names = "";
_SelectedNodeID = "";
SetValues();
fieldValue.Names = _names;
fieldValue.Index = _SelectedNodeID;
return fieldValue;
}
set
{
EnsureChildControls();
SPFieldValue fieldValue = (SPFieldValue)value;
_names = fieldValue.Names;
_SelectedNodeID = fieldValue.Index;
}
}
Проблема состоит в следующем т.к. изначально ( и в процессе работы значение поля может быть нулевым необходимо организовать заполнение поля по постбеку и после чтения значения поля.

Поле - деревянный список для списка Shrepoint из списка. Продолжение 2.

Доброго времени суток!
Довел работу до логического завершения.
Добавил редактор поля:
Добавляем в определение поля (fldtypes_TreeField.xml)
^Field Name="FieldEditorUserControl"^ /_controltemplates/ControlTemplates_TreeFieldFieldEditorControl.ascx^/Field^
+ в свойства PropertySchema добавляем Hidden="TRUE" - что бы отображался только мой контрол.
Далее создаем
public class TreeFieldEditor : UserControl, IFieldEditor
и переопределяем методы
public bool DisplayAsNewSection
{
get { return false; }
}

public void InitializeWithField(Microsoft.SharePoint.SPField field)
{
}

public void OnSaveChange(Microsoft.SharePoint.SPField field, bool isNewField)
{
}
и
protected override void CreateChildControls()
{

base.CreateChildControls();

TreeFieldTargetList.SelectedIndexChanged += new EventHandler(TreeFieldTargetList_SelectedIndexChanged);
if (!this.Page.IsPostBack)
{
FillDDL(); //- заполняем dropDownLists
}

и контрол
Control Language="C#" Inherits="TreeField.TreeFieldEditor, TreeField, Version=1.0.0.0, Culture=neutral, PublicKeyToken=!!!" AutoEventWireup="false" compilationMode="Always"
Переделал из LookUp контрола.
Откопал полезный вариант свойств поля: - удобно обращаться напрямую.
public SPTreeFieldField(SPFieldCollection fields, string fieldName)
: base(fields, fieldName)
{
this.Init();
}

public SPTreeFieldField(SPFieldCollection fields, string typeName, string displayName)
: base(fields, typeName, displayName)
{
this.Init();
}
private void Init()
{
this.ListName = this.GetCustomProperty("ListName") + "";
}

public override void Update()
{
this.SetCustomProperty("ListName", this.ListName);
base.Update();
}
private string listName;
public string ListName
{
get
{
return this.listName;
}
set
{
this.listName = value;
}
}
Хорошая ссылка по теме чел делает связанные дропдаунлисты
Пример кода и готовый солюшн

среда, 16 января 2008 г.

Поле - деревянный список для списка Shrepoint из списка. продолжение.

Пришлось довольно много помучиться, да и другой работы было много.
Итак:
1.Создаем свое поле:
две колонки в одной храним имена в другом значения ИД из списка источника. Отображаем только первую колонку.
как сделать:Блог Сергея Шаповалова
Что добавить:
берем из ascx дерево
TreeView1 = (TreeView)TemplateContainer.FindControl("TreeView1");
берем параметры:
_listTreeView = Field.GetCustomProperty("ListName").ToString();
_nodeNameField = Field.GetCustomProperty("FieldName").ToString();
_parentIdField = Field.GetCustomProperty("FieldParentID").ToString();

Работа с полем:
public override object Value
{
get
{
EnsureChildControls();
SPFieldTreeFieldValue fieldValue = new SPFieldTreeFieldValue();
count = 0;
_names = "";
_text = "";
parent = TreeView1.Nodes[0];
ReturnCheckedNames(parent);
fieldValue.Names = _names;
fieldValue.Index = _text;
return fieldValue;
}
set
{
EnsureChildControls();
SPFieldTreeFieldValue fieldValue = (SPFieldTreeFieldValue)value;
ValueBox.Text = fieldValue.Names;
_text = fieldValue.Index;
try
{
FillTreeView();
}
catch { }

}
}
FillTreeView() - заполнение контрола TreeView.
(Моя реализация великовата для этого блога, но думаю что особых проблем с этим быть не должно.)
- определяем заполнение поля
(ReturnCheckedNames(parent); - то как вы запишите выделенные элементы в строку.
использую глобальные переменные т.к. в своем методе использую рекурсию для перебора ChildNodes)
Names - вместо prefix:

2.У поля должны быть свойства в которых указаны: список источник, поле с именами и поле ParentId.
fldtypes_TreeField.xml

^propertyschema^
^fields^
^field name="ListName" displayname="ListName" displaysize="20" type="Text"^
^/field^
^field name="FieldName" displayname="FieldName" displaysize="20" type="Text"^
^/field^
^field name="FieldParentID" displayname="FieldParentID" displaysize="20" type="Text"^
^/field^
^/fields^
^/propertyschema^
Отображаем только первую колонку:
^renderpattern name="DisplayPattern"^
^switch^
^expr^
^column/^
^/expr^
^case value=""^
^/case^
^default^
^column subcolumnnumber="0" htmlencode="TRUE"^
^/default^
^/switch^
^/renderpattern^
3.У поля должен быть собственный контрол редактирования treeView и скриптом обработки логики вашего дерева(при необходимости).
ControlTemplates_TreeFieldFieldControl.ascx
Добавляем.
^asp:treeview id="TreeView1" runat="server"^^/asp:treeview^
P.S. при использовании скриптов для реализации "активных" checkBox'ов в дереве приходится использовать ид элементов он формируется следующим образом:
prefix+"TreeView1n"+(i)+"CheckBox"
prefix - сгенерированный sharepoint ключ добавляемый к иду вашего контрола
Для получения:
var prefix
1. Создать функцию на onLoad одного из элементов ascx вашего поля.
В функцию в качестве параметра нужно передавать this.
Потом брать его ид и отрезать конец - известный вам ид элемента, ну и записывать его в глобальную переменную prefix.
i - порядковый номер сквозной сверху в низ ноды дерева. Т.Е. для рутовой ноды это 0, для первого child это 1 для первого child.child это 2 и т.д. Поэтому в скрипте (да и в коде) имеет смысл работать со сквозной нумерацией, после первого заполнения дерева (а не с шарепоинтовскими идами).
"TreeView1n"+(i)+"CheckBox" - формирует ид checkBox в дереве(TreeView1 - ид дерева).
С удовольствием отвечу на комментарии.

среда, 9 января 2008 г.

Поле - деревянный список для списка Shrepoint из списка.

Здравствуйте!
По работе встала задача реализовать деревянный список для заполнения поля в списке Sharepoint.
Решил реализовать следующим образом:
Источник:
1. Лист Sharepoint с полем подстановки из этого же листа (ParentId)
Приемник:
1. Создаем свое поле: две колонки в одной храним имена в другом значения ИД из списка источника. Отображаем только первую колонку.
2. У поля должны быть свойства в которых указаны: список источник, поле с именами и поле ParentId.
3. У поля должен быть собственный контрол редактирования treeView и скриптом обработки логики ващего дерева(при необходимости).
По возможности в дальнейшем опишу реализацию.