Thursday, December 22, 2011

SharePoint Metadata field. Part 2: Custom Filter.aspx page

Part 1 - Web Services in DisplayPattern.

Displaying of field values in RenderPattern named HeaderPattern (displaying filtered values in a field header, to be more exact) is the second important point which is worth attention by SharePoint custom field types development.

In this field type pattern CAML markup is practically the same as HeaderPattern in Lookup field type with the exception of the first string which is given in Part 1. The changes will affect the   /_layouts/Filter.aspx page which is called by clicking on the field header.

If we have a look at the result of a query made after customization which is given in Part 1 we will be disappointed:

Custom Filter.aspx page




In hyperlink texts are different field values which are rendered by previously presented values pattern for fields of this type: [Web URL] [Delimiter] [List ID]. The task will be to display hyperlink texts correctly. The best way to accomplish it will be the simple substitution of hyperlink texts with HTML-markup in GetDistinctValues() function in the code of /_layouts/Filter.aspx page which have been formed for filtering. We have to substitute hyperlink texts with filter function in form of web URLs and List IDs for corresponding web and list titles. We have to take into attention that this substitution will affect only the field types which are being developed. The code of GetDistinctValues() function will look like:
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>";
        // substitution of Hyperlink Texts with filter function
        SPField fldThis = null;
        try
        {
            fldThis = list.Fields.GetFieldByInternalName(Request.QueryString["FieldInternalName"]);
        }
        catch { }
        if (fldThis != null)
        {
            if (fldThis.GetType().ToString() == "[Field type]")
            {
                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");
}

The code of UpdateExtLookupFilter method is given below. It substitutes hyperlinks texts of filtering values in form of [Web URL][Delimiter][List ID] for corresponding web and list titles.

string UpdateMetadataFilter(string siteUrl, string sValue)
{
    string sOptionValue, sHttp, sTitle;
    //sort filtered values in ascending order
    SortedList<string, string> sl = new SortedList<string,string>();
    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<string, string> key in sl)
        sNeedValue += key.Value;
    sValue = sValue.Replace(sOptions, sNeedValue);
    return sValue;
}

In UpdateExtLookupFilter method we use GetMetadataValue function to get site via URL and list title via ID and it looks like:

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;
                // just in case we check if value of list ID is a GUID value
                if (IsGUID(listID))
                {
                    try
                    {
                        list = web.Lists.GetList(new Guid(listID), false);
                    }
                    catch { }
                }
                if (list != null)
                    res = web.Title + valuesDelimeter + list.Title;
            }
        }      
    }
    return res;
}

Here we must take into account the correct approach to Dispose() method for SPSite and SPWeb Class - Best Practices: Using Disposable Windows SharePoint Services Objects.  The good checker in this case is SPDisposeCheck - SharePoint Dispose Checker Tool.

There is no problem with string value check function in GUID pattern:
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;
}
Now the dropdown filter list looks more user friendly:

Custom Filter.aspx page



The way to display this type of fields on NewForm.aspx and EditForm.aspx pages is a matter of taste. You can find many examples in Internet that’s why I don’t pay attention to it in this article. I only mention that we must take into account the correct approach to Dispose() method of SPSite and SPWeb Class by event processing  of those control elements which will be in view patterns  NewPattern and EditPattern.

This decision can also include the way of storing the other SharePont metadata in lists and correct displaying in all list templates. For the moment of writing this article, it included data displayed in parameter editing of the field given below:

SharePoint custom FieldEditor

No comments:

Post a Comment