Content By Query WebPart and the "a" Mystery

It’s about One of my favourites : Content By Query Webpart

Quite some time back I faced an interesting issue where a ContentByQuery WebPart (CQWP) will show “a”s appended to the output – the output being “PublishingContent” of Pages based on a particular “Page Layout Content Type”. It later turned out due to “Reusable Content” being used in Pages Content.I think the subsequent findings and workaround may make an interesting read about “Reusable Content” , “Publishing Pages” and CQWP customization, and hence this post.

Let’s call it Content By Query WebPart and the "a" Mystery.

And it starts with the basic CQWP customization steps (to include the extra Fields) – the two step process of :

  • Modifying the .webpart to include “CommonViewFields”
 <property name="CommonViewFields" type="string" >PublishingPageContent,HTML</property> 
  • Creating a CustomItemStyle.xls and a new ItemStyle so as to equip the XSLT to render extra Fields specified via “CommonViewFields”.

Standard procedure dissected in much detail at : https://blogs.msdn.com/ecm/archive/2006/10/25/configuring-and-customizing-the-content-query-web-part.aspx

The Webpart is then imported and added on a Webpart page and made to query “Pages Library” from the site collection. The important thing to note is that the Page Content of some of the Pages contains “Reusable Content” which can be added to the Page Content like this :

clip_image002[1]

Now with the two major parts –

1. Modified CQWP 2. Pages with Reusable Content

in place – we are ready to reproduce the issue. Once one applies the Custom ItemStyle – we note the PublishingPageContent is littered with “a”s all over. Such as the screenshot below :

ISSUE :

Actual content on TestPage2 (in edit mode)

clip_image002[3]

How it appears on the modified CQWP :

clip_image002[5]

CAUSE :

The finding came , when I used a simple CAML query on “Pages Library” :

 <Query>
  <Where>
    <Eq>
      <FieldRef Name='Title' />
      <Value Type='Text'><Page-Leaf-Name></Value>
    </Eq>
  </Where>
</Query>

and it returns all the fields associated with the item : <Page-Leaf-Name> .One of the fields is “ows_PublishingPageContent”.This is the same field that CQWP queries, when it’s webpart file contains :

 <property name="CommonViewFields" type="string" >PublishingPageContent,HTML</property> 

In my case, since I had a “ReusableText” field in my PageContent(apart from other typed text), I got following value from CAML query :

 <div id="__publishingReusableFragmentIdSection">
   < ahref =  "/sites/CBQ-News%20Site/ReusableContent/3_.000">a</a>  </div>
<p>
  <span id="__publishingReusableFragment"></span>
</p>
<p> </p>
<p>The other page Content apart from Reusable text, is stored as such in the database</p>

The highlighted portion is the entry for ReusableContent(ReusableText\ReusableHTML) and the character “a” can be noted. This entry is rendered as such in the CQWP’s resultset.

A ‘Page Content’ field , present on any Page (whether base on OOB or custom Page Layout) in a publishing site, is rendered through “RichHTML” class present in “Microsoft.SharePoint.Publishing.WebControls” and its “RenderFieldForDisplay” method in turn calls “HtmlEditorInternal.ConvertStorageFormatToViewFormat” method

And it can be noted that , it has code which converts the value :

 <a href="/sites/CBQ-News%20Site/ReusableContent/3_.000">a</a>

to actual content present in “ReusableContent” field , based on the hard-coded character “a” present in the “PageContent” value in database; by actually querying against the “Reusable Content” list.

Most importantly, the code inside a OOB CQWP doesn’t have the logic to convert the “ReusableContent” to actual content.

WORKAROUND :

Create a custom Webpart deriving from CQWP : Overriding CQWP sample usage helps eliminate mischievous “a”s.

Sample Visual Studio Project is attached for reference. Here is the improved display of custom CQWP after we taught it to tackle the “a”s :

clip_image002[7]

<SAMPLE> Webpart's OnInit method

 protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            this.ProcessDataDelegate = new ProcessData(modifyData);
            this.CommonViewFields = "PublishingPageContent,HTML;";
        }

  private DataTable modifyData(DataTable dt)
        {
            ////////////////////////////////////////////////
            DataColumn dc1 = null;
            foreach (DataColumn dc in dt.Columns)
            {
                if (dc.ColumnName.Equals("PublishingPageContent"))
                {
                    dc1 = dc;
                    break;
                }
            }
            foreach (DataRow row in dt.Rows)
            {
                try
                {
                    string strPubContent = row[dc1].ToString();
                    string resolvedReusableContent = HtmlEditorInternal.ConvertStorageFormatToViewFormat(strPubContent);
                    row[dc1] = resolvedReusableContent;
                                    }
                catch (Exception e)
                {
                }
            }
            return dt;

        }
 </SAMPLE>

Ok...So,with features like Cross List querying, Caching , Audience targeting etc, CQWP is a great data extraction, reporting and presentation tool – and readily customizable – as seen above.

CustomCBQ.zip