0% found this document useful (0 votes)
65 views41 pages

IDeliverable - Writing An Orchard Webshop Module From Scratch - Part 4

Uploaded by

Active88
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
65 views41 pages

IDeliverable - Writing An Orchard Webshop Module From Scratch - Part 4

Uploaded by

Active88
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 41

4/10/2015 IDeliverable 

­ Writing an Orchard Webshop Module from scratch ­ Part 4

Writing an Orchard Webshop


Module from scratch - Part 4
orchard (/Tags/orchard)

This post has been updated to be compatible for Orchard >= 1.4.

atible for Orchard >= 1.4.

This is part 4 of a tutorial on writing a new Orchard module from scratch.


For an overview of the tutorial, please see the introduction (writing-an-orchard-webshop-module-
from-scratch-part-1).

In Orchard, content items are really nothing more than a collection of Content Parts. Take for
example the Page content type: It's really just a content type definition named "Page" and a set of
parts attached to it, such as BodyPart, CommonPart, TitlePart and AutoroutePart.

For our Webshop module, we want the user to be able to turn every conten type into a product.
The way we can do this is by creating a custom content part ourselves. We'll call it
ProductPart. When we attach a ProductPart to a content type, that content type will become a
product. Let's define exactly what it means to be a product in our module.

A product has a unit price and a sku number.


The user can add a product to a shopping cart.
When the user checks out, an order is created that holds details of the purchased products.

In this tutorial, we will define a Book content type and a DVD content type as example content
types, and attach the ProductPart to them. This will effectively turn the book type and the dvd
type into a product.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 1/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Content Parts
If you're completely new to Orchard, you may be wondering what a content part is exactly.

Well, the short version: A content part is a reusable entity that can contain data and logic and can
be attached to any content type.
The long version: A content part is defined as a record in the database and has a name and
optionally a list of content fields (the fields are stored in another table, and there is a linking
table that connects content fields with a content part). In addition, we can create a class that
matches this name and derives from ContentPart or ContentPart<T> if you have a entity class
that you wish to associate with your content part.
So even though a content part is just an entity loaded from a database record, it is possible to
provide behavior for that content part by creating a class. When Orchard loads a content type, it
asks the database which parts are attached to it. For each part, it checks if there is a class that
derives from ContentPart and matches the name.
It then welds that part on to the content type. Welding is the process of adding an instance of the
content part class to a list of parts managed by the content type instance. If no matching content
part class was found, Orchard simply instantiates a ContentPart and adds tit to the list of parts.

The strength of this design is that content parts can now contain logic instead of just data.

The ProductPart
Our ProductPart will introduce 2 properties of its own and will be derived from ContentPart<T>.
The 2 properties are: UnitPrice (float) and Sku (string).

Because our ProductPart needs to be able to store data into the database (UnitPrice and Sku), we
need to create a data entity as well. Orchard will map classes that end with the word "Record"
and live in the Models namespace. So in our case, we will create a class called ProductPartRecord
in a folder called Models. Because our data entity will store information for a content part, we
will also derive it from ContentPartRecord. This is required so that Orchard can link our
ProductPartRecord data to a content item of which it is a part of. This linking is done by giving
each part the same ID as the ID of the content item it is attached to.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 2/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

FYI, Orchard uses NHibernate as its ORM.

Let's create the ProductPart and ProductPartRecord classes:

1. Reference the Orchard.Framework project in order to be able to inherit from


Orchard.ContentManagement.Records.ContentPartRecord.
2. Add a new Folder named Models
3. Create a new public class named ProductPartRecord inside the "Models" folder and make it
inherit from ContentPartRecord.
4. Create a new public class named ProductPart inside the "Models" folder which inherits
from ContentPart<ProductPartRecord>
5. The ProductPartRecord will have the following properties: Price (decimal) and Sku (string).
Important: the properties need to be virtual.
6. The ProductPart class will have the same properties as ProductPartRecord, implemented
as wrappers (see code below)

ProductPart.cs:

using Orchard.ContentManagement;
 
namespace Skywalker.Webshop.Models
{
    public class ProductPart : ContentPart<ProductPartRecord>
    {
        public decimal UnitPrice
        {
            get { return Record.UnitPrice; }
            set { Record.UnitPrice = value; }
        }
 
        public string Sku
        {
            get { return Record.Sku; }
            set { Record.Sku = value; }
        }
    }
}

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 3/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Although the wrapping properties of ProductPart is not required, it makes it easy to use for users
of the class. If you don't create the wrapper properties, users of the class would have to reference
the Record property first, e.g. productPart.Record.Sku). 

 ProductPartRecord.cs:

using Orchard.ContentManagement.Records;
 
namespace Skywalker.Webshop.Models
{
    public class ProductPartRecord : ContentPartRecord
    {
        public virtual decimal UnitPrice { get; set; }
        public virtual string Sku { get; set; }
    }
}

Notice that the properties of ProductPartRecord are marked as virtual. This is not so much a
requirement of Orchard, but a requirement of NHibernate and has something to do with the way
NHibernate generates derived proxy classes. If you omit to do this, you will most likely get an
error. When I tried this, I received a 404: the resource could not be found. However, what really
happended is that an exception occurred in the entity mapping layer, stating that the entity
contains properties that could not be mapped.

Migrations
Great! Now that we have a data entity, what we need to do next is create a table that can store
these type of entities. One way we could do this is to create the table manually. However, that
wouldn't be the best solution if we were to redistribute the module to others, for example via the
Orchard Gallery. So surely we could generate a SQL script and distribute it along with the

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 4/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

module? Well, that could work, as long as you instruct your users to execute that script before
enabling the feature. But that isn't optimal either. Lucky for us, there is a better way, and it's
called: Migrations.

A Migration in Orchard is an abstraction over running SQL scripts and is implemented as an API
that we can use to create and modify database tables.
We implement migrations as a class that inherits from DataMigrationImpl.
Inside the migration, we tell Orchard things like what tables to create, modify or drop and what
content types and parts to create, modify or delete.
Migrations are automatically executed when you enable a module (or more specifically, a feature.
Modules are never enabled: only their features. Most modules will have a default feature that has
the same name as the module).

Let's see how to create a migration that creates a database table for us.

We'll start by creating a new class file in the root of our module called Migrations and write the
following code:

Migrations.cs:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 5/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

using Orchard.Data.Migration;
 
namespace Skywalker.Webshop
{
    public class Migrations : DataMigrationImpl {
        public int Create() {
 
            SchemaBuilder.CreateTable("ProductPartRecord", table => table
 
                // The following method will create an "Id" column for us and set it is the
 primary key for the table
                .ContentPartRecord()
 
                // Create a column named "UnitPrice" of type "decimal"
                .Column<decimal>("UnitPrice")
 
                // Create the "Sku" column and specify a maximum length of 50 characters
                .Column<string>("Sku", column => column.WithLength(50))
                );
 
            // Return the version that this feature will be after this method completes
            return 1;
        }
    }
}

When a feature is enabled, Orchard will invoke a method called "Create" on alle classes that
derive from DataMigrationImpl if the feature is not yet registered in the database or its version is
0.
If a migration for feature was previously executed, the version for that migration will be greater
than 0. If the version would be 5 for example, then Orchard will invoke a method called
UpateFrom5.
From within that method, you would return a new value of 6. Orchard will use that value to
update the current version of the migration.

The ContentPartRecord method is a convenient method that will create an Id column, configured
as the primary key.
The implementation of that method looks like this:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 6/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Orchard.Framework/Data/Schema/CreateTableCommand.cs:

 /// <summary>
        /// Defines a primary column as for content parts
        /// </summary>
        public CreateTableCommand ContentPartRecord() {
            Column<int>("Id", column => column.PrimaryKey().NotNull());
 
            return this;
        }

Although we already enabled our feature, Orchard will detect that there is a DataMigration
available and checks the currently stored migration version number for our module. Because no
migration has yet run for our module, there will be no migration version available in the
Orchard_Framework_DataMigrationRecord table:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 7/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

However, when you refresh the Orchard Admin, Orchard will execute the migration in the
background, after which our feature will be at version 1

As you can see, Orchard inserted a new record into the Migrations table, holding the typename of
our Migrations class and the last version number it returned.
The Create method returned a value of 1.

The next thing we need to do is to create a ProductPart. Although we created a class called
ProductPart that derives from ContentPart, it's not enough: we need to register the part by
creating a record into the ContentPartDefinition table. We also need to tell Orchard that the
ProductPart is attachable: this will enable users to attach the ProductPart via the admin.

To do this, we'll add an UpdateFrom1 method to our Migration class, which will instruct Orchard
to create a part called ProductPart if it doesn't already exist and to make the ProductPart
attachable using the Attachable extension method.
In order to be able to use the AlterPartDefinition method, we need to import

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 8/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

the Orchard.ContentManagement.MetaData namespace.
In order to use the Attachable extension method, we need to first reference the Orchard.Core
project and import the Orchard.Core.Contents.Extensions namespace.

Add the following method to the Migrations class:

 public int UpdateFrom1(){
 
            // Create (or alter) a part called "ProductPart" and configure it to be "attach
able".
            ContentDefinitionManager.AlterPartDefinition("ProductPart", part => part
                .Attachable());
 
            return 2;
        }

Tip: Resharper (http://www.jetbrains.com/resharper/) will tell you which assemblies to reference


and namespaces to import, which is really helpful in al sorts of projects, expecially in Orchard.
The complete migration should now looks like this:

Migrations.cs:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 9/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
 
namespace Skywalker.Webshop
{
    public class Migrations : DataMigrationImpl {
        public int Create() {
 
            SchemaBuilder.CreateTable("ProductPartRecord", table => table
 
                // The following method will create an "Id" column for us and set it is the
 primary key for the table
                .ContentPartRecord()
 
                // Create a column named "UnitPrice" of type "decimal"
                .Column<decimal>("UnitPrice")
 
                // Create the "Sku" column and specify a maximum length of 50 characters
                .Column<string>("Sku", column => column.WithLength(50))
                );
 
            // Return the version that this feature will be after this method completes
            return 1;
        }
 
        public int UpdateFrom1(){
 
            // Create (or alter) a part called "ProductPart" and configure it to be "attach
able".
            ContentDefinitionManager.AlterPartDefinition("ProductPart", part => part
                .Attachable());
 
            return 2;
        }
    }
}

When you made a change to your module as we just did, you only need to save the file: Orchard
will recompile the module when you refresh the page.
When you refresh the admin, Orchard will once again run the migration in the background.
This will cause the migration record to be updated to version number 2, as well as make our
ProductPart attachable: It will create a new record in the
Settings_ContentPartDefinitionRecord table with the Attachable setting set to true:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 10/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

To verify that we have a ProductPart available, we navigate to Content -> Content Parts and
marvel at our work so far:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 11/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Notice that even though our part is called "ProductPart", Orchard displays the part here as
"Product". Now let's go ahead and create 2 new ContentTypes, called "Book" and "DVD", and
attach the ProductPart to both of them.

To create a new Content Type:

1. Go to Content -> Content Types and click the Create new Type button;
2. Enter "Book" as both the display name and the Content Type Id (which will be done for you
automatically) and press the "Create" button;
3. The Book content type has been created. Define what a Book is by attaching some product
parts that make sense: select Body, Comments, Product, Title, Autoroute, and Tags and hit
"Save"
4. Repeat step 1 to 3, this time using "DVD" as the content type name.

Now we have a Book and a DVD content type. Because they have the ProductPart attached, we
can treat them as products. 
http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 12/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

We attached the AutoroutePart so that our books and dvds will be reachable via user friendly
urls. The TitlePart enables the user to specify a title. The CommentsPart allows site visitors to
comment on the book and the Tags part allows the administrator to add tags to the book and
dvd.

Let's continue and try to create a new book. As you'll see, we see all sorts of input fields, but what
about the Price and Sku ifields?

They're not showing. Why is that? The way this works in Orchard is that in order to render a
Content Type, Orchard invokes a so called Driver for each ContentPart of the content type. A
driver is somewhat analogous to an MVC Controller, but it is just responsible for handling the
content part itself instead of the entire HTTP request.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 13/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Drivers
The Driver typically has 3 methods: one for displaying the part on the front end of the site, one
for displaying the part in edit mode on the back end of the site, and one to handle the postback
when the user saves a content item on which the content part is attached.

Each method returns a DriverResult, and most of the times that is a ShapeResult (which derives
from DriverResult). A ShapeResult tells Orchard which Razor template to use to render the part,
and also contains a dynamic object that will act as the model of the Razor template. This model is
called a Shape, and is implemented as a dynamic Clay object.

You could think of a Razor template as the "skin" of a shape. The shape itself will be the Model of
that view.

This concept of shapes and drivers is one of Orchard's most powerful features, but if also one of
the most challenging concepts to get your head around when you're getting started.
However, once you do get your head around it, it is quite simple.

Now, to make our ProductPart appear in the Create/Edit Content page, we simply need to create
a driver for it and implement the Editor methods.

Let's create a driver for our ProductPart:

1. Create a new folder called Drivers


2. Inside that folder, create a new class called ProductPartDriver

The ProductPartDriver class looks like this:

Drivers/ProductPartDriver.cs:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 14/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Skywalker.Webshop.Models;
 
namespace Skywalker.Webshop.Drivers
{
    public class ProductPartDriver : ContentPartDriver<ProductPart> {
 
        protected override string Prefix {
            get { return "Product"; }
        }
 
        protected override DriverResult Editor(ProductPart part, dynamic shapeHelper) {
            return ContentShape("Parts_Product_Edit", () => shapeHelper
                .EditorTemplate(TemplateName: "Parts/Product", Model: part, Prefix: Prefix)
);
        }
 
        protected override DriverResult Editor(ProductPart part, IUpdateModel updater, dyna
mic shapeHelper) {
            updater.TryUpdateModel(part, Prefix, null, null);
            return Editor(part, shapeHelper);
        }
 
    }
}

Our ProductPartDriver class has two methods: Editor and its overloaded version for handling a
postback.
When Orchard wants to display the editor for the ProductPart, it calls the Editor method of the
Driver. When the administrator submits the editor form, the overloaded Editor (with the
IUpdateModel argument) gets called.
Also note that we are overriding the Prefix property. Although this is optional, it is good practice
to always provide your own prefix. This helps avoiding naming conflicts when names for input
fields are generated.

The ContentShape method is defined in the ContentPartDriver<T> base class and returns a


ContentShapeResult. A ContentShapeResult tells Orchard the name of the shape as well as what
the shape looks like. In this case, we are creating a "well-known" shape called EditorTemplate,
which has a TemplateName property, a Model property and a Prefix property.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 15/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

We named our shape "Parts_Product_Edit", and its template can be found in "Parts/Product".
Because we are talking about editing a Content Part, Orchard will prefix that path with
"~/Orchard.Webshop/Views/EditorTemplates", so the full path will be:
"~/Orchard.Webshop/Views/EditorTemplates/Parts/Product.cshtml".

And that's where we will create the Razor template file:

Views/EditorTemplates/Parts/Product.cshtml:

@using System.Web.Mvc.Html
@model Skywalker.Webshop.Models.ProductPart
<fieldset>
    <legend>Product Fields</legend>
 
    <div class="editor‐label">@Html.LabelFor(x => x.Sku)</div>
    <div class="editor‐field">
        @Html.EditorFor(x => x.Sku)
        @Html.ValidationMessageFor(x => x.Sku)
    </div>
    <div class="hint">Enter the Stock Keeping Unit</div>
 
    <div class="editor‐label">@Html.LabelFor(x => x.UnitPrice)</div>
    <div class="editor‐field">
        @Html.EditorFor(x => x.UnitPrice)
        @Html.ValidationMessageFor(x => x.UnitPrice)
    </div>
    <div class="hint">Enter the sales price per unit</div>
</fieldset>

The CSS classes we're using here are styled by stylesheets that are stored in TheAdmin theme,
which is active when we're inside the admin.
Since we're using Razor, let's add two references to our project:

System.Web
System.Web.Mvc
System.Web.WebPages

We also need to copy 2 web.config files: one to our Views folder and one to the root of our project.
This is required in order to work with Razor.

Create a new web.config file in the root of your project and paste in the following code:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 16/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

web.config:

<?xml version="1.0"?>
<configuration>
 
  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configur
ation.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, Pu
blicKeyToken=31BF3856AD364E35">
      <remove name="host" />
      <remove name="pages" />
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, Syste
m.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" re
quirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection
, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364
E35" requirePermission="false" />
    </sectionGroup>
  </configSections>
 
  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0
.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
        <add namespace="System.Linq"/>
        <add namespace="System.Collections.Generic"/>
        <add namespace="Orchard.Mvc.Html"/>
      </namespaces>
    </pages>
  </system.web.webPages.razor>
 
  <system.web>
    <compilation targetFramework="4.0">
      <assemblies>
        <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKey
Token=31bf3856ad364e35"/>
        <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken
=31bf3856ad364e35"/>
        <add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B
77A5C561934E089"/>
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31b
f3856ad364e35" />
        <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToke
n=31bf3856ad364e35"/>
      </assemblies>
http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 17/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

    </compilation>
  </system.web>
 
</configuration>

Next, create a web.config file in the root of the Views folder and paste in the following code:

Views/web.config:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 18/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
  <system.web>
    <httpHandlers>
    </httpHandlers>
 
    <!‐‐
        Enabling request validation in view pages would cause validation to occur
        after the input has already been processed by the controller. By default
        MVC performs request validation before a controller processes the input.
        To change this behavior apply the ValidateInputAttribute to a
        controller or action.
    ‐‐>
    <pages
        validateRequest="false"
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=
3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35, processorArchitecture=MSIL"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neu
tral, PublicKeyToken=31BF3856AD364E35, processorArchitecture=MSIL"
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.
0, Culture=neutral, PublicKeyToken=31BF3856AD364E35, processorArchitecture=MSIL">
      <controls>
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31B
F3856AD364E35, processorArchitecture=MSIL" namespace="System.Web.Mvc" tagPrefix="mvc" />
      </controls>
    </pages>
  </system.web>
 
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <handlers>
    </handlers>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas‐microsoft‐com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 19/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Before Orchard will render the shape as returned from the Editor method, we need to define the
placement of the shape.

Placement is a system that helps determining at what position and in what local zone (which is
also really just a shape (http://weblogs.asp.net/bleroy/archive/2011/06/30/so-what-are-zones-
really.aspx)) a certain shape needs to be rendered.
We define the placement of our "Parts_Product_Edit" (we defined the name of the shape in the
Editor method of our ProductDriver) shape by creating a text file called Placement.info in the
root of our Module project, and it looks like this:

<Placement>
  <Place Parts_Product_Edit="Content:1" />
</Placement>

This tells Orchard to place any shape called "Parts_Product_Edit" in a local zone called "Content"
at the second position (the first position starts at 0, and is used by the RoutablePart. Try
different positions to see what looks best to you).
Note that we're using the term "local zone" instead of "zone". This is because Placement.info only
supports adding shapes to zones local to the content item being rendered. Orchard 1.5 will allow
you to specify "global" zones as well. If you need to render certain shapes to other zones than the
local zone, you need to write some code to do so. For more details on this, check out this great
post (http://weblogs.asp.net/bleroy/archive/2011/03/26/dispatching-orchard-shapes-to-arbitrary-
zones.aspx).

When we now add a Book Content Item, we will see that our Product editor template looks like
we expected:

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 20/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Let's test it by creating 3 books and 3 dvds (don't forget to publish them in order for them to see
them on the site):

Books:

The Hobbit, $50, SKU-1001


Wizard's First Rule, $39, SKU-1002
The Hunger Games, $29, SKU-1003

DVDs:

Prometheus, $30, SKU-1004


The Shawshank Redemption, $25, SKU-1005
The Dark Knight, $20, SKU-1006
 

This will indeed create 3 new Books and DVDs. However, when you edit one of the books, you will
see that the fields Price and Sku are empty!
So what is going on here?

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 21/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

StorageFilters
The problem is that Orchard has no way of knowing where to store that information. What we
need to do is to add a StorageFilter from within a ContentHandler that will tell Orchard to
use an IRepository<ProductRecord> to save product parts to.
This storage filter will be used for the specified content part whenever Orchard needs to load
and save data.

To create a ContentHandler that adds a StorageFilter:

1. Create a new folder named Handlers


2. Create a new class named ProductPartHandler that derives from ContentHandler

Write the following code:

using Orchard.ContentManagement.Handlers;
using Orchard.Data;
using Skywalker.Webshop.Models;
 
namespace Skywalker.Webshop.Handlers
{
    public class ProductPartHandler : ContentHandler
    {
        public ProductPartHandler(IRepository<ProductPartRecord> repository)
        {
            Filters.Add(StorageFilter.For(repository));
        }
    }
}

What we are seeing here is the constructor dependency injection pattern at work: Orchard
injects a repository of ProductPartRecord into our handler because we asked for it by simply
declaring it to be a dependency of our class constructor.
Next, we add a StorageFilter to the Filters collection, which enables Orchard to save and load our
ProductPart information when it needs to.
When you now try updating one of the books or dvds, you'll notice that the Price and Sku fields
actually get saved.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 22/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

To recap, there are a couple of steps to follow to create a ContentPart that can persist
information in the database:

1. Create a Record class that represents your entity


2. Create a ContentPart class that derives from ContentPart<TRecord>
3. Create a Migration for your content part that defines the database schema
4. Create a Driver for your content part
5. Create an editor template for your content part
6. Add a StorageFilter for your content part using a Handler

Sometimes you might find that you just want to create a content part that doesn't require
custom properties to be persisted into the database. For example, the Orchard Profile Module
(http://orchardprofile.codeplex.com/) defines a ProfilePart which has no physical ProfilePart
class. In that case, you only need to follow just one step:

1. Create a Migration that defines the content part

Optionally, when you want to render a content part, you might define a Driver that will create
Shapes.
Alternatively, you could create a ShapeTableProvider
(http://www.szmyd.com.pl/blog/customizing-orchard-shapes), which is simply a class that
derives from Orchard.DisplayManagement.Descriptors.IShapeTableProvider.

At first this may all seem a bit uneasy, until you find an actual need for it. Which we will soon
enough!

Stay tuned for the upcoming Part 5 -> Defining and rendering the ProductCatalog Content Type
(/blog/writing-an-orchard-webshop-module-from-scratch-part-5)

Download the source code for this post here: Part04.zip


(/Media/Default/Tutorials/WebshopDemo/Part04.zip)

 11/18/2014 10:52 PM by Sipke Schoorstra (/blog/author/sipke)

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 23/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

52 comments
Jay_Arr 01/13/2012 08:20 PM

Having a few difficulties here:

Did the Orchard.Webshop.Models namespace get created somewhere that I


missed? Because when I do follow the instructions, the Razor view doesn't
accept it, and neither does the ProductHandler class. They both work if I drop
the Models part of the namespace and just use Orchard.Webshop.

I created the folder 'Models' for the two classes, but VS doesn't recognize it as
a namespace

Jay_Arr 01/13/2012 09:32 PM

This is what I did to work around the issue:

In the ProductPart and ProductRecord classes, I used the


Orchard.Webshop.Models namespace instead. The Razor view then worked
fine with the original statements.

I had to add 'using' lines into ProductDriver and ProductHandler to specify


Orchard.Webshop.Models

In the migration, I had to change the AlterPartDefinition(typeof) so that it


included to full namespace for ProductPart. A 'using' line didn't work at all
here.

Why I had to do this, I have no idea, but I'm assuming it has something to do
with inheritance - I'm still relatively new to this though. Any suggestions on

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 24/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

how to fix this properly are welcome. Maybe I missed something without
realizing it, but I went over these instructions 3 times from scratch with the
same result.

Rey 01/15/2012 03:31 PM

hey man thanks for the tutorials. I'm having an issue, everything has worked
thus far but when I refresh the page to show the contentpart of the product,
it doesn't show at all. What can be could be happening?

Sipke Schoorstra 01/15/2012 03:50 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

You're absolutely right, at some stage I stored the ProductRecord and


ProductPart classes in the Orchard.Webshop.Models namespace, but I didn't
update the screenshots that sow the ProductPart code. I will update these
screenshots to avoid future confusion. Thanks for pointing it out!

Sipke Schoorstra 01/15/2012 03:53 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

You're welcome! Which page are you talking about? The admin page where
you should see all the content parts attached to the Book content type?

Guest 01/18/2012 10:51 PM

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 25/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Getting the following error in the ProductHandler class for this line
Filters.Add(StorageFilter.For(repository));

The type 'Orchard.Webshop.Models.ProductRecord' cannot be used as type


parameter 'TRecord' in the generic type or method
'Orchard.ContentManagement.Handlers.StorageFilter.For<trecord>
(Orchard.Data.IRepository<trecord>)'. There is no implicit reference
conversion from 'Orchard.Webshop.Models.ProductRecord' to
'Orchard.ContentManagement.Records.ContentPartRecord'. </trecord>
</trecord>

Guest 01/18/2012 10:58 PM

Nevermind, after some googling for what this error is, it seems I forgot to
have ProductRecord inherit from ContentPartRecord. Missed that step.

sartar singh 01/20/2012 12:45 PM

That has been great explanation.

blogger60 01/23/2012 03:32 AM

Product driver class editor method contains a dynamic parameter


shapeHelper. How do I know which methods it contains and what is the
appropriate method to call in editor method

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 26/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Nick Bratym 02/04/2012 10:38 AM [http://www.facebook.com/nbnick]


(http://www.facebook.com/nbnick)

There is some issue, you must create ProductHandler, before adding a books
items.

Sipke Schoorstra 02/04/2012 02:03 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

You're right, and I created the ProductHandler afterwards on purpose, to


demonstrate the reason for the ProductHandler and the StorageFilter. I think
that making mistakes and then solving them is a great way to learn and
understand.

Sipke Schoorstra 02/04/2012 02:11 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

That's one of the hardest things I found with Orchard as well: how do you
know what methods can be called on dynamic objects? Firstly, we should just
look at the docs which methods they discuss and use these. Secondly, we
could use the debugger and the stack trace to trace back to where the
dynamic shapeHelper object was created. You will soon find that the
shapehelper is a Clay object, which can have several ClayBehaviors attached
to it. In the end, it's these behaviors that define what methods and properties
can be called. For an excellent post on Clay, check out this link:
http://downplay.co.uk/tutorial... (http://downplay.co.uk/tutorials/clay/down-
the-rabbit-hole).

In any case you should know that when you're being passed a shapeHelper
from the Editor() method of a driver, you just have the EditorTemplate
available.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 27/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

When you're in the Display() method, you call a method with any name you
like. Clay will use that method name as the name for the shape it will create
(it's one of the ClayBehaviors who's responsible for this behavior). So, you
can call any method you like: all it does is create a shape with the name of the
method.

blogger60 02/09/2012 08:28 PM

Thanks Sipke...The information you provided was very helpful in clearing


confusion around Drivers and how orchard shape rendering work. Your
articles are really great and detailed. Your articles really helped me in
understanding how to do development/customization in Orchard(A very
good open source CMS in .Net).

Keep it up the good work.

Reno Dante 02/24/2012 04:25 PM

Orchard.Webshop.Models.ProductPart I am getting exception Object


reference not set to an instance of an object. I am using Orchard.sdf database

IRSOG 02/27/2012 05:33 AM

Thanks Sipke niceeeeeeeeeeeeeeeee

Reno Dante 03/01/2012 09:46 AM

You need to add handler.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 28/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Reno Dante 03/01/2012 09:47 AM

find out myself problem. Didn't add handler.

Reno Dante 03/01/2012 09:49 AM

Did you set path like \src\Orchard.Web\Modules

Michael Artz 03/05/2012 02:02 AM

Sorry to post here, but I've been chasing down a problem. I can create the
products as described, and add a new one via the admin screen. They appear
in the database, but they are not listed on the content screen or indeed in the
"list" beside the content Type.

Can you give me a suggestion of where I might need look?

Michael Artz 03/05/2012 04:30 AM

Ok, Im not sure what did it, but i added the commonpart and also made it
draftable and hey presto

Adam Bowman 03/09/2012 02:13 PM [http://profiles.google.com/abowman]


(http://profiles.google.com/abowman)

To get this to work, I needed to change the namespace for ProductPart and
ProductRecord to include Models.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 29/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

namespace Orchard.Webshop.Models

Sonns1990 04/23/2012 02:48 AM

Dear !
I have this problem when create ProductPart at
ContentManagement.ContentPart.cs !

Sipke Schoorstra 04/23/2012 02:46 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

Did you include the Orchard.Webshop namespace?

Sonns1990 04/26/2012 03:20 AM

I Included both namspace "Orchard.WebShop" or "WebShop" but it still


error.The first time created the project, i did named it "WebShop" and used
SQL CE.

I Recreated the project name "Orchard.WebShop" and use SQL Express, the
problem is solved

Software Development Company 05/05/2012 10:56 AM


[http://www.mindinventory.com/] (http://www.mindinventory.com/)

They attached the Containable part so that we can add the book to a List or
other ContentTypes that have the Container part attached. They also
attached a Route, our book will have both a title and a url. The Comments

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 30/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

allow site visitors to comment on the book and the Tags part allows the
administrator to add tags to the book.

Roland 05/11/2012 03:53 PM

Using 1.4.1, when i created the data migration part it never says there is an
update. On checking it looks like it has automatically updated it?

Sipke Schoorstra 05/11/2012 05:48 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

Yes, that's the new behavior since 1.4.0.

Roland 05/11/2012 07:16 PM

Ah rite - thanks. Wish they had updated the docs...

Chad Haney 07/14/2012 09:27 PM

When I go to add the view I can't add it? How do I make the view type show
up?

Chad 07/19/2012 05:29 PM

Just add a Html page and change the extension to .cshtml

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 31/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Sipke Schoorstra 07/21/2012 11:14 AM [http://www.ideliverable.com]


(http://www.ideliverable.com)

The reason is that the class library is just a plain class library. To turn it into
a real Orchard library, you need to open the project file with a program like
Notepad and add and change some properties.
To find out what exactly, you can open the ModuleCsProj.txt file in the
CodeGenerationTemplates folder of the Orchard.CodeGeneration module
and compare that file with your own project file.
Alternatively, you may as well use the code generation command to generate
a project for you and include the files you have so far in that project.

Will 08/07/2012 06:37 PM

Thanks for a great post. How would I go about removing the Permalink
textbox from my admin view? I tried <place parts_autoroute_edit="-">
</place> from within my module's placement.info (http://placement.info) file,
but that doesn't seem to work.

Will 08/07/2012 06:39 PM

BTW, I did capitilise the template name in the above comment, but it got
reformatted.

Public Coders 09/15/2012 09:27 AM [http://twitter.com/pubscode]


(http://twitter.com/pubscode)

i am getting following error. please suggest. PubsCode

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 32/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Assembly Orchard.Framework, Version=1.5.1.0, Culture=neutral,


PublicKeyToken=null uses System.Web.Mvc, Version=3.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35 which has a higher
version than referenced assembly System.Web.Mvc, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35

Mohammed Sepahvand 09/16/2012 11:47 AM


[http://www.facebook.com/mohammed.sepahvand]
(http://www.facebook.com/mohammed.sepahvand)

Very thorough and clear, really helped getting my head around the concepts.
keep up the good work.

Michael 10/21/2012 11:32 PM

This is a great series of articles. You're essentially doing CRUD in the Admin
section. Is there any way to do this type of thing in the front-end? I am not
thinking full blown line of business CRUD, just something simple for users. I
believe something like this could be modified to do that, but I am just having
trouble figuring out how to hook it into a front-end page (say
~/CustomerName/BasicDetails). Any thoughts?

Sipke Schoorstra 10/21/2012 11:37 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

Sure, simply create your controller and apply the [Themed] attribute to
either the controller or each individual action method. All set.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 33/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Michael 10/23/2012 12:01 PM

Sipke, another (maybe less challenging question ;)): I am playing around with
1.6RC and am trying to figure out how to update the project to work with it. I
keep getting an NHIbernate error:
///
A tenant could not be started:
DefaultNHibernate.InvalidProxyTypeException: The following types may
not be used as proxies:Skywalker.Webshop.Models.OrderRecord: method
set_Details should be 'public/protected virtual' or 'protected internal virtual'
///
I admit that I have not started from scratch and created the project by hand -
I'm impatient that way :/. Any thoughts?

Michael 10/23/2012 12:04 PM

I had this error at some point. If you are using VS2012 (like I was) and MVC 4,
then that might be your problem. In the project click "Add Reference" > Go to
Orchard.Framework, ...Web.Mvc, and probably (but you didn't show it)
...Web.Pages. Remove those references (uncheck the box) and add (check the
box) for the more recent versions.

Sipke Schoorstra 10/23/2012 06:10 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

It sounds like the Details property is not marked as virtual. virtually all
members of a class that you use to map with NHibernate needs to be virtual.
Hope that helps.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 34/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Abhishek Luv 11/09/2012 10:00 AM [http://twitter.com/AbhishekLuvTwee]


(http://twitter.com/AbhishekLuvTwee)

What does this line of code do

protected override string Prefix

get

return "Product";

Sipke Schoorstra 11/09/2012 08:53 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

It returns a string that ise used as a prefix when rendering the name and ID
of the part editor. You can see this when you view the HTML source of the
rendered part editor. It's used to distinguish between different parts and
their input fields.

Guest 11/10/2012 07:54 PM

Howdy,
This may be a silly question but I cannot get the Part to render. I am
following the tutorial for my own purposes so I don't have Products.
However, I checked the database and all of my parts are created once the
module was enabled.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 35/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

I created a new Content Type and added the Parts that I wanted. I set a
breakpoint in the Editor method within my Driver and it does not get hit. The
Tags module Editor method does get hit if I set a breakpoint there. I must be
missing some kind of hook into the framework ?

Guest 11/10/2012 07:54 PM

Howdy,
This may be a silly question but I cannot get the Part to render. I am
following the tutorial for my own purposes so I don't have Products.
However, I checked the database and all of my parts are created once the
module was enabled.

I created a new Content Type and added the Parts that I wanted. I set a
breakpoint in the Editor method within my Driver and it does not get hit. The
Tags module Editor method does get hit if I set a breakpoint there. I must be
missing some kind of hook into the framework ?

Guest 11/14/2012 10:47 AM

me too, even i copy and past and there no thing happened :( what can i do :??

Nour Berro 11/14/2012 11:04 AM

Solved, my error was with placement.info (http://placement.info) file name, i


rename it to the correct name and every thing is working now... thanks

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 36/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Ahmed Mostafa 11/14/2012 07:10 PM


[http://www.facebook.com/ahmed.mostafa]
(http://www.facebook.com/ahmed.mostafa)

Make sure that MVC is version 3 and MVC.Webpages is version 1.


because the web.config is adjusted with this version

Ahmed Mostafa 11/14/2012 07:12 PM


[http://www.facebook.com/ahmed.mostafa]
(http://www.facebook.com/ahmed.mostafa)

I really wants to thank you for the wonderful tutorial. it really helped me
much. I'll continue the series.

Ahmed Mostafa 11/14/2012 07:12 PM


[http://www.facebook.com/ahmed.mostafa]
(http://www.facebook.com/ahmed.mostafa)

I really wants to thank you for the wonderful tutorial. it really helped me
much. I'll continue the series.

Ahmed Mostafa 11/14/2012 08:30 PM


[http://www.facebook.com/ahmed.mostafa]
(http://www.facebook.com/ahmed.mostafa)

I was wondering, why we don't just create the SKU and unit price as fields
from the admin panel?

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 37/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Sipke Schoorstra 12/17/2012 01:20 PM [http://www.ideliverable.com]


(http://www.ideliverable.com)

You could totally do that of course. Personally I prefer to implement


characteristics of an entity as part of a content part record, but it would
totally work when implemented as fields (and would have it's own benefits,
such as configuration options).

Meni 01/07/2015 01:40 PM

The following text appears twice, at the beginning of the tutorial: "atible for
Orchard >= 1.4". Probably a copy and paste error. Please fix that.

Besides, it's a great tutorial.

Leave a comment
Name:

Enter your name

Email address:

Enter your email address

Not displayed on the site, used to obtain Gravatar image.

URL:

Enter your website URL

Comment:

How to Format

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 38/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Type the text
Privacy & Terms
(http://www.google.com/intl/en/policies/)

Submit

 Subscribe to IDeliverable Blog (RSS)


(/rss?containerid=32)

 Subscribe (email) (/subscribe)

Topics
autofac (1) (/Tags/autofac) azure (2) (/Tags/azure) cloud services (2) (/Tags/cloud%20services)

codemirror (1) (/Tags/codemirror) globalization (1) (/Tags/globalization) jquery (1) (/Tags/jquery)

knockoutjs (1) (/Tags/knockoutjs) linqjs (1) (/Tags/linqjs) orchard (33) (/Tags/orchard)

orchard harvest (1) (/Tags/orchard%20harvest) performance (2) (/Tags/performance)

powershell (2) (/Tags/powershell) ssl (1) (/Tags/ssl) startup tasks (2) (/Tags/startup%20tasks)

webapi (1) (/Tags/webapi)

Authors
http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 39/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Daniel Stolt (/blog/author/daniel)


Daniel is the go-to guy here at IDeliverable for all things Azure. He
blogs about his experiences developing for the cloud.

(/blog/author/daniel)

Sipke Schoorstra (/blog/author/sipke)


Sipke is the resident Orchard CMS specialist here at IDeliverable. He
blogs about site building and module development.

(/blog/author/sipke)

Archive
2014 2013 2012
November (6) December (2) October (1)
(/blog/archive/2014/11) (/blog/archive/2013/12) (/blog/archive/2012/10)
September (3) June (5) September (3)
(/blog/archive/2014/9) (/blog/archive/2013/6) (/blog/archive/2012/9)
March (9) August (1)
(/blog/archive/2013/3) (/blog/archive/2012/8)
January (1) April (7)
(/blog/archive/2013/1) (/blog/archive/2012/4)
February (4)
(/blog/archive/2012/2)
January (18)
(/blog/archive/2012/1)

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 40/41
4/10/2015 IDeliverable ­ Writing an Orchard Webshop Module from scratch ­ Part 4

Postal address:
IDeliverable, Ltd.
PO Box 58341
3733 Limassol
CYPRUS

Visiting address:
IDeliverable, Ltd.
Sotiri Tofini 4, 2nd floor
Agios Athanasios
4102 Limassol
CYPRUS

VAT number: CY10318155U


TIC number: 12318155W
[email protected] (mailto:[email protected])
http://www.ideliverable.com (http://www.ideliverable.com)

All information on this website copyright © 2015 by IDeliverable, Ltd.

http://www.ideliverable.com/blog/writing­an­orchard­webshop­module­from­scratch­part­4 41/41

You might also like