In a recent Kentico project I was asked for some custom filtering options to the CMSDesk listing page of specific document types, allowing users to filter the documents based on specific fields of the document types.

Listing Mode - List
CMSDesk Content Listing Mode

“Listing page” Property

Now this can be achieved normally by creating a custom listing page constituted of the listing grid with the custom filter, then assigning this page to the specific document type in the “List page” property as shown in the following screenshot.

Listing page
Document type – Editing pages settings – Listing page property

Customize The Default Listing Page

The listing page option is a good one, yet what if I want to use the system default listing page, yet with custom filtering options other than the default “Document name and type” filter options? It was obvious that some customization was required in order to achieve this. Before going into the details here is a sample screenshot after the customization.

Listing Mode Sample Custom Filter
Listing Mode Sample Custom Filter

Assuming you have a “document type” for clients, having fields as in the above screenshot (Name, Type, Practice Area, Billing Type, etc…) and you want the user to be able to filter the list of clients based on one or a combination of those fields instead of just the document name.

Step 1 – Customize the Listing Grid Definition

By default the system listing page code and listing grid definition XML can be found under ~/CMSModules/Content/CMSDesk/View/ folder as Listing.aspx(.cs) and Listing.xml

The XML contains the UniGrid definition and this is where we can customize the listing grid as well as specifying our custom filter. What I did is duplicated this XML naming it ClientsListing.xml and customized the definition in it as follow (Omitting the default sections).

<grid>
  ...
  <columns>
    <column source="##ALL##" visible="false">
      <filter type="custom" path="~/CMSGlobalFiles/DocumentClientsFilter.ascx" source="NodeID" />
    </column>
    <column source="##ALL##" externalsourcename="documentname" sort="DocumentName" caption="$general.documentname$" wrap="false" width="100%" >
    </column>
    ...
  </columns>
</grid>
Basically what I did in that is removed the tags defining filters on all the fields such as the Document Name (<filter type=”text” source=”DocumentName” />) and added the above highlighted section defining a custom filter defined in ~/CMSGlobalFiles/DocumentClientsFilter.ascx and where the source column which will be used in the SQL WHERE clause is the NodeID column.

Step 2 – Customize the Listing Grid Initialization

Now after customizing our UniGrid XML definition, we need to define when this XML must be loaded instead of the default definition. For that we have to modify the OnInit event of the Listing.aspx(.cs) page.

By default in the OnInit event of the listing page, we are setting the GridName of the listing UniGrid to the Default Listing.xml definition, what we need to do is to set some conditions to when to load the ClientsListing.xml defined in Step 1 instead of the default one, and this condition can be based on the NodeID for example. Following is an example code:
    protected override void OnInit(EventArgs e){
        ...
        base.OnInit(e);
        string filterXMLFile = "Listing.xml";
        string currNodeID = QueryHelper.GetString("nodeid", "0"); //Get The Node ID of the Current Selected Menu Item in the Tree.
        switch (currNodeID){//Check the NodeID Value and set the appropriate XML Path
            case "200": //(Products Page Node ID)
                filterXMLFile = @"CustomListing\ProductsListing.xml";
                break;
            case "201": //(Clients Page Node ID)
                filterXMLFile = @"CustomListing\ClientsListing.xml";
                break;
        }
        docList.Grid.GridName = filterXMLFile; //Set the UniGrid XML definition
        ...
     }

Step 3 – Create the Custom Filter Control

Remember we pointed in our custom UniGrid XML definition to a custom filtering control ~/CMSGlobalFiles/DocumentClientsFilter.ascx which we need to create, and to do that we can follow the footsteps of an already defined custom control used for Culture Filtering and can be found here: ~/CMSModules/Content/Controls/Filters/DocumentCultureFilter.ascx

I will not go into details of how to define the fields and the layout of the controls, I will leave that to you. What we need to do is to Inherit the control from “CMSAbstractBaseFilterControl“, following that we have to override the WhereCondition Property as follow:

    public override string WhereCondition{
        get{
            base.WhereCondition = GenerateWhereCondition();
            return base.WhereCondition;
        }
        set{
            base.WhereCondition = value;
        }
    }

Now as you can notice we have a method which will generate the Where Clause “GenerateWhereCondition()” and this is where we will build our custom where condition and append it to the Base Control WhereCondition Property to be used when filtering the documents list, and remember here that the source column of the custom filter is set to the NodeID column, hence the where condition has to be related to this column.

    private string GenerateWhereCondition(){
        string where = "";
        string clientName = txtName.Text;
        string financeNumber = txtFinanceNumber.Text;
        if (!string.IsNullOrEmpty(financeNumber)){
            if (!string.IsNullOrEmpty(where))
                where += " AND ";
            where += "FinanceNumber LIKE '%" + financeNumber + "%'";
        }
        if (!string.IsNullOrEmpty(clientName)){
            if (!string.IsNullOrEmpty(where))
                where += " AND ";
            where += "Name LIKE '%" + clientName + "%'";
        }

        if (!string.IsNullOrEmpty(where))
            where = " AND " + where;        where = "NodeID IN (select NodeID from View_MySite_Client_Joined WHERE ClassName = 'CMS.Client' " + where + ")";
        return where;
    }

In the above example you can see that I am building my custom where condition based on the custom filter fields defined, and targeting My Site Clients View selecting only the NodeID from that view and passing that as argument to the main documents Where Clause using the SQL IN Operator, by that only the documents matching the filter characteristics will be loaded in the documents list.

Finally, don’t forget to setup your controls and load custom data into them if necessary. in the Page_Load event, and in order to code the reset functionality you have to override the ResetFilter() method.

Step 4 – Customize The DocumentList Control

To this stage if we load in listing mode for one of our targeted pages (ex: Clients or Products), the filter control will not show up. After further investigation into the DocumentList Control (~\CMSModules\Content\Controls\DocumentList.ascx.cs), it appears that OnPreRender, there is a condition checking if the Filter Fields are targeting the “NodeID” column, then set its visibility to false, which is not rendering our custom control. For that I’ve added some condition here as well to check if the NodeID is the one of our targeted Pages then set the visibility to True instead of False.

    protected override void OnPreRender(EventArgs e) {
        ...
        // Hide column with languages if only one culture is assigned to the site
        if (DataHelper.DataSourceIsEmpty(SiteCultures) || (SiteCultures.Tables[0].Rows.Count <= 1)){
           ...
            // Hide language filter 
            if (gridDocuments.FilterFields.ContainsKey("NodeID")){
                bool showNodeID = false;
                if ((CMSContext.EditedDocument.NodeID == 200) || (CMSContext.EditedDocument.NodeID == 201))
                     showNodeID = true;
                gridDocuments.FilterFields["NodeID"].FilterRow.Visible = showNodeID;
            }
        }
        ...
    }

Other Options

The steps described above is one of the possible options to achieve such customization task. As Johnathan from Kentico stated in his reply to my forum thread, setting the FilterDirectoryPath property in DocumentListing.aspx.cs to the desired custom control based on the document type will lend a similar result.
Advertisements