Hi Everybody,
Regarding my last post I want to elaborate a little more on the process of creating a custom control by inheriting from an existing Sitefinity public one.
First, open Visual Studio and create a blank solution [called "SitefintiySL" for example]. Add your web site ["TIUSite" in my case] and a new class library project ["SitefinityExtender"]. In the class library add references to the
System.configuration,
System.Web as well as to the
Telerik.Framework,
Telerik.Cms.Engine and
Telerik.News assemblies. In the web site add reference to the class library.
In this particular case we will be extending the SingleNews control by adding functionality for navigating to the previous and next items having in mind that the items are sorted by publication date.
So we need two more HyperLink controls to point to the next and previous news items. So we should either change the current SingleNews.ascx template [~/Sitefinity/ControlTemplates/News/SIngleNews.ascx] or create a new one and tell the control to use it. If you decided to add new template, you should override the TemplateUrl property of the control to return different default value or to set it for every instance either way. My way is to add the buttons to the current template so no particular overrides are needed. Here is how I changed the template:
| <div class="reverseMargins"> |
| <h2 class="singlenews"><asp:Literal ID="Title" runat="server" /></h2> |
| <p class="newsdate"><asp:Literal ID="Publication_Date" runat="server" /></p> |
| <p class="author"><em>by: <asp:Literal ID="Author" runat="server" /></em></p> |
| |
| <asp:Literal ID="Content" runat="server" /> |
| <p> |
| <asp:HyperLink ID="backToList" runat="server" CssClass="astrong" Visible="false"> |
| <asp:Literal runat="server" Text="<%$Resources:BackToNews %>"></asp:Literal> |
| </asp:HyperLink> |
| <asp:HyperLink ID="prevLink" runat="server" Text="<< Prev"> |
| </asp:HyperLink> |
| <asp:HyperLink ID="nextLink" runat="server" Text="Next >>"> |
| </asp:HyperLink> |
| </p> |
| </div> |
| |
Back to the class library, rename the automatically added Class1.cs file to something meaningful ["CustomSingleNews.cs"] and create the control definition and implementation (later) there:
| using System; |
| using System.Collections.Generic; |
| using System.Text; |
| using Telerik.News.WebControls; |
| |
| namespace SitefinityExtender |
| { |
| public class CustomSingleNews : SingleNews |
| { |
| |
| } |
| } |
We need NewsManager, current article instances and a collection for all the news items. So we declare them as private fields. We also need to create Article property which would assure that the current article field is instantiated only once. The NewsManager could be instantiated at the beginning of the CreateChildControls method as it would be needed at a later stage. Here is what we've got so far for our control:
| public class CustomSingleNews : SingleNews |
| { |
| private IContent Article |
| { |
| get |
| { |
| if (this.article == null) |
| this.article = this.newsManger.Content.GetContent(base.ArticleID); |
| return this.article; |
| } |
| } |
| protected override void CreateChildControls() |
| { |
| base.CreateChildControls(); |
| if (this.newsManger == null) |
| this.newsManger = new NewsManager(base.ProviderName); |
| } |
| |
| private NewsManager newsManger; |
| private IContent article; |
| private IList items; |
| } |
Note that in the base CreateChildControls method is called as well so that it would load the template. Now we need to do the following:
- Determine the previous and next items according to the current one;
- Find the HyperLink controls from the template and assign appropriate NavigateUrl value to them.
For the previous and next items we would need to get all news items collection, find the current article in it and according to it get the other ones. Achieving this can be done by creating three additional methods:
GetNextItem, GetPrevItem and FillItems. For the FillItems implementation we create an array of two IMetaSearchInfo items. We set the first one to get all items with publication date - current date range and the other one to get items with current date - expiration date range. By passing this to the ContentManager GetContent method we would retrieve all active items. The IMetaSearchInfo items could be changed to something different, for example all items in the publication date - current date range. This method could be called in the CreateChildControls method and the return value assigned to the items field.
Assigning appropriate values for the NavigateUrl of the newly HyperLink controls should be done in the CreateChildControls method after we call the base method so that the template is already loaded. We should seek for these controls in the second level of the controls hierarchy as the first one represents a container control and the second one the template controls. The NavigateUrl property is filled according to the current SiteMap node Url and the Url format settings from the NewsManager.SettingsElement. The UrlRewriteService would do the work.
Our custom control would have code similar to this:
| using System; |
| using System.Collections.Generic; |
| using System.Text; |
| using Telerik.News.WebControls; |
| using Telerik.News; |
| using Telerik.Cms.Engine; |
| using System.Collections; |
| using System.Web.UI.WebControls; |
| using System.Web; |
| using System.ComponentModel; |
| |
| namespace SitefinityExtender |
| { |
| public class CustomSingleNews : SingleNews |
| { |
| private IContent Article |
| { |
| get |
| { |
| if (this.article == null) |
| this.article = this.newsManger.Content.GetContent(base.ArticleID); |
| return this.article; |
| } |
| } |
| |
| protected override void CreateChildControls() |
| { |
| base.CreateChildControls(); |
| |
| if (this.newsManger == null) |
| this.newsManger = new NewsManager(base.ProviderName); |
| |
| if (SiteMap.CurrentNode != null) |
| { |
| IContent cnt = null; |
| |
| string baseUrl = SiteMap.CurrentNode.Url; |
| int idx = baseUrl.LastIndexOf('.'); |
| if (idx > -1) |
| baseUrl = baseUrl.Substring(0, idx); |
| if (!baseUrl.EndsWith("/")) |
| baseUrl += "/"; |
| |
| if (this.items == null) |
| this.FillItems(); |
| |
| HyperLink prevLink = (HyperLink)this.Controls[0].Controls[0].FindControl("prevLink"); |
| if (prevLink != null) |
| { |
| cnt = this.GetPrevItem(); |
| if (cnt != null) |
| { |
| prevLink.NavigateUrl = String.Concat(baseUrl, |
| UrlRewriterService.FormatURL(cnt, this.newsManger.SettingsElement)); |
| } |
| else |
| prevLink.Visible = false; |
| } |
| |
| HyperLink nextLink = (HyperLink)this.Controls[0].Controls[0].FindControl("nextLink"); |
| if (nextLink != null) |
| { |
| cnt = this.GetNextItem(); |
| if (cnt != null) |
| { |
| nextLink.NavigateUrl = String.Concat(baseUrl, |
| UrlRewriterService.FormatURL(cnt, this.newsManger.SettingsElement)); |
| } |
| else |
| nextLink.Visible = false; |
| } |
| } |
| } |
| |
| private IContent GetNextItem() |
| { |
| int articleIndex = this.items.IndexOf(this.Article); |
| |
| if (this.items == null || articleIndex == this.items.Count - 1) |
| return null; |
| |
| IContent cnt = (IContent)this.items[articleIndex + 1]; |
| return cnt; |
| } |
| |
| private IContent GetPrevItem() |
| { |
| int articleIndex = this.items.IndexOf(this.Article); |
| |
| if (this.items == null || articleIndex == 0) |
| return null; |
| |
| IContent cnt = (IContent)this.items[articleIndex - 1]; |
| return cnt; |
| } |
| |
| private void FillItems() |
| { |
| this.items = null; |
| IMetaSearchInfo[] filter = new IMetaSearchInfo[2]; |
| |
| filter[0] = new MetaSearchInfo(MetaValueTypes.DateTime, |
| this.newsManger.PublicationDateField, DateTime.Now, SearchCondition.LessOrEqual); |
| filter[1] = new MetaSearchInfo(MetaValueTypes.DateTime, |
| this.newsManger.ExpirationDateField, DateTime.Now, SearchCondition.GreaterThen); |
| |
| this.items = this.newsManger.Content.GetContent(this.newsManger.PublicationDateField, filter); |
| } |
| |
| private NewsManager newsManger; |
| private IContent article; |
| private IList items; |
| } |
| } |
Finally we should add the control in the web.config so we could use it in Sitefinity. Under configuration/telerik/cms/toolboxControls add this code line:
| <add name="Custom Single News" section="News" type="SitefinityExtender.CustomSingleNews, SitefinityExtender"/> |
That's it. When you refresh or load the application everything should work fine.
If anyone has any comments, suggestions or ideas we would be glad to hear them. The code file as well as the changed template you can find in the previous post.
Greetings,
Vassil Daskalov
the Telerik team