Как оказалось, это довольно частая задача. Но практически нигде не разрешённая.
Итак, у нас есть обработчик события создания элемента списка (ItemAdding); и нам необходимо сразу после создания перейти на другую страницу.
На первый взгляд задача простая: у нас есть и «Page.Response.Redirect(…)» и «SPUtility.Redirect(…)».
Но… Для первого случая у нас нет объекта Page! А второй метод требует указания HttpContext, который в обработчиках события пуст.
Решение!
Как всегда хак! :)
Как известно, создание элементов списка можно отменять (для этого обработчики событий видимо изначально и создавались). При этом, отменяя, мы можем указать адрес, на который нам следует перейти (чтобы посмотреть причину отмены) (доступно только в SharePoint 2010).
То есть, мы можем отменить создание элемента, и сделать искомый редирект на нужную страницу. Нам нужно только пересоздать отменённый элемент. Вот тут и кроется ограничение этого метода – у нас нет доступа к вложениям элемента при его создании.
Пример кода реализации данного метода:
Событие ItemAdded для этого метода не подходит, т.к. там элемент списка уже создан и отменить это действие уже нельзя.
Итак, у нас есть обработчик события создания элемента списка (ItemAdding); и нам необходимо сразу после создания перейти на другую страницу.
На первый взгляд задача простая: у нас есть и «Page.Response.Redirect(…)» и «SPUtility.Redirect(…)».
Но… Для первого случая у нас нет объекта Page! А второй метод требует указания HttpContext, который в обработчиках события пуст.
Решение!
Как всегда хак! :)
Как известно, создание элементов списка можно отменять (для этого обработчики событий видимо изначально и создавались). При этом, отменяя, мы можем указать адрес, на который нам следует перейти (чтобы посмотреть причину отмены) (доступно только в SharePoint 2010).
То есть, мы можем отменить создание элемента, и сделать искомый редирект на нужную страницу. Нам нужно только пересоздать отменённый элемент. Вот тут и кроется ограничение этого метода – у нас нет доступа к вложениям элемента при его создании.
Пример кода реализации данного метода:
public
override void ItemAdding(SPItemEventProperties
properties)
{
base.ItemAdding(properties);
//Отключаем сам обработчик, чтобы избежать зацикливания
EventFiringEnabled = false;
var list =
properties.List;
//Создаём элемент списка и заполняем его свойствами
из только что созданного пользователем элемента
var ni = list.Items.Add();
//Выбираем все созданные пользователем свойства доступные для редактирования,
а также свойство Title (Название)
foreach (var ff in list.Fields.Cast<SPField>().Where(spf => !spf.ReadOnlyField
&& (spf.FromBaseType == false || spf.InternalName
== "Title")))
ni[ff.InternalName]
= properties.AfterProperties[ff.InternalName];
ni.SystemUpdate();
//Включаем
обработчик
EventFiringEnabled
= true;
//Отменяем создание элемента созданного пользователем
properties.Cancel = true;
//Устанавливаем действие при отмене - переход на другую страницу
properties.Status = SPEventReceiverStatus.CancelWithRedirectUrl;
//Устанавливаем адрес страницы, на которую делаем переход. В
данном случае - страница редактирования только что созданного элемента
properties.RedirectUrl = string.Format("/{0}?ID={1}",
properties.List.Forms[PAGETYPE.PAGE_EDITFORM].Url, ni.ID);
}
Событие ItemAdded для этого метода не подходит, т.к. там элемент списка уже создан и отменить это действие уже нельзя.
а кнопочку на форме переопределить не проще? Если кто-то подписался на событие создания item кроме тебя, то что делать?
ОтветитьУдалить1. Не проще, если у нас много таких списков. Тем более если они уже развёрнуты.
ОтветитьУдалитьЕсли же всё ещё впереди, то да, тут в зависимости от задачи.
2. Ставим Sequence у этого события побольше, либо у того другого поменьше. И всё будет ok!
1. Что может быть проще, чем унаследоваться от класса SaveButton и добавить редирект?
Удалить2. Кроме события adding есть событие added, и тут уже никто не сможет подписаться.
Т. е. своим хакам ты ломаешь стандартный функционал, хотя можешь поменять renderingtemplate формы и поставить свою кнопку, а это не есть гут.
3. В коде при копировании не учтены seald и hidden филды;
4. Хак доступен только для SharePoint 2010.
1. Что может быть проще, чем унаследоваться от класса SaveButton и добавить редирект?
ОтветитьУдалить2. Кроме события adding есть событие added, и тут уже никто не сможет подписаться;
Т. е. своим хакам ты ломаешь стандартный функционал, хотя можешь поменять renderingtemplate формы и поставить свою кнопку, а это не есть гут.
3. В коде при копировании не учтены seald и hidden филды;
4. Хак доступен только для SharePoint 2010.
В SharePoint есть еще delegate контролы в них тоже можно организовать редирект, но в данном случае своя кнопка - это самое верное решение.
1. А если элемент создаётся не через форму? Есть и другие способы создать элемент списка в обход формы.
Удалить2. А кто должен? Если это наш список, мы с ним работаем! Мы изменяем поведение только того списка к которому прицеплено наше событие.
3. Это специально так сделано, пользователь ничего не может ввести в hidden поля. А если нужно sealed поля, то пожалуйста, модифицируйте код под ваши нужны!
4. Да, об этом упоминалось в статье.
1. Допустим элемент создаётся рабочим процессом...
УдалитьЕсли SPListItem создается в job, workflow, console, services или в других местах, зачем редирект? Редирект нужен на форме, а так только увевичиваете число обращений к базе. Для таких вещей и были введены renderingtemplates и своя страница для создания SPListItem.
УдалитьУ человека была реальная задача, сделать редирект после создания элемента списка рабочим процессом, который запускался из другого списка.
УдалитьВ связи с чем и возникла данная статья.
Простите, а чем Вас не устраивает такое решение?
ОтветитьУдалитьhttp://www.sharepointkings.com/2008/05/httpcontext-in-eventhandler.html
Вы его пробовали?
УдалитьОно написано для SP'07 и возможно там оно работало.
Но для SP'10 оно не работает, и как я писал выше "HttpContext.Current" всегда пусто!
Я пробовал несколько вариантов получения HttpContext, но без результатно. Если у вас есть рабочий вариант, то прошу вас поделиться!
УдалитьСпасибо!
Да, пробовал.
Удалитьhttp://dl.dropbox.com/u/34871964/EventHandlersTest.zip
Да, контекст существует. Но redirect происходит если только создавать элемент списка через форму. Если же элемент списка создавать рабочим процессом, то нет.
УдалитьВ FormContext всегда можно указать редирект неважно через кнопку или другое поле.
ОтветитьУдалитьВот только где его взять, если SPContext отсутствует???
УдалитьИ в конструкторе эвент ресивера SPContext.Current тоже равен null?
УдалитьЯ не правильный вопрос задал. )
УдалитьКаким образом вы укажете OnSaveHandler (я так понял мы про него говорим) в обработчике события?
Игорь, мне потребовалось подобное решение..
ОтветитьУдалитьно редирект просто игнорируется, и переход происходит на стандартную error.aspx
или для библиотеки документов подобное не реализуемо?
Дайте Ваш код.
Удалитьproperties.RedirectUrl задаёте?
properties.Status = SPEventReceiverStatus.CancelWithRedirectUrl; - так задано?
Это два ключевых свойства для осуществления редиректа.
Этот комментарий был удален автором.
ОтветитьУдалитьpublic override void ItemAdding(SPItemEventProperties properties)
ОтветитьУдалить{
base.ItemAdding(properties);
this.EventFiringEnabled = false;
....
int _numb = GetNextNumberDocument(folderparent, properties.List, _nameDocSet);
Hashtable prop = new Hashtable();
var list = properties.List;
string _title= properties.List.Title + "-" + _numb.ToString();
SPContentTypeId oCntID = new SPContentTypeId();
oCntID = list.ContentTypes[_nameDocSet].Id;
properties.AfterProperties[_nameFieldNumber] = _numb;
properties.AfterProperties["Title"] = properties.List.Title + "-" + _numb.ToString();
//Выбираем все созданные пользователем свойства доступные для редактирования, а также свойство Title (Название)
foreach (SPField ff in list.Fields)
{
if (!ff.ReadOnlyField && (ff.FromBaseType == false || ff.InternalName=="Title"))
{
object vv= properties.AfterProperties[ff.InternalName];
if (vv!=null && !string.IsNullOrEmpty(vv.ToString())) prop.Add(ff.InternalName,vv.ToString());
}
}
DocumentSet oDocSet = DocumentSet.Create(folderparent, _title, oCntID, prop, true);
properties.Cancel = false; //ставил true тоже
properties.Status = SPEventReceiverStatus.CancelWithRedirectUrl;
properties.RedirectUrl = oDocSet.WelcomePageUrl;
}
}
this.EventFiringEnabled = true;
}
если отправите письмо мне на ящик, то смогу выслать и весь проект...
мыло не хочу светить в явном виде, чтобы лишний раз его не светить, но оно состоит из моего ника и домена сайта, который указан с ником. без www.
Какой точно адрес передаётся в oDocSet.WelcomePageUrl?
УдалитьВозможно должен быть локальный, но точно не помню уже...
Я пробовал создавать пустую страницу test.aspx ложил в layouts/myasp/
ОтветитьУдалитьproperties.RedirectUrl ="/_layouts/myasp/test.aspx";
результат тот же был...открывалась стандартная страница
завтра на работе гляну какой именно адрес передается, но кажется там полный адрес:
http://.../../_layouts/не_помню названия_страницы_отображения_доксета.aspx?параметры"
вот типа такого передается.
Высылайте тогда проект, посмотрю у себя.
ОтветитьУдалитьМожно в скайп: akimov-ru