Orchard CMS Extension: Creating Widgets

Original author: The Orchard Team
  • Transfer
This is a continuation of a series of articles on the development of their own sites based on the Orchard CMS content management system. The first articles of this series can be found at the following links:In Orchard, a widget is a slice of the UI that can be reused and can be arbitrarily placed on any page of the site. Examples of widgets may include: tag cloud, search form, list of tweets from Twitter. A widget is a type of content that allows you to reuse your existing UI and code.

This article describes the procedure for writing your own widgets through the creation of the content part, which is then transformed into a widget. This article is based on the original article Writing a content part .

Content creation


Content is a small piece of functionality or user interface that can be added to any type of content in Orchard. Examples of such parts: a route for the ability to access a content item, tags for tagging content items, a menu for the ability to add content items to user menus.

In this part, we will look at the process of creating a new content part from scratch, using the capabilities of the productive scaffolding tool Orchard. Despite the fact that we will consider development through Visual Studio, it is not necessary to have Visual Studio to create content parts, you can use any editor to your taste. In this part of the guide, we will consider creating a content part that will display a map with the ability to configure through latitude and longitude parameters.

Important. Before you start creating a file structure for your module, you need to download and install the Code Generation function in Orchard. You can find more information on this function at this link .

To start creating a new content part with a map, download the Orchard project and open it in Visual Studio.

Type "codegen module Maps / IncludeInSolution: true" at the Orchard command prompt. The “IncludeInSolution” parameter tells Orchard to add a new module project to the Orchard project list.

image

After executing this command, Visual Studio will ask you to reload the solution in the development environment, agree with this. After that, a new Maps project will appear in the solution, which will contain a set of files added by default so that you can create a new piece of content.

Open the Module.txt file in the root of the project, this file contains some information about your module, such as the name, description, version, author and a set of functions provided by this module. This file also contains additional information, such as dependency information. The module created by default is very simple and contains only one function, without additional dependencies. Edit Module.txt as follows:

Name: Maps 
AntiForgery: enabled 
Author: The Orchard Team 
Website: http://orchardproject.net 
Version: 1.0.0 
OrchardVersion: 1.0.0 
Description: Adds a map image to content items, based on longitude and latitude. 
Features: 
    Maps: 
        Description: Adds a map image to content items, based on longitude and latitude. 
        Category: Geolocation


Now, let's start creating our content part. In order to start, we need to create a file that will contain a class with a description of the data of our content part. Typically, these classes are located in the “Models” folder. Right-click on the Models folder and select Add -> Class ..., in the window that appears, name the new Maps.cs file.

image

In Orchard, the content part is represented by the Record class, which contains the data fields stored in the database and the ContentPart class, which uses the Record for storage. Add the MapRecord (ContentPartRecord) and MapPart (ContentPart) classes as shown below:

using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;
namespace Maps.Models {
    public class MapRecord: ContentPartRecord {
        public virtual double Latitude {get; set; }
        public virtual double Longitude {get; set; }
    }
    public class MapPart: ContentPart {
        [Required]
        public double Latitude
        {
            get {return Record.Latitude; }
            set {Record.Latitude = value; }
        }
        [Required]
        public double longitude
        {
            get {return Record.Longitude; }
            set {Record.Longitude = value; }
        }
    }
}


Now build the project using the Build command, in order to make sure there are no errors.

image

The next step is to create data migration for our map module. Why do we need a class for data migration? The reason is that simply declaring the Record and Part classes to store data does not actually affect the database. Data migration is the mechanism that tells Orchard how to update the database schema when turning on the map module on a site. Migration can also update the data scheme from the previous version to a new one, but this issue is beyond the scope of this article and we will not touch on it.

In order to create a data migration class, you can use the Orchard code generation function. Run the command "codegen datamigration Maps" on the command line.

image

Visual Studio will again ask you to reload the solution. After loading, you will find in the project a new class for data migration.

image

The data migration class added by the command contains a single Create () method that defines the database structure based on the Record classes in the project. Due to the fact that we have only one MapRecord file in the project with two properties of latitude and longitude, the data migration class is very simple. Please note that the Create method is called when the module function is activated, after which the database receives the corresponding update.

using System;
using System.Collections.Generic;
using System.Data;
using Maps.Models;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Maps.DataMigrations {
    public class Migrations: DataMigrationImpl {
        public int Create () {
			// Creating table MapRecord
			SchemaBuilder.CreateTable ("MapRecord", table => table
				.ContentPartRecord ()
				.Column ("Latitude", DbType.Double)
				.Column ("Longitude", DbType.Double)
			);
            ContentDefinitionManager.AlterPartDefinition (
                typeof (MapPart) .Name, cfg => cfg.Attachable ());
            return 1;
        }
    }
}


A line with the call to AlterPartDefinition has been added so that the part of the content that we created can join any type of content. Note the need to add a string using Maps.Models;to connect the assembly.

Now let's add a handler for our content part. An Orchard handler is a class that defines the behavior of the content part, processes events, or manipulates the data model before rendering the part on the web page. Since our map content is very simple, our class will only use IRepositoryas a structure for accessing data. Add the following contents to the Handlers \ MapHandler.cs file:

using Maps.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
namespace Maps.Handlers {
    public class MapHandler: ContentHandler {
        public MapHandler (IRepository repository) {
            Filters.Add (StorageFilter.For (repository));
        }
    }
}


In addition, we need to add a driver for our content part. An Orchard driver is a class that defines the relationships of shapes to display in each context in which a part can be displayed. For example, when a map is displayed on a page, the Display method determines the name of the template that will be used for display depending on different displayTypes (for example, “details” or “summary”).

In the same way, the “Editor” driver method defines a template for displaying the editor of the content part (in our case, for the map this will be the input of latitude and longitude). Our part will be simple, we will use a figure named Map for both the Display context and the Editor. Add the MapDriver class, as described below:

using Maps.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
namespace Maps.Drivers {
    public class MapDriver: ContentPartDriver {
        protected override DriverResult Display (
            MapPart part, string displayType, dynamic shapeHelper) {
            return ContentShape ("Parts_Map", () => shapeHelper.Parts_Map (
                Longitude: part.Longitude,
                Latitude: part.Latitude));
        }
        // GET
        protected override DriverResult Editor (
            MapPart part, dynamic shapeHelper) {
            return ContentShape ("Parts_Map_Edit",
                () => shapeHelper.EditorTemplate (
                    TemplateName: "Parts / Map",
                    Model: part,
                    Prefix: Prefix));
        }
        // POST
        protected override DriverResult Editor (
            MapPart part, IUpdateModel updater, dynamic shapeHelper) {
            updater.TryUpdateModel (part, Prefix, null, null);
            return Editor (part, shapeHelper);
        }
    }
}


Now we can use Visual Studio to create views for viewing and editing the content part. First add the Parts and EditorTemplates / Parts folders to the Views folder of the Maps project. Then add the files under the same name Maps.cshtml to each of the Views / EditorTemplates / Parts folders:

@model Maps.Models.MapPart
Map fields
@ Html.LabelFor (model => model.Latitude)
@ Html.TextBoxFor (model => model.Latitude) @ Html.ValidationMessageFor (model => model.Latitude)
@ Html.LabelFor (model => model.Longitude)
@ Html.TextBoxFor (model => model.Longitude) @ Html.ValidationMessageFor (model => model.Longitude)


and Views / Parts with the following contents:

Location


Both of these templates will subsequently appear as small parts of a large page on the site. Due to the fact that the system needs to know the order and location where parts of the content will be displayed, we need to specify this data in a special placement.info file in the root of the modules folder:



This definition tells the system that the Parts_Map figure (which is displayed using Views / Parts / Maps.cshtml) should be displayed in the Content area, if it exists, at 10 position. In addition, it is indicated that the editor should be displayed in the Primary zone in the second position.

To activate our content part of the map, go to the Modules section in the Orchard admin panel and enable it.

image

You can test the content part by simply joining any type of content in the system using the “Content Types” section in the Orchard admin panel. Let's add our content part to an existing content type, for example, the Event type that we created in the previous article Orchard CMS Extension: Creating Content Types .

On the Manage Content Types page of the administration panel, click Edit to edit the definition of the content type.

image

In the list of parts, click Add in order to add the content part we created for the map. The Map part will be displayed in the list of available content parts, select it and click Save.

image

Now, go to the Manage Content section and edit the content item data. Please note that the content part of the Map added its own to the list of parameters: latitude and longitude. Enter the current location coordinates and republish the content item.

image

Now on the page of your site you can see the map displayed for a specific event.

You can download the finished package with the content part of the Map at this link:Orchard.Module.Maps.1.0.0.nupkg . This package is ready to install and use and contains complete source codes.

Turning the content part into a widget


In order to get a ready-made widget from the content part, you need to update the database with a description of your widget. To do this, add the method to a special file in the content part of Migrations.cs. The following code shows such a file for the Map content part with the UpdateFrom1 method added:UpdateFrom



using System.Data;
using Maps.Models;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace maps
{
    public class Migrations: DataMigrationImpl
    {
        public int Create ()
        {
            // Creating table MapRecord
            SchemaBuilder.CreateTable ("MapRecord", table => table
                .ContentPartRecord ()
                .Column ("Latitude", DbType.Single)
                .Column ("Longitude", DbType.Single)
            );
            ContentDefinitionManager.AlterPartDefinition (typeof (MapPart) .Name, cfg => cfg
                .Attachable ());
            return 1;
        }
        public int UpdateFrom1 ()
        {
            // Create a new widget content type with our map
            ContentDefinitionManager.AlterTypeDefinition ("MapWidget", cfg => cfg
                .WithPart ("MapPart")
                .WithPart ("WidgetPart")
                .WithPart ("CommonPart")
                .WithSetting ("Stereotype", "Widget"));
            return 2;
        }
    }
}


In this example, the UpdateFrom1 method creates a MapWidget by combining MapPart, WidgetPart, and CommonPart, and then sets the widget's special type (stereotype). WidgetPart and CommonPart objects are built into Orchard. The method returns 2, which means the version number.

Now the content part has been transformed into a widget.

Widget display


Once you have created the widget, open the administration panel and go to the Widgets tab. Here you can select the layer and zone for the location of your widget.

image

Conclusion


In this article, we examined one of the options for expanding the functionality of Orchard CMS by creating a content part and transforming it into a widget, which can later be located on any page of the site, on any layer, in any zone.

In the following articles, we will consider widget management issues and the possibility of packing and distributing the widget as a separate module.

Also popular now: