Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OrchardCMS module development

Jay Harris
February 26, 2014

OrchardCMS module development

So, you need a Content Management System on the .NET framework. While your business might spend wheelbarrows of money on a platform that is powerful and extensible, your personal site would abandon extensibility for a free, open-source solution. But what if we had an option that was free and powerful and extensible? We do: Orchard CMS. Since we already know that Orchard is free, in this session we will discuss the power of Orchard’s CMS engine. You will learn how to build new modules for the Orchard platform, allowing you to extend functionality as you see fit to meet the needs of your site, your business, and customers.

Author:
Jay Harris
Problem Solver | Arana Software
[email protected] | www.aranasoft.com
www.twitter.com/jayharris

Jay Harris

February 26, 2014
Tweet

More Decks by Jay Harris

Other Decks in Technology

Transcript

  1. We wanted a modern CMS based on ASP.NET MVC. We

    also wanted an extremely extensible platform similar to what PHP has with Drupal. But most importantly we wanted to build better bridges between our team at Microsoft and the open-source community. “ ” Betrand Le Roy, Creator of Orchard
  2. A manifest stores metadata that Orchard uses to describe modules

    and themes to the system, such as name, version, description, author, and tags.” Manifest [Module|Theme] “ docs.orchardproject.net
  3. Name: AntiSpam AntiForgery: enabled Author: The Orchard Team Website: http://orchardproject.net

    Version: 1.7.2 OrchardVersion: 1.7.2 Description: Provides anti-spam services to protect your Features: Orchard.AntiSpam: Name: Anti-Spam Description: Provides anti-spam services to prot Category: Security Dependencies: Orchard.Tokens, Orchard.jQuery Akismet.Filter: Name: Akismet Anti-Spam Filter Description: Provides an anti-spam filter based Category: Security Dependencies: Orchard.AntiSpam 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  4. The Orchard command interpreter supports running a few built-in commands

    as well as specific commands from enabled features of an Orchard installation. “ ” Orchard.exe
  5. C:\Orchard\src\Orchard.Web\bin> Orchard.exe Initializing Orchard session. (This might take a few

    seconds...) Type "?" for help, "exit" to exit, "cls" to clear screen orchard> _
  6. orchard> call me "Rock God" Error executing command "call me

    Rock God" ---------------------------------------------------------- No command found matching arguments "call me Rock God". Commands available: site setting set baseurl, autoroute create, theme list, theme activate, widget create, layer create, menuitem create, menu create, blog create, blog import, ...
  7. orchard> feature enable Orchard.CodeGeneration Enabling features Orchard.CodeGeneration Code Generation was

    enabled orchard> codegen module SlideShare /IncludeInSolution:true Creating Module SlideShare Module SlideShare created successfully orchard> _
  8. Name: SlideShare AntiForgery: enabled Author: Jay Harris, Arana Software Website:

    http://www.aranasoft.com Version: 1.0 OrchardVersion: 1.0 Description: Provide SlideShare presentations as an atta Features: SlideShare: Description: Provide SlideShare presentations as an Category: Social 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  9. using Orchard.ContentManagement.Records; namespace SlideShare.Models { public class SlideShareRecord : ContentPartRecord

    { public virtual string SlideShareId { get; set; } public virtual int? StartFromSlide { get; set; } } } 1 2 3 4 5 6 7 8 9 10
  10. using Orchard.ContentManagement.Records; namespace SlideShare.Models { public class SlideShareRecord : ContentPartRecord

    { public virtual string SlideShareId { get; set; } public virtual int? StartFromSlide { get; set; } } } 1 2 3 4 5 6 7 8 9 10
  11. using Orchard.ContentManagement; using Orchard.ContentManagement.Records; namespace SlideShare.Models { public class SlideShareRecord

    : ContentPartRecord { public virtual string SlideShareId { get; set; } public virtual int? StartFromSlide { get; set; } } public class SlideSharePart : ContentPart<SlideShareRecord> { } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  12. public class SlideSharePart : ContentPart<SlideShareRecord> { public string SlideShareId {

    get { return Record.SlideShareId; } set { Record.SlideShareId = value; } } [Range(1, 20)] public int? StartFromSlide { get { return Record.StartFromSlide; } set { Record.StartFromSlide = value; } } } 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
  13. orchard> codegen datamigration SlideShare Creating Data Migration for SlideShare Data

    migration created successfully in Module SlideShare orchard> _
  14. using System.Data; using Orchard.Data.Migration; namespace SlideShare { public class Migrations

    : DataMigrationImpl { public int Create() { // Creating table SlideShareRecord SchemaBuilder.CreateTable("SlideShareRecord", table => table !!!! .ContentPartRecord() !!!! .Column("SlideShareId", DbType.String) !!!! .Column("StartFromSlide", DbType.Int32) ); return 1; } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  15. // column methods can use generic types... !!!! .Column<string>("SlideShareId") !!!!

    .Column<int>("StartFromSlide") // or they can use a DbType argument. !!!! .Column("SlideShareId", DbType.String) !!!! .Column("StartFromSlide", DbType.Int32) // either way. just not both. 11 12 13 11 12 13
  16. public class Migrations : DataMigrationImpl { public int Create() {

    // The ‘return’ is for versioning return 1; } public int UpdateFrom1() { // Do some data migrations return 2; } public int UpdateFrom2() { // More data migrations return 3; } } 5 6 14 15 16 25 26 27 33 34 35
  17. Indicates if administrators can attach the Content Part to any

    Content Type, or if the Part is limited only to Types explicitly specified by the Module. ” Attachable [Content Part] “
  18. using System.Data; using Orchard.ContentManagement.MetaData; using Orchard.Core.Contents.Extensions; using Orchard.Data.Migration; using SlideShare.Models;

    namespace SlideShare { public class Migrations : DataMigrationImpl { public int UpdateFrom1() { ContentDefinitionManager.AlterPartDefinition( "SlideSharePart", cfg => cfg.Attachable()); return 2; } } } 1 2 3 4 5 6 7 8 17 18 19 20 21 22 23 24
  19. using System.Data; using Orchard.ContentManagement.MetaData; using Orchard.Core.Contents.Extensions; using Orchard.Data.Migration; using SlideShare.Models;

    namespace SlideShare { public class Migrations : DataMigrationImpl { public int UpdateFrom1() { ContentDefinitionManager.AlterPartDefinition( typeof(SlideSharePart).Name, cfg => cfg.Attachable( return 2; } } } 1 2 3 4 5 6 7 8 17 18 19 20 21 22 23 24
  20. Most Handlers will be very simple behavior managers. Often, the

    only behavior they will have to specify is how data is to be stored.” Part Handlers “
  21. using SlideShare.Models; using Orchard.ContentManagement.Handlers; using Orchard.Data; namespace SlideShare.Handlers { public

    class SlideShareHandler : ContentHandler { public SlideShareHandler( IRepository<SlideShareRecord> repository) { Filters.Add(StorageFilter.For(repository)); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  22. using System; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using SlideShare.Models; namespace SlideShare.Drivers

    { public class SlideShareDriver : ContentPartDriver<SlideSharePart> { } } 1 2 3 4 5 6 7 8 9 10 11 12 13
  23. protected override DriverResult Display( SlideSharePart part, string displayType, dynamic shapeHelper)

    { return ContentShape("Parts_SlideShare", () => shapeHelper.Parts_SlideShare( SlideShareId: part.SlideShareId, StartFromSlide: part.StartFromSlide ) ); } 10 11 12 13 14 15 16 17 18 19 20 21
  24. //GET protected override DriverResult Editor( SlideSharePart part, dynamic shapeHelper) {

    return ContentShape("Parts_SlideShare_Edit", () => shapeHelper.EditorTemplate( TemplateName: "Parts/SlideShare", Model: part, Prefix: Prefix ) ); } 23 24 25 26 27 28 29 30 31 32 33 34 35
  25. //POST protected override DriverResult Editor( SlideSharePart part, IUpdateModel updater, dynamic

    shapeHelper) { updater.TryUpdateModel(part, Prefix, null, null); return Editor(part, shapeHelper); } 37 38 39 40 41 42 43 44 45
  26. protected override DriverResult Display( SlideSharePart part, string displayType, dynamic shapeHelper)

    { return ContentShape("Parts_SlideShare", () => shapeHelper.Parts_SlideShare( SlideShareId: part.SlideShareId, StartFromSlide: part.StartFromSlide ) ); } 10 11 12 13 14 15 16 17 18 19 20 21
  27. @if(!string.IsNullOrWhiteSpace(Model.SlideShareId as string)) { <iframe src="http://www.slideshare.net/slideshow/ embed_code/13888742?startSlide=8" width="427" height="356" frameborder="0"

    marginwidth="0" marginheight="0" scrolling="no" allowfullscreen style="border: 1px solid #CCC; border-width: 1px 1px 0; margin-bottom: 5px"> </iframe> } 1 2 3 4 5 6 7 8 9
  28. @if(!string.IsNullOrWhiteSpace(Model.SlideShareId as string)) { var startSlide = Model.StartFromSlide as int?;

    var startSlideQS = startSlide.HasValue ? string.Format("startSlide={0}", startSlide.Value) : string.Empty; var slideShareUrl = string.Format( "http://www.slideshare.net/slideshow/embed_code/ {0}?{1}", Model.SlideShareId, startSlideQS); <iframe src="@slideShareUrl" width="427" height="356" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" allowfullscreen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  29. //GET protected override DriverResult Editor( SlideSharePart part, dynamic shapeHelper) {

    return ContentShape("Parts_SlideShare_Edit", () => shapeHelper.EditorTemplate( TemplateName: "Parts/SlideShare", Model: part, Prefix: Prefix ) ); } 23 24 25 26 27 28 29 30 31 32 33 34 35
  30. @model SlideShare.Models.SlideSharePart <fieldset> <legend>SlideShare Fields</legend> <div class="editor-label"> @Html.LabelFor(m => m.SlideShareId)

    </div> <div class="editor-field"> @Html.TextBoxFor(m => m.SlideShareId) @Html.ValidationMessageFor(m => m.SlideShareId) </div> </fieldset> 1 2 3 4 5 6 7 8 9 10 11
  31. @model SlideShare.Models.SlideSharePart <fieldset> <legend>SlideShare Fields</legend> <div class="editor-label"> @Html.LabelFor(m => m.SlideShareId)

    </div> <div class="editor-field"> @Html.TextBoxFor(m => m.SlideShareId) @Html.ValidationMessageFor(m => m.SlideShareId) </div> <div class="editor-label"> @Html.LabelFor(m => m.StartFromSlide) </div> <div class="editor-field"> @Html.TextBoxFor(m => m.StartFromSlide) @Html.ValidationMessageFor(m => m.StartFromSlide) </div> </fieldset> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  32. orchard> feature enable Orchard.Packaging Enabling features Orchard.Packaging Packaging was enabled

    orchard> package create SlideShare C:\Code Package "C:\Code\Orchard.Module.SlideShare.1.0.nupkg" successfully created orchard> _
  33. jay harris P R E S I D E N

    T [email protected] #growingOrchard @jayharris