Обновление уже развернутых типов контента SharePoint для обработки дополнительных событий элементов

У меня есть тип контента сайта, который использовался для нескольких списков в моем семействе сайтов. В этом типе контента я описываю приемник событий для обработки события ItemAdding. Это прекрасно работает. Теперь мне нужно обновить тип контента, чтобы также обрабатывать ItemUpdating. Я попытался просто изменить xml для своего типа контента, так как это, казалось, позволяло легко отслеживать версии. Это сработало в том смысле, что мои обновления были применены к типу контента сайта, но не к моим спискам, которые использовали этот тип контента. Этого и ждали. Затем я заметил, что SharePoint SDK принимает мрачный взгляд на это:

Ни при каких обстоятельствах вы не должны обновлять файл определения типа контента для типа контента после того, как вы установили и активировали этот тип контента. Windows SharePoint Services не отслеживает изменения, внесенные в файл определения типа контента. Следовательно, у вас нет метода для передачи изменений, внесенных в типы контента сайта, в дочерние типы контента.

Затем SDK указывает на пару разделов, в которых описывается, как использовать пользовательский интерфейс или код для внесения изменений. Поскольку пользовательский интерфейс не поддерживает приемники событий, я думаю, что выберу кодовый путь.

Я думал, что смогу сделать что-то подобное и просто добавить новый приемник событий в копию списка типа контента:

SPList list = web.Lists["My list"];
SPContentType ctype = list.ContentTypes["My content type"];
// Doesn't work -- EventReceivers is null below.
ctype.EventReceivers.Add(SPEventReceiverType.ItemUpdating, 
                         "My assembly name", "My class name");

Но загвоздка в том, что ctype.EventReceivers здесь имеет значение null, хотя у меня уже есть ItemAdding, подключенный к этому списку. Судя по всему, он был перемещен в сам список. Итак, в списке есть действительная коллекция EventReceivers.

SPList list = web.Lists["My list"];
list.EventReceivers.Add(SPEventReceiverType.ItemUpdating, 
                        "My assembly name", "My class name");

Итак, у меня есть пара вопросов:

  1. Правильный ли способ сделать это - просто добавить новых приемников событий прямо в список и вообще забыть о моем типе контента?
  2. Как лучше всего справиться с этим с точки зрения управления конфигурацией, чтобы выполнить это изменение? Должен ли я создать простое консольное приложение, чтобы найти все подходящие списки и изменить каждый из них? Или каким-то образом создание функции - лучший вариант? В любом случае, похоже, что это изменение произойдет само по себе, и его трудно обнаружить будущим разработчикам, которым, возможно, потребуется работать с этим типом контента.

person Chris Farmer    schedule 02.07.2009    source источник
comment
Хороший вопрос: я хотел спросить нечто подобное.   -  person Alex Angas    schedule 03.07.2009


Ответы (3)


Вы вызывали ctype.Update (true) после добавления EventReceiver? Если вы этого не сделаете, это не будет продолжаться. И не используйте тип содержимого List, вместо этого используйте SPWeb.ContentTypes.

У меня работает этот код:

var docCt = web.ContentTypes[new SPContentTypeId("0x0101003A3AF5E5C6B4479191B58E78A333B28D")];
//while(docCt.EventReceivers.Count > 0)
//  docCt.EventReceivers[docCt.EventReceivers.Count - 1].Delete();
docCt.EventReceivers.Add(SPEventReceiverType.ItemUpdated, "ASSEMBLYNAME, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c5b857a999fb347e", "CLASSNAME");

docCt.Update(true);

Параметр true означает, что он также передается всем дочерним ContentTypes. (т.е. ко всем спискам, использующим тип контента).

person Colin    schedule 02.07.2009
comment
Думаю, я даже не пошел по этому пути после того, как SDK подразумевал не касаться определения типа контента. Теперь я вижу, что это другое, так как вы можете явно указать ему, чтобы изменения были опущены. Кажется, это идеальный способ сделать это. Я попробую. Спасибо! Теперь, как вы это делаете с точки зрения управления конфигурацией? :) - person Chris Farmer; 03.07.2009
comment
Это правда, что вы не должны трогать CType def. но это означает ТОЛЬКО тот CAML, который использовался для его создания / Через объектную модель вы можете делать то, что вам нравится. Мы выполняем приведенный выше код через приемник функции, который связан с функцией, которая содержит определения cType. the while ... delete было добавлено, чтобы мы могли деактивировать / активировать эту функцию столько раз, сколько захотим, и всегда оставлять 1 приемник событий на каждый cType (IMHO, самый чистый путь). Итак, на ваш вопрос дан ответ? - person Colin; 03.07.2009

Чтобы ответить на вторую часть вашего вопроса, это непростая вещь из-за того, что изменения типов контента в коллекции sitecollection не будут перенесены в списки, где они используются. «Копия», по сути, создается из полей в коллекции сайтов, и после добавления типа контента в список между ними больше нет связи. Я думаю, это связано с тем, что вы должны вносить изменения в списки, не затрагивая коллекцию сайтов. Как бы то ни было, мой вклад в эту «проблему» и то, как я ее решил, заключается в том, чтобы сделать xml «мастером», а в приемнике функций я вытаскиваю xml и нахожу все места, где используется тип контента, и оттуда обновляю типы контента (на самом деле fieldrefs) на уровне списка, чтобы он соответствовал тому, что в xml. Код выглядит примерно так:

var elementdefinitions = properties.Feature.Definition.GetElementDefinitions();

foreach (SPElementDefinition elementDefinition in elementdefinitions)
{
   if (elementDefinition.ElementType == "ContentType")
   {
     XmlNode ElementXML = elementDefinition.XmlDefinition;

     // get all fieldrefs nodes in xml
     XmlNodeList FieldRefs = ElementXML.ChildNodes[0].ChildNodes;

     // get reference to contenttype
     string ContentTypeID = ElementXML.Attributes["ID"].Value.ToString();
     SPContentType ContentType = 
         site.ContentTypes[new SPContentTypeId(ContentTypeID)];

     // Get all all places where the content type beeing used
     IList<SPContentTypeUsage> ContentTypeUsages = 
        SPContentTypeUsage.GetUsages(ContentType);
   }
}

Следующее, что нужно сделать, это сравнить ссылки на поля в xml xml с полями в списке (выполняется с помощью атрибута ID) и убедиться, что они равны. К сожалению, мы не можем обновить все вещи в классе SPFieldLink (fieldref) и (да, я знаю, что он не поддерживается) здесь я фактически использовал отражение для обновления этих значений (например, ShowInEditForm).

person Johan Leino    schedule 03.07.2009
comment
Хороший фрагмент кода, но не полностью связанный с добавлением itemeventreceiver, хотя +1 - person Colin; 03.07.2009
comment
@Colin - Нет, это больше связано с обновлением изменений типов контента. Я думаю, что на добавление приемников событий уже был дан ответ - person Johan Leino; 03.07.2009

Что касается второй части вашего вопроса, я хотел бы рассказать, что мы делали для подобных ситуаций в прошлом. В нашей ситуации нам потребовалась пара разных скриптов: один, который позволил бы нам распространять обновления типа контента на все списки во всех веб-сайтах, а другой, который сбрасывал бы главные страницы / макеты страниц до определения сайта (ненастроенная форма ).

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

Пользовательские команды stsadm для SharePoint

person UnhipGlint    schedule 02.07.2009