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

Языконезависимая фильтрация полей типа choise в коде

Если вы хотя бы иногда сталкивались в полями "выбор" при разработке для Sharepoint, вы знаете, что они представляют собой банальный массив строк, которые меняются в зависимости от используемого языка. И если вам нужно, чтобы ваше решение работало одинаково, не зависимо от языка установки то приходится использовать всякие нетривиальные решения. Вот так я, например, сделал фильтрацию по статусам задач из списков типа "Задачи".  Выглядит это вот так:


И, by design, должно фильтровать список задач по соответствующим статусам. Проблема в том, что при англоязычной установке статус называется "Not Started", а на русскоязычной "Не начата". И более того, все эти статусы можно банально поменять или добавить новые, которые тоже должны работать как ожидается.



Использование модальных диалогов в SharePoint

Возможность редактирования и добавления элементов нужна всегда, а возиться и делать свои собственные диалоги не всегда есть возможность и необходимость. Тем более, если есть возможность использовать стандартные. Давайте, посмотрим, куда, например, ведет ссылка "Add new item" в стандартном списке "Tasks":


Ведет она вот сюда:
Add new item


Если же быть более точным, то эта ссылка работает только в том случае, если у пользователя отключен JavaScript. В противном случае вызывается функция EditItem2, которая и открывает красивое диалоговое окошка для добавления нового элемента.

Итак, первый способ использование модальных диалоговых окошек - использование этой функции. Правда, для ее использования вам нужно знать несколько параметром: Guid списка (обязательно), ID элемента (обязательно для редактирования), ID типа контента (нужно только, если ваш список использует несколько типов контента, и вам нужен отличный от того, что по умолчанию). Ну и самое главное - нужно указать тип диалога. Их всего три: PageType=4 (просмотр), PageType=6 (редактирование) и PageType=8 (создание нового элемента). То есть, получается примерно так:

Просмотр элемента

Редактирование элемента

Добавление элемента

А теперь второй способ:


среда, 19 марта 2014 г.

Делаем плагины в программах на .Net

В .Net есть два варианта того, как сделать свои плагины: использование Microsoft Enterprise Library и использование отражений (reflection). Первый способ не совсем тривиальный и дня него нужно дополнительно скачивать MEL. Второй же способ доступен "из коробки" и, в большинстве ситуаций, его возможностей вполне достаточно.
Вообще, механизм отражений в .net - вещь достаточно мощная и интересная, так что, в будущем расскажу про него еще что-нибудь интересное. А пока переходим к плагинам.

Так как основа нашего механизма плагинов - отражение, то нам нужен какой-то базовый класс, который мы будет создавать при загрузке плагина. Так как кроме сигнатур предполагаемых методов с нашей стороны ничего больше не нужно, то идеальный вариант - создать интерфейс, поместив его в отдельную сборку (которую потом можно отдать сторонним разработчикам, которые будут делать плагины):

    
    public interface IPluginDef
    {
        string SayHello();
    }

Итак, что наследуя ваш интерфейс, насоздавали классов, наприсылали вам DLL'ек c ними и надо с этип добром теперь что-то делать. Для начала объявим где-нибудь в вашей программе глобальную переменную, в которой будут жить загруженные классы плагинов:
    
  List< IPluginDef > all_plugins = new List< IPluginDef >(); 



Управление пользователями SharePoint из кода

Все, что необходимо для управления пользователями в объектной модели SharePoint содержится в пространстве имен Microsoft.Office.Server.UserProfiles самый интересный для нас класс которого - UserProfileManager. Все, что нужно для его инициализации это передать в конструктор текущий контекст:

     UserProfileManager userPM = new UserProfileManager(SPServiceContext.Current);

Все готово к работе. Теперь, например, можно создать нового пользователя:
    string accountName = "DOMAIN\\newuser";
    if (!userPM.UserExists(accountName)) 
    {
      userPM.CreateUserProfile(accountName);

      UserProfile user = userPM.GetUserProfile(accountName);
      //Установим отображаемое имя пользователя. По сути, PreferredName - это AD'шный DisplayName, но об этом ниже.
      user["PreferredName"] = "Новый пользователь";
      user.Commit();
    }

А потом сказать, что он и Вася Пупки - коллеги, а Петя Васечкин - его руководитель:
    UserProfile user = userPM.GetUserProfile("DOMAIN\\newuser");
    UserProfile userColleague = userProfileManager.GetUserProfile("DOMAIN\\vpupkin");
    if (!user.Colleagues.IsColleague(userProfileColleague.ID))
    { 
       //если использовать Create, то коллега получит мейл, что его добавили. 
       //Если это не нужно, используйте CreateWithoutEmailNotification, сигнатура у него такая же.                  
       user.Colleagues.Create(userColleague,                        
       ColleagueGroupType.General,      
       //если нужно, тут указываем группу конфиденциальности, в которую поместить коллегу.      
       string.Empty,   
       //должен ли коллега быть частью рабочей группы                    
       true,                               
       Privacy.Public);  
       
       if (userPM.UserExists("DOMAIN\pvasechkin")) 
       {
          user["Manager"].Clear();               
          user["Manager"].Add("DOMAIN\pvasechkin");                    
          user.Commit();
       }
    }

Ну и в конце, создадим Васе персональный сайт:
    UserProfile user = userPM.GetUserProfile(sAccount);
    user.CreatePersonalSite();

Вообще же, класс UserProfile очень удобен для получения всевозможной информации о пользователе.


вторник, 18 марта 2014 г.

Про инженеров

Жизненно...


Как найти все списки, созданные на базе определенного шаблона

Если вам нужно найти все списки созданные на базе, например, шаблона "Задачи" (tasks), то, прежде всего, нужно знать ID типа шаблона (можно подглядеть вот тут: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.splisttemplatetype.aspx )
Зная ID шаблона можно сделать вот так:
    foreach (SPList list in SPContext.Current.Web.Lists) 
    { 
        //107 - шаблон tasks 
        if (!list.Hidden & (int)list.BaseTemplate == 107) 
        { 
           //Мы нашли список на базе шаблона 
        } 
    } 
Если же вы хотите найти списки, созданные на базе какого-то кастомного шаблона, то так просто это сделать не получится. В таком случае list.BaseTemplate все равно будет содержать идентификатор базового шаблона, на базе которого создан ваш шаблон. Поэтому, чтобы как-то определить на основе вашего шаблона создан список, или на основе базового, то нужно ввести в шаблон что-то уникальное, например, добавить свой тип контента. Тогда, если базовый тип списка совпадает с базовым для вашего шаблона, то все что нужно остается - это проверить, содержит ли список ваш тип контента:
    foreach (SPList list in SPContext.Current.Web.Lists) 
    { 
        if (!list.Hidden & (int)list.BaseTemplate == 107) 
        { 
            //если мы хотим найти лист, созданный на базе кастомного шаблона 
            foreach (SPContentType cT in list.ContentTypes) 
            { 
                if (cT.Name == "MyNewContentType") 
                //мы нашли необходимый нам список 
                break; 
            } 
        } 
    } 


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

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

Иногда бывает необходимо отлавливать события внутри приложения вне зависимости от того, какой контрол активен. Например, вы хотите реализовать аналог "IDDQD" в своем приложении ;))) Или просто хотите обрабатывать события по каким-то своим законам. Сегодня я расскажу вас как это сделать.

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

    
    public delegate void MouseWheelEvent(int rotation);
    public delegate void KeyDownEvent(int keyCode);

    public class GlobalEventHandler : IMessageFilter
    {
        public event MouseWheelEvent TheMouseWheel;
        public event KeyDownEvent TheKeyDown;

        public bool PreFilterMessage(ref Message m)
        {

        }

    }
следующий этап - реализовать метод PreFilterMessage:


воскресенье, 16 марта 2014 г.

Про скорость циклов for

Вы наверняка часто натыкались в сети на статьи о том, как оптимальнее использовать циклы. Многие вполне авторитетные источники (например, Bill Wagner в книжке Effective C#) заявляют, что использование array.Length в качестве условия окончания цикла более оптимально, нежели использование просто цифр или переменной. То есть вот так:


            int[] testArray = new int[1000000];

            
            int dim = testArray.Length;
            for (int i = 0; i < dim; i++)
            {
                Console.WriteLine(testArray[i]);
            }
            //Предполагается, что компилятор сгенерирует вот такой код:
            //
            //for (int i = 0; i < dim; i++)
            //{
            //    if (i < testArray.Length)                    
            //        testArray[i] = i;
            //    else 
            //        throw new IndexOutOfRangeException();
            //}

            for (int i = 0; i < testArray.Length; i++)
            {
                testArray[i] = i;
            }           
Сегодня я решил потратить полчасика и проверить как дела обстоят на самом деле.


Выясняем все о том кто запустил ваш процесс.

Если вы хотите узнать кто запустил ваше приложение, то это очень просто сделать двумя способами. Если вас интересует только домен и имя пользователя, то достаточно использовать класс Environment:
    string domainName = Environment.UserDomainName;
    string useName = Environment.UserName;

Если же вы хотите знать больше, то вначале надо добавить в проект ссылку на библиотеку System.Security, а потом сделать вот так:
           WindowsIdentity identity = WindowsIdentity.GetCurrent();

И можно узнавать подробности:
            identity.IsAnonymous //Анонимный аккаунт?
            identity.IsSystem //Системный аккаунт

А дописав еще пару строк можно узнать запущена ли ваша программа пользователем с правами администратора или нет:
        public static bool IsAdmin()
        {
            WindowsIdentity identity = WindowsIdentity.GetCurrent();
            WindowsPrincipal principal = new WindowsPrincipal(identity);
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }

Ну и, наконец, можно узнать пути ко всем папкам текущего использую функцию Environment.GetFolderPath, принимающая в качестве параметра тип необходимой папки из перечисления Environment.SpecialFolder. Например, AppData.

     string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);