Custom WebParts Creation – Object Model
In this section, let us focus on the Visual Studio WebParts. As of now, we were gone through the step by step procedures to establish the Cross Site Publishing. Anyhow, we want to showcase these Pages written on the Authoring to the Publishing Site using WebParts. The webPart display templates, I am not focusing. That will deviate from our objective. Hence, now the focus would be two webparts. One is the Landing Page WebPart which is nothing but a Category WebPart and another one is Content Page WebPart which is nothing but an Item WebPart.
Basically on the Landing Page, we need to show what are the pages related to the Current Navigation Term with an extra filter based on the Content Type. As we have one Content Type (DemoPage), I want to filter based on that as well.
Now, the steps are as below.
1. Open the Visual Studio as “Run as Administrator”.
2. Add 2 WebParts. Both of them would be a Visual WebPart.
3. Let me name them as DemoWebPart and ContentWebPart.
4. On the DemoWebPart – That is going to be our Landing (Category WebPart), Based on the Current Term, the pages needs to be retrieved and displayed. In addition to that, I am adding a filter for the Content Type as well.
5. This can be accomplished by the OOB webpart itself. But, if we require further filtering, OOB webpart customization cannot help us.
6. For that, I am inheriting the ContentBySearchWebPart class and create our own Content Search Webpart.
7. On the ASCX file, I am not doing any customization. As this is going to be Content Search WebPart, the customizations needs to be done on the Display Templates. I am not focusing that. As of now, we will be using the default Display Templates.
8. Hence, the ASCX file will looks like
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="DemoWebPart.ascx.cs" Inherits="CSP.Demo.WebParts.DemoWebPart.DemoWebPart" %>
9. On the ASCX.CS file, let us frame our Query, Result Source and other properties as well.
using Microsoft.Office.Server.Search.WebControls;
using System;
using System.ComponentModel;
using System.Web.UI.WebControls.WebParts;
namespace CSP.Demo.WebParts.DemoWebPart
{
[ToolboxItemAttribute(false)]
public partial class DemoWebPart : ContentBySearchWebPart
{
// Uncomment the following SecurityPermission attribute only when doing Performance Profiling on a farm solution
// using the Instrumentation method, and then remove the SecurityPermission attribute when the code is ready
// for production. Because the SecurityPermission attribute bypasses the security check for callers of
// your constructor, it's not recommended for production purposes.
// [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Assert, UnmanagedCode = true)]
//public DemoWebPart()
//{
//}
//protected override void OnInit(EventArgs e)
//{
// base.OnInit(e);
// InitializeControl();
//}
protected override void OnLoad(EventArgs e)
{
this.ChromeType = PartChromeType.None;
if (this.AppManager != null)
{
if (this.AppManager.QueryGroups.ContainsKey(this.QueryGroupName) && this.AppManager.QueryGroups[this.QueryGroupName].DataProvider != null)
{
this.AppManager.QueryGroups[this.QueryGroupName].DataProvider.BeforeSerializeToClient += new
BeforeSerializeToClientEventHandler(AddNewsProperties);
}
}
base.OnLoad(e);
}
private void AddNewsProperties(object sender, BeforeSerializeToClientEventArgs e)
{
string queryPublishing = string.Empty;
string queryPreview = string.Empty;
DataProviderScriptWebPart dp_news = sender as DataProviderScriptWebPart;
//dp_news.SourceID = "5ae5c0e5-b90a-45d8-9fdf-c7915e9f3ccc";
this.ResultsPerPage = 5;
queryPublishing = " " + "((ContentType:'" + "DemoPage" + "'))" + " AND " + "(owstaxIdDemoTag:{Term.IdWithChildren})";
dp_news.QueryTemplate = queryPublishing;
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
10. Now, let us deploy the Solution. The Feature creation and making the feature activate and all, all of us would be familiar with.
11. Now, on the Screen, let me add the WebPart to the LandingPage.aspx.
12. If we closely look into the URL, http://c4968397007:1000/sites/Publishing/demoterm, the WebPart will retrieve all the pages which has the term demoterm and its child terms.
13. On the Authoring Page, I have 2 Pages. TestPage 1 and TestPage2.
14. Both the pages are having the terms DemoTerm1, DemoTerm2 respectively. Hence, when we land on the DemoTerm, both the pages are getting retrieved.
15. When I navigate to DemoTerm1, http://c4968397007:1000/sites/Publishing/demoterm/demoterm1 then the screen would be like
16. When I navigate to DemoTerm2, http://c4968397007:1000/sites/Publishing/demoterm/demoterm2 the screen looks like
17. Now, coming to the ContentPage Webpart. This webpart will be placed on the ContentPage.aspx. This webpart is the one which is going to render our content from the Authoring Site on the Publishing Site.
18. This we will be creating using the Visual WebPart itself. This, does not need to be a Content Search WEbpart. This we will retrieve based on the ID.
19. On the ASCX file, I am going to place a Literal Control
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ContentWebPart.ascx.cs" Inherits="CSP.Demo.WebParts.ContentWebPart.ContentWebPart" %>
<script src="/_layouts/15/sp.runtime.js" type="text/javascript"></script>
<script src="/_layouts/15/sp.js" type="text/javascript"></script>
<script src="/_layouts/15/sp.search.js" type="text/javascript"></script>
<div class="content_contentpage fix-url ms-rtestate-field" id="divContent" runat="server">
</div>
<div id="Hidden Debugger" style="display: none">
<asp:Literal runat="server" ID="Debugger" />
</div>
20. On the ASCX.CS file, the code will looks like below.
using System;
using System.ComponentModel;
using System.Web.UI.WebControls.WebParts;
using System.Web;
using System.Web.UI;
using System.Collections.Generic;
using Microsoft.Office.Server;
using Microsoft.Office.Server.Search.Query;
using Microsoft.Office.Server.Search.Administration;
using System.Data;
using Microsoft.SharePoint;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace CSP.Demo.WebParts.ContentWebPart
{
[ToolboxItemAttribute(false)]
public partial class ContentWebPart : WebPart
{
// Uncomment the following SecurityPermission attribute only when doing Performance Profiling on a farm solution
// using the Instrumentation method, and then remove the SecurityPermission attribute when the code is ready
// for production. Because the SecurityPermission attribute bypasses the security check for callers of
// your constructor, it's not recommended for production purposes.
// [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Assert, UnmanagedCode = true)]
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
InitializeControl();
this.ChromeType = PartChromeType.None;
}
private void WriteToDebugger(string message)
{
this.Debugger.Text += message + "<br/><br/>";
}
protected void Page_Load(object sender, EventArgs e)
{
LoadContent();
}
public void LoadContent()
{
string strContentVal = string.Empty;
try
{
string title = string.Empty;
string itemId = string.Empty;
string strSearchQuery = string.Empty;
string strUrlQuery = string.Empty;
string urlSuffix = string.Empty;
string MP_PUBLISHINGCONTENT = "PublishingPageContentOWSHTML";
string MP_PAGETITLE = "Title";
string MP_LISTITEMID = "ListItemID";
DateTime Todaydate = DateTime.Now;
Dictionary<string, Query> Dqueries = new Dictionary<string, Query>();
Dictionary<string, ResultTableCollection> Dresult;
string strPageUrl = HttpContext.Current.Request.Url.PathAndQuery;
if (strPageUrl != null)
strUrlQuery = strPageUrl.Split('?')[1];
if (strUrlQuery != null)
{
string strTemp = strUrlQuery.Split('&')[3];
if (strTemp != null)
urlSuffix = strTemp.Split('=')[1];
if (urlSuffix != null)
{
title = urlSuffix.Split('/')[2];
itemId = urlSuffix.Split('/')[1];
}
}
SearchServiceApplicationProxy searchProxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.GetProxy(SPServiceContext.GetContext(SPContext.Current.Site));
KeywordQuery keywordQuery = new KeywordQuery(searchProxy);
strSearchQuery = "(SecondaryFileExtension='aspx') AND " + MP_PAGETITLE + ":" + title + " AND " + MP_LISTITEMID + ":" + itemId;
this.WriteToDebugger("Search query:" + strSearchQuery);
keywordQuery.SelectProperties.Add(MP_PUBLISHINGCONTENT);
keywordQuery.SelectProperties.Add(MP_PAGETITLE);
keywordQuery.QueryText = strSearchQuery;
Dqueries.Add("First", keywordQuery);
SearchExecutor ss = new SearchExecutor();
Dresult = ss.ExecuteQueries(Dqueries, true);
foreach (KeyValuePair<string, ResultTableCollection> result in Dresult)
{
if (result.Value.Count > 0 && result.Value.QueryErrors == null)
{
var resultTable = result.Value.Filter("TableType", KnownTableTypes.RelevantResults);
if (resultTable != null && resultTable.Count() == 1)
{
DataTable tableResults = new DataTable();
tableResults.Load(resultTable.First(), LoadOption.OverwriteChanges);
string[] selectedColumns = new[] { MP_PAGETITLE, MP_PUBLISHINGCONTENT };
if (tableResults.Rows.Count > 0)
{
DataTable dt = new DataView(tableResults).ToTable(false, selectedColumns);
foreach (DataRow dtrow in dt.Rows)
{
strContentVal = Convert.ToString(dtrow[MP_PUBLISHINGCONTENT]);
string pageTitle = Convert.ToString(dtrow[MP_PAGETITLE]);
string[] documentURLs = new string[] { };
strContentVal = strContentVal.Replace("<br>", "<br/>").Replace("<br clear="all">", "<br clear="all"/>");
divContent.Controls.Add(new LiteralControl("<h2>" + pageTitle + "</h2>"));
divContent.Controls.Add(new LiteralControl("<div>" + strContentVal + "</div>"));
this.WriteToDebugger("Successfully processed the content");
}
}
}
}
}
}
catch (Exception ex)
{
divContent.Controls.Add(new LiteralControl("<div>" + strContentVal + "</div>"));
this.WriteToDebugger("Exception in Load Page content : Message :: " + ex.Message + " ::StackTrace:: " + ex.StackTrace);
}
}
}
}
21. On the above piece of code, I am trying to retrieve the PublishingPage Content from the Authoring and render them on the Literal control. A very straight forward approach.
22. The only thing here is, from the URL we need to get the Title and the ID. Usually the URL would be a friendly one. As we set them while establishing a connection. Hence, the URL would be something like,
http://c4968397007:1000/sites/Publishing/demoterm/demoterm2/DemoTerm2/4/TestPage2
23. From that, using the below line, we are extracting the required parameters.
string strPageUrl = HttpContext.Current.Request.Url.PathAndQuery;
if (strPageUrl != null)
strUrlQuery = strPageUrl.Split('?')[1];
if (strUrlQuery != null)
{
string strTemp = strUrlQuery.Split('&')[3];
if (strTemp != null)
urlSuffix = strTemp.Split('=')[1];
if (urlSuffix != null)
{
title = urlSuffix.Split('/')[2];
itemId = urlSuffix.Split('/')[1];
}
}
24. After adding the webpart to the ContentPage, the page will be looking like
With this, we are done with the End to End implementation of Cross Site Publishing.
Thanks for the patience to read all the 9 Parts.
Download the Source HERE
Happy Coding.
Sathish Nadarajan.
Leave a comment