Orchard CMS

Introduction

Recently I have been doing development work on Orchard CMS, creating and modifying modules in Visual Studio. I have found that the documentation is fairly limited especially for the new features. I have had to use stackoverflow.com and various blogs to make my new Modules work.  So far the most useful blog has been

http://www.ideliverable.com/blog/writing-an-orchard-webshop-module-from-scratch-part-1

This tutorial proved to be useful however it covers a lot of ground and I had problems debugging it.

 Basic Requirements for a module

If you want to create a basic content part in your module it must have the following components.

  • Driver
  • Handler
  • Model
  • Data tables, created from the data migration
  • placement.info entries.  Without this your part will not be displayed.
  • Module.txt

If you are adding a new feature to a module then you will need to mark each of the classes with the OrchardFeature attribute.  The FeatureName must match the Features value in Module.txt.

Orchard Data Schema

The key feature of Orchard is that a Content Definition is made up of Content Parts. A Content Definition can also have Fields on the Content Item itself as well as properties on the Parts which make up the Content Definition.  This SQL query shows how this is reflected in the database is structure.

SELECT i.Id, v.Data, t.Title
FROM Orchard_Framework_ContentItemRecord i
JOIN Orchard_Framework_ContentItemVersionRecord v ON v.ContentItemRecord_id = i.Id
JOIN Title_TitlePartRecord t ON t.Id = v.Id
WHERE i.ContentType_id IN (SELECT Id FROM Orchard_Framework_ContentTypeRecord WHERE Name = 'MyContentDefinition');

The v.Data in this query will return XML which contains the Content Definition Fields and the Title comes from the TitlePart which is a part of the ‘MyContentDefinition’ Content Definition (confusingly, ContentType is another name for Content Definition).

In Orchard 1.8 the Part data can be is stored in the Data XML instead of using a separate table for each part.  This is implemented with an InfoSet which will de-normalize the data and improve DB performance.

If you create your own parts (in a module) they will be similar to the built in Title Part however your Part Record will not have a ContentItemRecord_id field.  The Id field will be used to join onto the content item record, for example

JOIN MyCompany_MyModule_MyPartRecord p ON p.Id = i.Id

Here you can see that the primary key on your record table is also a foreign key linking to the content.  You can query Orchard data by building HQL (NHibernate) queries in C#.  This is probably the most efficient and powerful way to access Orchard data however it is not always used because it can add complexity where built in Orchard functions could be used.

Computer Nerds

A well rounded Software Developer should be willing to adapt their thinking to the situation. Many developers have worked in a specialised role for too long and are unwilling to change. This is dangerous for the individual and leeds to a number of recognisable characteristics. To illustrate this point, read about the following ‘types’ which I have worked with.

VB Programmers
Don’t understand anything about syntax in C based languages and usually write far too many lines of code to express a simple program. They also don’t bother with good programming principles like SOLID and DRY which seem to flow naturally out of OO languages like C# and Java.

Continue reading

HTML5

HTML5 is a specification which is used for developing web browsers like Internet Explorer.  It helps to define what the browser should do.

What is the point of HTML5?

Compatibility Issues

The problem with web development is that there are significant differences between various web browsers.  This includes differences between newer versions of a specific browsers such as Internet Explorer and differences between rival browsers.  Also there are a number of plug-in tools (e.g. Flash) which may need to be installed. Continue reading

Resource files in Visual Studio

Aside

Resource files can be added to a project in Visual Studio.  These are xml based text files which contain key-value pairs.  When the resource files are saved, source code will be automatically generated that can be compiled like any other C# or VB file.  It is advisable to put the resource files into a separate project so that they are contained within a common namespace.

Continue reading

Custom Fonts

If a font on your web page is not installed on a client machine then there may be difficulty in making that font appear on the page when it is displayed. Normally the browser will use fonts installed on the client machine however there are various ways to get around this problem but different browsers handle fonts differently. Adding custom font support for all browsers can be a big task so there are some tools that will help. Continue reading

Beginning IronPython

Okay, so you’ve read the chapter on IronPython syntax. Brilliant, learning a new programming language is easy! You feel like such a polyglot. What’s all the fuss about?  All that remains now is a few formalities. Install the framework and knock out a quick app to demonstrate how easy it is. The only problem is that nothing works and the error messages make no sense.

Back to the world of the programming beginner

You need to learn a few practical lessons before you can say that you are a competent Python programmer. It is important to be able to interpret the common error messages and structure your project.  Normally you will find that the official documents will gloss over this stuff because they like to leave it to your discretion. As a result you will find yourself searching the internet for a blog to help!

  • You can execute from the command line with ipy.exe, use the IronPython Console or the VS Tools.

Exercise calling C# code from IronPython

Use IronPython to create some tests/specifications; one static .NET CLR class and one normal .NET CLR class. The classes will be contained in an assembly project called Polyglot, as we want to use multiple programming languages in this example.

  • Greet static class. This will have one method and one property. The method accepts a parameter, a name, and the returns a greeting for that name, e.g. “Hello Jon”. The property simply returns a string “Hello World!”.
  • Math class. This class deals with simple arithmetic, does not handle negative numbers maintains a list of answers. It has Add and Subtract methods and the Subtract method throws an exception if it cannot return a positive number. It also has an array containing all the results with the most recent result at the beginning of the list.

See IronPython Code in new tab
These unit tests will demonstration how to use a unit testing framework, import and run CLR code and the difference between an instance and a type variable in Python.

Points to Note

  1. line 2: import unittest
    Use the default unit test frameworks which is similar to NUnit in .NET.
  2. line 4: sys.path.Add('C:\\sut')
    We have added a directory to the system paths. This is where I have stored to Polyglot.dll will be tested.
  3. line 5: clr.AddReferenceToFile('Polyglot.dll')
    Add a reference to Polyglot assemble so it can be called via the CLR.
  4. line 7: from Polyglot import Greet
    Specify that the Greet class in the Polygot namespace will be used. There is no equivalent command in C# because C# intrinically uses all the types in a namespace, i.e. using namespace.
  5. line 9: class GreetTest(unittest.TestCase):
    Brackets are used to inherit from a class
  6. line 10: softwareUnderTest = Greet
    A reference to the C# Greet class type has been created. This is a static class so we can call methods on the class by call the methods directly from the type reference.
  7. line 19: softwareUnderTest = Math()
    An instance of the C# Math class has been created. The new keyword is not required like in C# however we must remember to use brackets.
  8. Method definitions & self. All class level variables, methods and types are referenced with self.
  9. Indentation and white-space are important. Python uses these instead of the {} in C based languages.
  10. line 46: unittest.main() Used to execute the unit tests.

Continue reading

Example IronPython Test Script

.NET Dynamic Language Runtime

RunTests.py

import sys
import unittest
import clr
sys.path.Add('C:\\sut') # folder containing software to be tested
clr.AddReferenceToFile('Polyglot.dll')

from Polyglot import Greet

class GreetTest(unittest.TestCase):
    softwareUnderTest = Greet
    def testGreet_ShowCsGreeting_GreetWithName(self):     
        self.assertEquals('Hello Brian', self.softwareUnderTest.GreetMe('Brian'), 'Greet Me test')        
    def testGreet_ShowCsGreeting_GreetHello(self):  
        self.assertEquals('Hello World!', self.softwareUnderTest.Hello, 'Greet Hello test')

from Polyglot import Math

class MathTest(unittest.TestCase):
    softwareUnderTest = Math()
    def testMath_Add_Simple(self):
        self.assertEquals(3, self.softwareUnderTest.Add(1,2), 'Simple Add')
    def testMath_Subtract_Simple(self):
        self.assertEqual(3, self.softwareUnderTest.Subtract(5,2), 'Simple Subtract')
    def testMath_Subtract_NegativeResultFails(self):
        with self.assertRaises(ValueError):
            self.softwareUnderTest.Subtract(3, 4)
    def testMath_Results_IsSetAndOrdered(self):
        result1 = 2
        result2 = 3
        result3 = 4
        self.softwareUnderTest = Math()
        self.assertEqual(0, len(self.softwareUnderTest.Results)) # to begin with there are no results
        self.softwareUnderTest.Add(1,1)
        self.assertEqual(1, len(self.softwareUnderTest.Results)) # after one operation there will be one result
        self.assertEqual(result1, self.softwareUnderTest.Results[0]) # check the result

        # run a couple more operations and check they are stored propery
        self.softwareUnderTest.Subtract(5,2)
        self.softwareUnderTest.Add(2,2)
        self.assertEqual(result3, self.softwareUnderTest.Results[0])
        self.assertEqual(result2, self.softwareUnderTest.Results[1])
        self.assertEqual(result1, self.softwareUnderTest.Results[2])

try:
    if __name__ == '__main__':
        unittest.main()
except:
    e = sys.exc_info()[0]
    print( "Error: %s" % e )
    clr.ClearProfilerData() # unload the .dll file so the file lock is released

a=raw_input('The end!')

Continue reading