понедельник, 18 июня 2012 г.

SharePoint: Столбцы метаданных. Часть 2: Custom Filter.aspx page

Первая часть статьи здесь.

Второй важный момент, которому стоит уделить внимание при разработке настраиваемого типа столбца SharePoint - это отображение значений столбцов в шаблоне отображения (RenderPattern) под названием HeaderPattern,  а точнее, отображение фильтруемых значений в заголовке столбца

В описываемом типе столбца внешне CAML-разметка шаблона, по сути, ничем не будет отличаться от такого же HeaderPattern, как скажем, у типа Lookup, за исключением первой строки, приведенной в первой части статьи. Изменения здесь коснутся страницы /_layouts/Filter.aspx, на которую происходит запрос при клике на заголовке столбца.

Если посмотреть на результат запроса после проведения кастомизации, рассказанной в первой части статьи, то результат малоудовлетворительный: 


В текстах гиперссылках – различные значения столбцов, представленные по ранее оговоренному шаблону хранения значений столбцов данного типа: [URL узла][Разделитель][ID списка]. Задача будет состоять в корректном выводе текстов  гиперссылок. Лучшим способом решения данной задачи будет простая замена текстов гиперссылок в уже сформированной для фильтрации HTML-разметке в функции GetDistinctValues() в коде страницы /_layouts/Filter.aspx. Т.е., заменить тексты гиперссылок фильтрации, представленных в виде URL узлов и ID списков на соответствующие названия узлов и списков. Также следует учесть, что данное изменение коснется только разрабатываемого типа столбцов. Код функции GetDistinctValues() будет выглядеть следующим образом:

void GetDistinctValues()
{
    SPWeb web = SPControl.GetContextWeb(Context);
    try
    {
        Guid listId = new Guid(Request.QueryString["ListId"]);
        Guid viewId = new Guid(Request.QueryString["ViewId"]);
        SPList list = web.Lists.GetList(listId, true);
        SPView view = list.Views[viewId];
        SPQuery query = new SPQuery();
        query.ViewXml =
            view.PropertiesXml.Substring(0, view.PropertiesXml.Length - 2) +
            "><Query>" + view.Query + "</Query>" +
            "<ViewFields><FieldRef Name='" + SPHttpUtility.HtmlEncode(Request.QueryString["FieldInternalName"]) +
            "'/></ViewFields>" + "<RowLimit>1</RowLimit>" +
            "<ViewHeader>" +
            "<Fields><Field/></Fields>" +
            "</ViewHeader>" +
            "<ViewBody/><ViewFooter/><ViewEmpty><Fields><Field/></Fields></ViewEmpty></View>";
        /// замена текстов гиперссылок филтьруемых значений
        SPField fldThis = null;
        try
        {
            fldThis = list.Fields.GetFieldByInternalName(Request.QueryString["FieldInternalName"]);
        }
        catch { }
        if (fldThis != null)
        {
            if (fldThis.GetType().ToString() == "[Тип столбца]")
            {
                string ddlFilter = list.RenderAsHtml(query);
                ddlFilter = UpdateMetadataFilter(web.Site.Url, ddlFilter);
                Response.Write(ddlFilter);
                return;
            }
        }
        ///
        Response.Write(list.RenderAsHtml(query));
    }
    catch { }
    Response.AddHeader("Cache-Control", "no-cache");
}

Код метода UpdateExtLookupFilter, который как раз и заменяет тексты гиперссылок фильтруемых значений с вида [URL узла][Разделитель][ID списка] на соответствующие названия узлов и списков, приведен ниже: 
string UpdateMetadataFilter(string siteUrl, string webUrl, string sValue)
{
    string sOptionValue, sHttp, sTitle;
    // отсортируем заодно фильтруемые значения в порядка возрастания
    SortedList<stringstring> sl = new SortedList<stringstring>();
    string sOptions = "";
    Regex RegEx = new Regex(@"<OPTION Value=\""" + "http.*?</OPTION>"RegexOptions.Compiled);
    MatchCollection mColl = RegEx.Matches(sValue);
    for (int i = 0; i < mColl.Count; i++)
    {
        sOptionValue = mColl[i].Value;
        Regex RegExHttpValue = new Regex(@"http.*?\"""RegexOptions.Compiled);
        sHttp = RegExHttpValue.Matches(sOptionValue)[0].Value;
        sHttp = sHttp.Substring(0, sHttp.Length - 1).Trim();
        sTitle = GetMetadataValue(siteUrl, sHttp);
        if (String.IsNullOrEmpty(sTitle)) sTitle = "null";
        Regex RegExOptionText = new Regex(@">.*<"RegexOptions.Compiled);
        sOptionValue = sOptionValue.Replace(RegExOptionText.Matches(sOptionValue)[0].Value, ">" + sTitle + "<");
        sl.Add(sTitle, sOptionValue);
        sOptions += sOptionValue;
        sValue = sValue.Replace(mColl[i].Value, sOptionValue);
    }
    string sNeedValue = "";
    foreach (KeyValuePair<stringstring> key in sl)
        sNeedValue += key.Value;
    sValue = sValue.Replace(sOptions, sNeedValue);
    return sValue;
}

Функция GetMetadataValue получения названия веб-узла по его URL и названия списка по его ID, применяемая в методе UpdateExtLookupFilter выглядит так: 
string GetMetadataValue(string siteUrl, string value)
{
    string res = "";
    char valuesDelimeter = ';';
    if (!String.IsNullOrEmpty(value))
    {
        string[] values = value.Split(valuesDelimeter);
        string webUrl = values[0];
        string listID = values[1];
        using (SPSite site = new SPSite(siteUrl))
        {
            using (SPWeb web = site.OpenWeb((site.ServerRelativeUrl + webUrl.Substring(site.Url.Length)).Replace("//""/")))
            {
                SPList list = null;
                // проверка существования списка по его GUID
                if (IsGUID(listID))
                {
                    try
                    {
                        list = web.Lists.GetList(new Guid(listID), false);
                    }
                    catch { }
                }
                if (list != null)
                    res = web.Title + valuesDelimeter + list.Title;
            }
        }
    }
    return res;
}

Здесь обязательно следует учитывать корректный подход к методу Dispose() классов SPSite и SPWeb - Рекомендации: использование высвобождаемых объектов служб Windows SharePoint Services . Хорошим контроллером в этом случае является утилита SPDisposeCheck - SharePoint Dispose Checker Tool.

С функцией проверки значения строки по шаблону GUID особых проблем нет:
bool IsGUID(string expression)
{
    if (expression != null)
    {
        Regex guidRegEx = new Regex(@"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$"RegexOptions.Compiled);
        return guidRegEx.IsMatch(expression);
    }
    return false;
}

Теперь выпадающий список фильтрации имеет более привлекательный для пользования вид.

SharePoint сustom field type filter.aspx

Как отобразить данный тип столбцов на страницах NewForm.aspx и EditForm.aspx – дело вкуса. Множество примеров можно найти на просторах Интернета, поэтому в данной статье этот момент не рассматривается. Стоит только отметить, что в обработках событий тех или иных элементов управления, которые будут входить в шаблоны отображения NewPattern и EditPattern, нужно обязательно учитывать корректный подход к методу Dispose() классов SPSite и SPWeb.

Данную задачу можно расширить до способа хранения других метаданных SharePoint в его же списках и корректного их отображения во всех шаблонах списка. К примеру, на момент написания статьи сюда входили данные, отображенные в области редактирования параметров столбца данного типа ниже:



English version - SharePoint Metadata field. Part 2: Custom Filter.aspx page.

Комментариев нет:

Отправить комментарий