Страницы

Tuesday, March 13, 2012

Голосование своими руками в SharePoint 2010

Рис. 1.
Доброго времени суток, дорогие коллеги и просто читатели. Как часто, работая с SharePoint 2010 вам приходится, слышать просьбы, о создании голосований с изображениями? Для тех кому ("хватит это терпеть" (c) Жириновский) надоело, представляю прототип решения для SharePoint 2010 в виде веб части, подключаемой к любой библиотеке документов с изображениями.

Данная веб часть, подключается к любой библиотеке документов, и выводит список изображений для голосования (см. Рис. 1). Результаты голосования записываются в автоматически создаваемое поле SPListItem-a. В представленной версии отсутвует AJAX кнопка, и механизм запоминания проголосовавшего (он сделан на основе coockies, в полной версии).

Отрисовка списка, выполнена по статье сайта artlebedev.ru - "Блоки картинок, с подписями выровненныe по центру".

Еще одним из интересных моментов, является способ получения данных из библиотеки документов, и приведения SPListItemCollection к списку строго типизированных объектов. Методика выполнена в виде Extension методов для SPListItemCollection (см. код ниже).
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.SharePoint;

namespace ImageVotingWebPart
{
    public static class SPListItemCollectionExtension
    {
        /// 
        /// Преобразование элементов коллекции  к 
        /// 
        /// Тип результирующей коллекции
        /// 


 Исходная коллекция
        /// 
        public static IEnumerable<T> Select<T>(this SPListItemCollection collection)
            where T : new()
        {
            return collection
                .Cast<SPListItem>()
                .Select(item => item.Cast<T>());
        }

        /// 
        /// Извлечение из SPListItem полей класса  и создание его экземпляра
        /// 
        /// Тип результата
        /// 


 Преобразуемый элемент
        /// Возвращает объект класса  с теми заполнеными полями, которые были в исходном SPListItem
        public static T Cast<T>(this SPListItem item) 
            where T : new()
        {
            var result = new T();
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (PropertyInfo property in properties)
            {
                try
                {
                    object value = item[property.Name];
                    property.SetValue(result, value, null);
                }
                catch (Exception exc)
                {
                    Debug.WriteLine(exc);
                }
            }

            return result;
        }
    }
}
Модель:
namespace ImageVotingWebPart.ImageVotingWebPart
{
 public class ImageVotingItem
 {
  public int ID { get; set; }
  public string EncodedAbsThumbnailUrl { get; set; }
  public string FileLeafRef { get; set; }
  public string FileRef { get; set; }
  public int Rating { get; set; }
  public string Title { get; set; }

  public string TitleOrName
  {
   get { return !string.IsNullOrEmpty(Title) ? Title : FileLeafRef; }
  }
 }
}
Код веб части:
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
namespace ImageVotingWebPart.ImageVotingWebPart
{
    [ToolboxItemAttribute(false)]
    public class ImageVotingWebPart : WebPart
    {
        // Visual Studio might automatically update this path when you change the Visual Web Part project item.
        private const string _ascxPath = @"~/_CONTROLTEMPLATES/ImageVotingWebPart/ImageVotingWebPart/ImageVotingWebPartUserControl.ascx";

        protected override void CreateChildControls()
        {
            Control control = Page.LoadControl(_ascxPath);
            Controls.Add(control);
        }

        [Personalizable]
        [WebBrowsable]
        [WebDisplayName("Адрес библиотеки")]
        public string LibraryUrl { get; set; }
    }
}
Код контрола ImageVotingWebPartUserControl:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using ImageVotingWebPart.Properties;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace ImageVotingWebPart.ImageVotingWebPart
{
    public partial class ImageVotingWebPartUserControl : UserControl
    {
        #region Constants

        private const string c_ratingFieldName = "Rating";
        protected const string c_voteCommandName = "Vote";

        #endregion

        protected ImageVotingWebPart WebPart
        {
            get { return (ImageVotingWebPart)Parent; }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (string.IsNullOrEmpty((WebPart.LibraryUrl ?? string.Empty).Trim()))
            {
                return;
            }

            bindImageList();
        }

        protected void OnImageListItemCommand(object sender, RepeaterCommandEventArgs e)
        {
            if (e.CommandName != c_voteCommandName)
            {
                return;
            }

            int id;

            if (!int.TryParse((string)e.CommandArgument, out id))
            {
                return;
            }

            if (SPUtility.ValidateFormDigest())
            {
                SPSecurity.RunWithElevatedPrivileges(() => increaseImageRating(id));
            }
        }

        private void increaseImageRating(int id)
        {
            try
            {
                using (SPSite site = new SPSite(WebPart.LibraryUrl))
                {
                    string webRelativeUrl = WebPart.LibraryUrl.Replace(site.Url, String.Empty);
                    using (SPWeb web = site.OpenWeb(webRelativeUrl))
                    {
                        SPList list = web.GetList(WebPart.LibraryUrl);

                        if (!list.Fields.ContainsField(c_ratingFieldName))
                        {
                            list.Fields.Add(c_ratingFieldName, SPFieldType.Integer, true);
                        }
                        else
                        {
                            SPField field = list.Fields.GetField(c_ratingFieldName);

                            if (field.Type != SPFieldType.Integer)
                            {
                                throw new ApplicationException(ImageVotingResources.RatingFieldWrongTypeError);
                            }
                        }

                        SPItem item = list.GetItemById(id);
                        int rating = (int)(item[c_ratingFieldName] ?? 0);
                        item[c_ratingFieldName] = rating + 1;
                        item.Update();
                    }
                }
            }
            catch (Exception e)
            {
                setError(e.Message);
            }
        }

        private void bindImageList()
        {
            string[] usingFields = new[]
            {
                "ID",
                "EncodedAbsThumbnailUrl",
                "FileRef",
                "FileLeafRef",
                c_ratingFieldName,
                "Title",
            };

            try
            {
                using (SPSite site = new SPSite(WebPart.LibraryUrl))
                {
                    string webRelativeUrl = WebPart.LibraryUrl.Replace(site.Url, String.Empty);
                    using (SPWeb web = site.OpenWeb(webRelativeUrl))
                    {
                        SPList list = web.GetList(WebPart.LibraryUrl);
                        SPQuery query = new SPQuery();
                        query.ViewFields = string.Join("", usingFields.Select(name => string.Format("<FieldRef Name='{0}' />", name)).ToArray());
                        query.ViewAttributes = "Scope='Recursive'";
                        ImageList.DataSource = list.GetItems(query).Select<ImageVotingItem>();
                        ImageList.DataBind();
                    }
                }
            }
            catch (Exception exc)
            {
                setError(string.Format(ImageVotingResources.LoadImagesError, exc.Message));
            }
        }

        private void setError(string error)
        {
            ErrorLabel.Text = error;
        }
    }
}
Разметка контрола ImageVotingWebPartUserControl.ascx:
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>

<%@ Register TagPrefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ImageVotingWebPartUserControl.ascx.cs"

    Inherits="ImageVotingWebPart.ImageVotingWebPart.ImageVotingWebPartUserControl" %>

<%@ Import Namespace="ImageVotingWebPart.Properties" %>

<style type="text/css">

    .thumbnail

    {

        /* Убираем подчеркивание у элемента ins,

который был использован для совместимости со старыми версиями Internet Explorer */

        text-decoration: none;

        /* Следующее правило для Firefox 2 */

        display: -moz-inline-box;

        /* а это для остальных */

        display: inline-block;

        vertical-align: top;

        /* Отступы между блоками */

        margin: 2em 0 0 5px;

    }

    .thumbnail .r

    {

        background-color: #D6E4FE;

        height: 164px;

        width: 164px;

        text-align: center;

        /*layout magic*/

        display: table;

        /*# - is hack for ie7*/

        #position: relative;

        overflow: hidden;

    }

    .centered

    {

        /*# - is hack for ie7*/

        #position: absolute;

        #top: 50%;

        #left: 50%;

        display: table-cell;

        vertical-align: middle;

    }

</style>

<asp:Label ID="ErrorLabel" runat="server" ForeColor="Red" /><br />

<asp:Repeater ID="ImageList" runat="server" OnItemCommand="OnImageListItemCommand">

    <ItemTemplate>

        <ins class="thumbnail">

            <div class="r">

                <div class="centered">

                    <div style=" #position: relative; #top: -50%; #left: -50%;">

                        <a href='<%# Eval("FileRef") %>' target="_blank" title='<%# Eval("TitleOrName") %>'>

                            <img alt='' src='<%# Eval("EncodedAbsThumbnailUrl") %>' style="border-style: none;"

                                title='<%# Eval("TitleOrName") %>' />

                        </a>

                    </div>

                </div>

            </div>

            <br />

            <table width="164px">

                <col width="80%" />

                <col />

                <tr>

                    <td style="text-align: center">

                        <asp:Literal ID="Literal2" runat="server" Text='<%# Eval("TitleOrName") %>' />

                    </td>

                </tr>

                <tr>

                    <td style="text-align: center">

                        <asp:Literal ID="Literal3" runat="server" Text='<%# ImageVotingResources.RatingLabelText %>' />:&nbsp;

                        <asp:Literal ID="Literal4" runat="server" Text='<%# Eval("Rating") %>' />

                        <asp:ImageButton ID="ImageButton1" runat="server" CommandName='<%# c_voteCommandName %>'

                            CommandArgument='<%# Eval("ID") %>' ImageUrl="~/_layouts/images/ImageVotingWebPart/vote.png"

                            Width="20" Height="20" Style="vertical-align: middle;" Title='<%# ImageVotingResources.VoteButtonText %>' />

                    </td>

                </tr>

            </table>

        </ins>

    </ItemTemplate>

</asp:Repeater>

Пост написан, в ответ на многочисленные просьбы пользователей форума www.gotdotnet.ru, на тему - "КАК МНЕ СДЕЛАТЬ ГОЛОСОВАНИЕ ДЛЯ КАРТИНОК В SHAREPOINT"?

Wednesday, February 15, 2012

Silverlight, ASP.NET MVC - Продвинутый TreeView


Получил задание - реализовать групповое присвоение свойств, организационным структурам (департаментам, отделам). Немного подумав, над тем как, это будет выглядеть, я пришел к выводу, что лучше всего сделать дерево, в котором видно, само подразделение и его статус. Статусы я решил подсвечивать квадратиком (закрашенная плитка). "Погуглив", подходящих контролов типа TreeView для ASP.NET MVC 3, которые бы меня устраивали, я не нашел. Решил все реализовывать на Silverlight.

ASP.NET MVC (часть)

Модели данных:

public class StructureTreeViewItem
{
   public int ID { get; set; }
   public int? ParentID { get; set; }
   public string Name { get; set; }
   public int TypeID { get; set; }
}
public class StructureType
{
   public int ID { get; set; }
   public string Name { get; set; }
}
Контроллер
public JsonResult GetNodes()
{
    using (var db = new ...Entities())
    {
        var s = (...
                    where ....
                    select new StructureTreeViewItem()
                    {
                        ID = structs.StructureID,
                        ParentID = structs.StructureParentID,
                        Name = structsLangs.StructureName,
                        TypeID = structs.StructureTypeId
                    }).ToList();

        return Json(s, JsonRequestBehavior.AllowGet);
    }
}

public JsonResult GetStructureTypes()
{
    using (var db = new ...Entities())
    {
        var structureTypesList = new List<structuretype>();

        db.StructureTypes
            .OrderBy(q => q.id)
                .ToList()
                    .ForEach(q => structureTypesList
                        .Add(new StructureType() { ID = q.id, Name = q.Name }));

        return Json(structureTypesList, JsonRequestBehavior.AllowGet);
    }
}

public EmptyResult UpdateStructures(string structures)
{
    if (!string.IsNullOrWhiteSpace(structures))
    {
        using (var db = new ...Entities())
        {
            var treeViewStructures = new List<structuretreeviewitem>();
            structures.Split(';').ToList().ForEach(s =>
            {
                var treeViewStructureDict = s.Split('=');
                int structureId, structureTypeId;
                if (treeViewStructureDict.Length == 2
                    && int.TryParse(treeViewStructureDict[0], out structureId)
                    && int.TryParse(treeViewStructureDict[1], out structureTypeId))
                {
                    var dbSctructure = db.Structure.FirstOrDefault(q => q.StructureID == structureId);
                    if (dbSctructure != null && db.StructureTypes.FirstOrDefault(q => q.id == structureTypeId) != null)
                    {
                        dbSctructure.StructureTypeId = structureTypeId;
                        db.SaveChanges();
                    }
                }
            });
        }
    }
    return new EmptyResult();
}

Silverlight (часть)

Модели

public class StructureType : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged<t>(Expression<func<t>> raiser)
    {
        var e = PropertyChanged;
        if (e != null)
        {
            var propName = ((MemberExpression)raiser.Body).Member.Name;
            e(this, new PropertyChangedEventArgs(propName));
        }
    }

    private int m_id;
    public int ID 
    {
        get
        {
            return m_id;
        }
        set
        {
            m_id = value;
            RaisePropertyChanged(() => this.ID);
        }
    }

    private string m_name;
    public string Name 
    {
        get
        {
            return m_name;
        }
        set
        {
            m_name = value;
            RaisePropertyChanged(() => this.Name);
        }
    }

    private SolidColorBrush m_color;
    public SolidColorBrush Color
    {
        get
        {
            return m_color;
        }
        set
        {
            m_color = value;
            RaisePropertyChanged(() => this.Color);
        }
    }
}
public class StructureItem : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged<t>(Expression<func<t>> raiser)
    {
        var e = PropertyChanged;
        if (e != null)
        {
            var propName = ((MemberExpression)raiser.Body).Member.Name;
            e(this, new PropertyChangedEventArgs(propName));
        }
    }

    public StructureItem()
    {
        m_checked = false;
        m_childItems = new ObservableCollection<structureitem>();
    }

    private int m_id;
    public int ID
    {
        get
        {
            return m_id;
        }
        set
        {
            m_id = value;
            RaisePropertyChanged(() => this.ID);
        }
    }

    private int? m_parentId;
    public int? ParentID
    {
        get
        {
            return m_parentId;
        }
        set
        {
            m_parentId = value;
            RaisePropertyChanged(() => this.ParentID);
        }
    }

    private string m_name;
    public string Name
    {
        get
        {
            return m_name;
        }
        set
        {
            m_name = value;
            RaisePropertyChanged(() => this.Name);
        }
    }

    private bool m_checked;
    public bool Checked 
    {
        get
        {
            return m_checked;
        }
        set
        {
            m_checked = value;
            RaisePropertyChanged(() => this.Checked);
        }
    }

    private StructureType m_structureType;
    public StructureType StructureType
    {
        get
        {
            return m_structureType;
        }
        set
        {
            m_structureType = value;
            RaisePropertyChanged(() => this.StructureType);
        }
    }

    ObservableCollection<structureitem> m_childItems;
    public ObservableCollection<structureitem> ChildItems 
    {
        get
        {
            return m_childItems;
        }
        set
        {
            m_childItems = value;
            RaisePropertyChanged(() => this.ChildItems);
        }
    }
}
При загрузке Silverlight приложение, подтягивает данные, о типах структурных подразделений. После чего, они привязываются к ComboBox-у, а затем загружаются сами структурные подразделения. После загрузки структурных подразделений, происходит их сортировка в иерархическую коллецию с помощью рекурсии.
public MainPage()
{
    InitializeComponent();
    onEndLoad();
}
private void disableUi()
{
    Dispatcher.BeginInvoke(() =>
    {
        // Бизи индикатор
        bi.IsBusy = true;
    });
}
private void enableUi()
{
    Dispatcher.BeginInvoke(() =>
    {
        // Отключение бизи индикатора
        bi.IsBusy = false;
    });
}
private void onEndLoad()
{
    disableUi();

    var webClient = new WebClient();
    webClient.OpenReadCompleted += onGetStructureTypesCompleted;
    webClient.OpenReadAsync(new Uri("/Core/GetStructureTypes", UriKind.Relative));
}
private void onGetStructureTypesCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error != null)
    {
        if (e.Error.Message != null)
        {
            MessageBox.Show(e.Error.Message);
        }

        enableUi();
        return;
    }

    var items = (JsonArray)JsonValue.Load(e.Result);
            
    var typedItems = new ObservableCollection<structuretype>();

    foreach (var item in items)
    {
        var structureType = new StructureType();
        structureType.ID = int.Parse(item["ID"].ToString());
        structureType.Name = item["Name"].ToString().Replace("\\\"", "\"").Trim('"').Trim();

        // Тут подбираем цвета для каждого из типов структур
        switch (structureType.Name)
        {
            case "Сервисное":
                structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0x72, 0x72));
                break;
            case "Зарабатывающее":
                structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0x40, 0xC7, 0x40));
                break;
            default:
                structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0xB4, 0xB4, 0xB4));
                break;
        }

        typedItems.Add(structureType);
    }

    cbStructureTypes.ItemsSource = typedItems;

    var serviceUri = new Uri("/Core/GetNodes", UriKind.Relative);
    var webClient = new WebClient();
    webClient.OpenReadCompleted += onGetNodesCompleted;
    webClient.OpenReadAsync(serviceUri);
}
private void onGetNodesCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error != null)
    {
        if (e.Error.Message != null)
        {
            MessageBox.Show(e.Error.Message);
        }

        enableUi();
        return;
    }

    var notSortedItems = new ObservableCollection<structureitem>();
    var structureTypes = cbStructureTypes.ItemsSource as ObservableCollection<structuretype>;

    var nodes = (JsonArray)JsonValue.Load(e.Result);

    foreach (var node in nodes)
    {
        var structureItem = new StructureItem();

        structureItem.ID = int.Parse(node["ID"].ToString());
        structureItem.Name = node["Name"].ToString().Replace("\\\"", "\"").Replace("\"\"", "\"");

        var typeId = int.Parse(node["TypeID"].ToString());
        foreach (var sType in structureTypes)
        {
            if (sType.ID == typeId)
            {
                structureItem.StructureType = sType;
            }
        }

        structureItem.ParentID =
            node["ParentID"] == null || string.IsNullOrWhiteSpace(node["ParentID"].ToString()) ?
            0 : int.Parse(node["ParentID"].ToString());

        notSortedItems.Add(structureItem);
    }

    var sortedItems = new ObservableCollection<structureitem>();

    notSortedItems.ToList().ForEach(q =>
        {
            if (q.ParentID.HasValue && q.ParentID.Value == 0)
            {
                sortedItems.Add(storeTree(q, notSortedItems));
            }
        });

    twStructures.ItemsSource = sortedItems;

    enableUi();
}
private StructureItem storeTree(StructureItem item, ObservableCollection<structureitem> allItems)
{
    var childItems = allItems.Where(q => q.ParentID == item.ID).ToList();

    if (childItems.Count > 0)
    {
        childItems.ForEach(q =>
        {
            item.ChildItems.Add(storeTree(q, allItems));
        });
    }

    return item;
}
// Обработчик события нажатия на чекбокс в дереве
private void checkBox_Click(object sender, RoutedEventArgs e)
{
    if (m_selectedItems == null)
        m_selectedItems = new List<structureitem>();

    var currentCheckBox = sender as CheckBox;
    if (currentCheckBox.IsChecked.HasValue && currentCheckBox.IsChecked.Value)
    {
        m_selectedItems.Add(currentCheckBox.Tag as StructureItem);
    }
    else
    {
        m_selectedItems.Remove(currentCheckBox.Tag as StructureItem);
    }
}
private void bSave_Click(object sender, RoutedEventArgs e)
{
    if (cbStructureTypes.SelectedItem != null && m_selectedItems != null && m_selectedItems.Count > 0)
    {
        disableUi();
        m_selectedItems.ForEach(q =>
            {
                q.StructureType = cbStructureTypes.SelectedItem as StructureType;
                q.Checked = false;
            });

        sendToSite();
        m_selectedItems.Clear();
        enableUi();
    }
}
// Вызов метода апдейт
private void sendToSite()
{
    var webClient = new WebClient();
    webClient.Headers["Content-type"] = "application/x-www-form-urlencoded";
    var parametersString = new StringBuilder();
    m_selectedItems.ForEach(s =>
    {
        if (parametersString.ToString() != string.Empty)
            parametersString.Append(";");
        parametersString.AppendFormat("{0}={1}", s.ID, s.StructureType.ID);
    });
    webClient.UploadStringAsync(new Uri("/Core/UpdateStructures/", UriKind.Relative), "POST", string.Format("structures={0}", parametersString.ToString()));
}
Разметка XAML
<Grid.Resources>

    <sdk:HierarchicalDataTemplate x:Key="tvTemplate" ItemsSource="{Binding Path=ChildItems}" >

        <StackPanel Orientation="Horizontal">

            <CheckBox Tag="{Binding}" IsChecked="{Binding Path=Checked, Mode=TwoWay}" Click="checkBox_Click" />

            <Rectangle Height="15" Width="15" Margin="0,0,3,0" StrokeThickness="1" Fill="{Binding Path=StructureType.Color}"/>

            <TextBlock Text="{Binding Path=Name}" />

        </StackPanel>

    </sdk:HierarchicalDataTemplate>

</Grid.Resources>

<sdk:TreeView Margin="12,41,12,12"

                Name="twStructures"

                BorderBrush="#FFB1B1B1"

                ItemsSource="{Binding}"

                ItemTemplate="{StaticResource tvTemplate}"

                SelectedValuePath="ID"/>

<ComboBox Height="23" Margin="12,12,0,0" Name="cbStructureTypes" VerticalAlignment="Top" ItemsSource="{Binding}" HorizontalAlignment="Left" Width="219">

    <ComboBox.ItemTemplate>

        <DataTemplate>

            <StackPanel Orientation="Horizontal">

                <Rectangle Height="15" Width="15" Margin="0,0,3,0" StrokeThickness="1" Fill="{Binding Path=Color}"/>

            <TextBlock Text="{Binding Path=Name}" />

            </StackPanel>

        </DataTemplate>               

    </ComboBox.ItemTemplate>

</ComboBox>

<Button Content="Сохранить" Height="23" Margin="237,12,0,0" Name="bSave" VerticalAlignment="Top" HorizontalAlignment="Left" Width="75" Click="bSave_Click" />

<toolkit:BusyIndicator BusyContent="Loading please wait..." Name="bi" />

Интересно, кто нибудь, умеет правильно вызывать методы контроллеров с параметрами из Silverlight?

Tuesday, February 14, 2012

SPSecurity.RunWithElevatedPrivileges в Sharepoint 2010

Недавно мне попался в руки проект, в котором я принимал участие в роли bugfixer-а. Мне попался стандартный блок кода с повышением привилегий при работе списком в EventReciever-е списка:

SPSecurity.RunWithElevatedPrivileges(delegate()
{
    using (SPSite site = properties.OpenSite())
    {
        using (SPWeb web = site.OpenWeb())
        {
С виду этот блок кода выглядел, нормальным, но он не работал. Не долго думая, я включил рефлектор и решил посмотреть, что же тут не так? Данные из рефлектора:
public SPSite OpenSite() 
{ 
    if ((this.m_site == null) && (this.WebUrl != null)) 
    { 
        if (base.EventUserToken != null) 
        { 
            this.m_site = new SPSite(this.WebUrl, base.EventUserToken); 
        } 
        else
        { 
            this.m_site = new SPSite(this.WebUrl); 
        } 
        this.m_siteCreatedByThis = true; 
    } 
    return this.m_site; 
}
public SPWeb OpenWeb()
{
    this.OpenSite();
    if (this.m_site != null)
    {
        return this.m_site.OpenWeb(this.RelativeWebUrl);
    }
    return null;
}

Оказалось, что сайт, открывается всегда с определенным EventUserToken, который в контексте EventReciever всегда был != null.

Будьте осторожны, при использовании методов OpenSite(), OpenWeb() внутри List EventReciever-ов.