+1-888-365-2779
Try Now
More in this section
Categories
Bloggers
Blogs RSS feed

Products module: Reusing Views

by Ivan Osmak

[This post is part of the developer's manual preview published on this blog. You can find temporary TOC here.]
 

With the introduction of the new backend architecture in Sitefinity 3.6, building modules based on Generic Content module became several times faster and simpler. While it was always possible to build Generic Content based modules, the problem often occurred when the requirements specified that certain parts of module need to differ from the base implementation. Due to somewhat clunky architecture, even the smallest modifications imposed on developers to rewrite and implement rather large parts of the base module. Often times, the required modifications were so extensive that building a clean pluggable module from the scratch made more sense.

 

All this has changed in Sitefinity 3.6.

 

Views: Encapsulation of presentation and logic into simple building units


So far we have mentioned Views and the new architecture many times, giving more theoretic reasoning behind it. The sample products module, based on Generic Content module, will give a real-world justification of the new architecture.

 

The key idea of the improvements in the new architecture is to allow certain amount of functionality to be easily reused, while at the same time making modifications to the base implementation equally simple. While, reuse was not a problem even in the prior versions, things did get more complex when functionality or presentation needed to be changed.

 

With the new concept of the Views, developers are now able to cherry pick the Views (single contained unit of functionality) which they wish to reuse completely, reuse partially or implement from scratch, while leaving the rest of module intact.

 

Let us examine the architectural overview of the Products module, with special focus on the Views and their reuse.

 

As the legend suggests, we have been able to modify different Views to different degrees depending on our requirements.

 

First, we have ProductsControlPanel and ProductsView Views which have been completely implemented from the scratch, since we had no use of inheriting from base implementation.

 

Then, we have different products Views in yellow color which we have modified mostly to add new meta fields that Products module will use and change the labels on the user interface (e.g. where in Generic Content it would say: “Save this item”, we changed to “Save this product”).

 

Next are the three Views in the gray color on which we only changed the assembly info for embedded templates and localization, since we wanted to use the templates and localization resources from Telerik.Samples.Products assembly as opposed to Telerik.Cms.Engine assembly (where Generic Content module is located). The reason we have done this is to change several labels.

 

Finally, we have the Views for Comments administration in bluish color. We have not modified anything on these Views and we are using them directly from the Generic Content module. Since we are adding Views by specifying its type (or alternatively virtual path if we are working with user control based views), there is nothing preventing us to add a view which resides in different assembly - provided that we have referenced the view and that its entire implementation matches our requirements.

 

Implementing View from a scratch


When one wishes to implement a View from scratch for the Generic Content based module, the procedure is exactly the same as if it would be for implementing any other View, as it was described in this article.

 

One of such Views in Products module is the ProductsView View. Since ProductsView is only a container View (does not have its own graphical user interface, but rather displays the first view in the collection of its child views), the code for it is rather simple:
using Telerik.Cms.Engine; 
using Telerik.Cms.Web.UI; 
using Telerik.Cms.Engine.WebControls.Admin; 
using Telerik.Samples.Products.WebControls.Admin.Products; 
using Telerik.Samples.Products.Resources; 
 
namespace Telerik.Samples.Products.WebControls.Admin 
    /// <summary> 
    /// Container view for all product related views 
    /// </summary> 
    public class ProductsView : ViewModeControl<ProductsControlPanel>, IGenericContentHost 
    { 
        /// <summary> 
        /// Content Manager used by the control 
        /// </summary> 
        public ContentManager Manager 
        { 
            get 
            { 
                return this.Host.Manager; 
            } 
        } 
 
        /// <summary> 
        /// Gets the products manager. 
        /// </summary> 
        /// <value>The products manager.</value> 
        public ProductsManager ProductsManager 
        { 
            get 
            { 
                return this.Host.ProductsManager; 
            } 
        } 
 
        /// <summary> 
        /// Loads configured views. 
        /// </summary> 
        protected override void CreateViews() 
        { 
            AddView<ProductsItemListView>("ProductsItemListView""ProductsItemListView_Title""ProductsItemListView_Description"null, Messages.ResourceManager); 
            AddView<ProductEditView>("ProductEditView""ProductEditView_Title""ProductEditView_Description"null, Messages.ResourceManager); 
            AddView<ProductHistoryView>("ProductHistoryView""ProductHistoryView_Title""ProductHistoryView_Description"null, Messages.ResourceManager); 
            AddView<ProductNewView>("ProductNewView""ProductNewView_Title""ProductNewView_Description"null, Messages.ResourceManager); 
            AddView<ProductPreviewView>("ProductPreviewView""ProductPreviewView_Title""ProductPreviewView_Description"null, Messages.ResourceManager); 
        } 
    } 
 
An interesting point here to note is that when basing modules on Generic Content module, every View that will act as a host for other Views should implement IGenericContentHost interface. The implementation basically requires an access to the instance of the ContentManager instantiated with proper provider name under the Manager property.

 

Moderately modifying the View


When talking about moderately modified Views, we understand that user interface (template), labels (localization) and certain business logic have been modified, while most of the functionality has been reused. Any of the product views such as ProductNewView or ProductHistoryView are good examples of this approach. Let us take a look at the ProductHistoryView first. ProductHistoryView inherits from ContentItemHistoryView and at the same time sets its host to the ProductsView, which is a host View in this context:
public class ProductHistoryView : ContentItemHistory<ProductsView> 
The use of Generics in this implementation is exactly the reason why we are able to reuse Views in different places of module hierarchy as well as in entirely different modules.

 

After we have declared the View, we will define a new embedded template for the View in which we will be able to change our user interface as needed. To do that we are overriding LayoutTemplateName and LayoutTemplatePath properties like follows:
/// <summary> 
        /// Gets or sets the path to a custom layout template for the control. 
        /// </summary> 
        /// <value></value> 
        [WebSysTemplate(ProductHistoryView.layoutTemplateName, "ProductHistoryView_Template_Desc""/Products"false"2009-04-10")] 
        public override string LayoutTemplatePath 
        { 
            get 
            { 
                return base.LayoutTemplatePath; 
            } 
            set 
            { 
                base.LayoutTemplatePath = value; 
            } 
        } 
 
        /// <summary> 
        /// Gets the name of the embedded layout template. 
        /// </summary> 
        /// <value></value> 
        protected override string LayoutTemplateName 
        { 
            get 
            { 
                return ProductHistoryView.layoutTemplateName; 
            } 
        } 
 
        private const string layoutTemplateName = 
            "Telerik.Samples.Products.Resources.ControlTemplates.Backend.ProductHistoryView.ascx"
 
The only reason why we are overriding LayoutTemplatePath property is to declare the WebSysTemplate attribute on it which will be used by the template management tool to be introduced in Sitefinity 4.0.

 

On the other hand, by overriding LayoutTemplateName, we are specifying the name of the embedded template which we wish to use with this View.

 

Now, that user interface has been modified, we are left with modifications of some functionality. Since Generic Content module in Sitefinity 3.6 has been designed for extensibility, certain virtual methods have been exposed for the purpose of simple View reuse. Following code demonstrates some of them exposed on ContentItemHistory base View:
/// <summary> 
        /// Gets the edit command. 
        /// </summary> 
        /// <param name="itemId">The item id.</param> 
        /// <returns></returns> 
        protected override string GetEditCommand(Guid itemId) 
        { 
            return CreateHostViewCommand<ProductEditView>(itemId.ToString()); 
        } 
 
        /// <summary> 
        /// Gets the list command. 
        /// </summary> 
        /// <returns></returns> 
        protected override string GetListCommand() 
        { 
            return CreateHostViewCommand<ProductsItemListView>(); 
        } 
 
        /// <summary> 
        /// Gets the preview command. 
        /// </summary> 
        /// <param name="itemId">The item id.</param> 
        /// <returns></returns> 
        protected override string GetPreviewCommand(Guid itemId) 
        { 
            return CreateHostViewCommand<ProductPreviewView>(itemId.ToString()); 
        } 
 
Namely, instead of going down to the user interface elements, methods such as GetListCommand or GetPreviewCommand have been abstracted away and provide us with a simple way to modify the behavior of the base View.

 


Barely modified Views


Sometimes we have no need to change the business logic of the View, but simply we wish to provide View with a different template and our own localization messages. In this case, all we need to do is override the AssemblyInfo and LocalizationAssemblyInfo properties of the base View and set them to the desired assembly.

 

The important thing to understand about those two properties is that they will default to the assembly in which View is located if not set differently. Now, let us see how in ProductsCategoriesView (View which belongs to Telerik.Samples.Products assembly) we have managed to use the templates and localization already defined in Telerik.Cms.Engine (since we didn’t need to make any changes):
/// <summary> 
        /// Gets the type from the assembly containing the embedded resources. 
        /// Cannot be null reference. 
        /// </summary> 
        /// <value></value> 
        public override Type AssemblyInfo 
        { 
            get 
            { 
                return typeof(GenericControlPanel); 
            } 
            set 
            { 
                base.AssemblyInfo = value; 
            } 
        } 
 
        /// <summary> 
        /// Gets the type from the assembly containing the embedded localization resource. 
        /// Override if embedded templates are using ASP.NET localization. 
        /// </summary> 
        /// <value></value> 
        public override Type LocalizationAssemblyInfo 
        { 
            get 
            { 
                return typeof(GenericControlPanel); 
            } 
            set 
            { 
                base.LocalizationAssemblyInfo = value; 
            } 
        } 
 
Both properties return Type, so it is enough that we return the type of any class located in the desired assembly.

 


Completely reusing Views


Sometimes we don’t need to change anything on the base implementation of the View. In such cases the simplest thing to do is to completely reuse the View.  A good example of this approach are the comment Views. Namely, CommentsView View located implemented on Generic Content module has three child views:
  • CommentsList
  • CommentsEdit and
  • CommentsPreview
Since we don’t need to change anything on the three child views, we have simply implemented ProductCommentsView  by inheriting CommentsView container view. The base implementation of CommentsView will add the three already mentioned child views and thus provide us with full implementation of comments administration with no coding at all. This is the sample of how whole parts of view hierarchy, and not only single views, can be ported to different modules explained in this article.

 


Conclusion


Careful reader could come up with even more efficient way of reusing the base implementations on Products module, however, since this is a sample module different approaches have been used to demonstrate the versitality of the new backend architecture introduced in Sitefinity 3.6.

 

The sample products module discussed in this article can be downloaded from here.
 

1 comment

Leave a comment
  1. Jeremy Schaab Nov 24, 2009
    Do you know if anyone has created a pluggable module using REST DataServices in .NET 3.5 SP1?  Is this possible?

    Leave a comment