This is a continuation of a series of articles on the development of your own sites based on the Orchard CMS content management system. The first articles in this series can be found at the following links:
In Orchard, a widget is a piece of UI that can be reused and can be arbitrarily placed on any page of the site. Examples of widgets may include: a tag cloud, a search form, a 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 how to write your own widgets through the creation of the content part, which is then transformed into a widget. This article is based on the original
Writing a content part .
Creating the content part
The content part is a small piece of functionality or user interface that can be added to any type of content in Orchard. Examples of such parts are: a route for accessing a content item, tags for tagging content items, a menu for adding content items to custom menus.
')
In this part, we will look at the process of creating a new content part from scratch, using the capabilities of the Orchard scaffolding productive tool. 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 the creation of the content part, which will display a map with the ability to customize through the parameters of latitude and longitude.
Important. Before you start creating the file structure for your module, you need to download and install the Code Generation function in Orchard. More information on this feature can be found
at this link .
To start creating a new content part with a map, load the Orchard project and open it in Visual Studio.
Type "codegen module Maps / IncludeInSolution: true" at the Orchard command line. The “IncludeInSolution” parameter tells Orchard to add a new module project to the Orchard project list.

After executing this command, Visual Studio will ask you to reload the solution in the development environment, agree with that. After that, the solution will have a new Maps project, 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 project root, 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 default module is very simple and contains only one function, with no 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 </ span>
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. Usually such 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.

In Orchard, the content part is represented by the Record class, which contains 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 <MapRecord> {
[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 to ensure that there are no errors.

The next step is to create a data migration for our map module. Why do we need a class for data migration? The reason is that a simple declaration of the classes Record and Part for storing data does not in fact affect the database. Data migration is the mechanism that tells Orchard how to update the database schema when the map module is enabled on the site. Migration can also update the data scheme from a previous version to a new one, but this question is beyond the scope of this article and we will not touch 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.

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

The data migration class added by the command contains the only Create () method that defines the structure of the database based on the Record classes in the project. Due to the fact that we have only one MapRecord file with two properties of latitude and longitude, the class of data migration is very simple. Note that the Create method is called at the moment 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;
}
}
}
The line with the AlterPartDefinition call is added so that the part of the content created by us can be added to any type of content. Note the need to add a line
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, handles events, or manipulates the data model before rendering the part on a web page. Since our content part of the maps is very simple, our class will use only IRepository <MapRecord> as a structure for accessing data. Add the following content 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 <MapRecord> 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 values ​​(for example, “details” or “summary”).
In the same way, the driver method “Editor” defines a template for displaying the editor of the content part (in our case for the card this will be the input of latitude and longitude). Our part will be simple, we will use a shape named Map for both the Display context and the Editor. Add a MapDriver class, as written below:
using Maps.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
namespace Maps.Drivers {
public class MapDriver: ContentPartDriver <MapPart> {
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
<fieldset>
<legend> Map Fields </ legend>
<div class = "editor-label">
@ Html.LabelFor (model => model.Latitude)
</ div>
<div class = "editor-field">
@ Html.TextBoxFor (model => model.Latitude)
@ Html.ValidationMessageFor (model => model.Latitude)
</ div>
<div class = "editor-label">
@ Html.LabelFor (model => model.Longitude)
</ div>
<div class = "editor-field">
@ Html.TextBoxFor (model => model.Longitude)
@ Html.ValidationMessageFor (model => model.Longitude)
</ div>
</ fieldset>
and Views / Parts with the following content:
<img alt = "Location" border = "1" src = "http://maps.google.com/maps/api/staticmap?
& zoom = 14
& size = 256x256
& maptype = roadmap
& markers = color: blue | @ Model.Latitude, @ Model.Longitude
& sensor = false "/>
Both of these templates will continue to appear as small parts of a large page of 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 the special placement.info file in the root of the modules folder:
<Placement>
<Place Parts_Map = "Content: 10" />
<Place Parts_Map_Edit = "Content: 7.5" />
</ Placement>
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 one exists, in 10th 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 administration panel and turn it on.

You can test the content by simply joining any type of content in the system using the “Content Types” section in the Orchard administration panel. Let's add our content part to the existing content type, for example, the Event type, which 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 content type definition.

In the parts list, click Add 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.

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

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 for installation 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 the description of your widget. To do this, add the
UpdateFrom<version#>
method to the special content part file
Migrations.cs.The following code shows such a file for the content part of the Map with the UpdateFrom1 method added:
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 specifies the special type (stereotype) of the widget. WidgetPart and CommonPart objects are built into Orchard. The method returns 2, which means the version number.
Now the content part is transformed into a widget.
Widget display
After 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.

Conclusion
In this article, we looked at one of the options for expanding the functionality of Orchard CMS by creating the 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 look at widget management and the packaging and distribution of the widget as a separate module.