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

OrchardCMS module development

1318668b99b2d5a3900f3f7758763a69?s=47 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
jay@aranasoft.com | www.aranasoft.com
www.twitter.com/jayharris

1318668b99b2d5a3900f3f7758763a69?s=128

Jay Harris

February 26, 2014
Tweet

Transcript

  1. Orchard MODULES GROWING YOUR

  2. 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
  3. Content FUNDAMENTALS

  4. ContentFields

  5. ContentFields contain Data “Get in my belly!” “Oh noes!”

  6. ContentParts

  7. ContentParts have Behavior scary eye big sharp teeth really mean!

  8. ContentTypes

  9. ContentTypes are Consumable by Users

  10. Types Parts Fields Data

  11. Types Fields Data

  12. content item a is an each instance of content type

  13. Module/Feature

  14. Module/Feature features extend the CMS modules contain and distribute

  15. None
  16. Module FUNDAMENTALS

  17. RecordsAndParts records are Data Models parts are View Models

  18. HandlersAndDrivers handlers give behavior to the code drivers are are

    specialized controllers
  19. ShapeTemplates

  20. DependencyInjection

  21. Controllers

  22. ModuleManifest

  23. 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
  24. 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
  25. Orchard EXECUTABLE

  26. WebSiteAndConsoleApp

  27. 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
  28. C:\Orchard\src\Orchard.Web\bin> Orchard.exe_

  29. 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> _
  30. 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, ...
  31. Module DEVELOPMENT

  32. Step #1 Code Generation

  33. CodeGeneration

  34. orchard> feature enable Orchard.CodeGeneration_

  35. orchard> feature enable Orchard.CodeGeneration Enabling features Orchard.CodeGeneration Code Generation was

    enabled orchard> _
  36. orchard> feature enable Orchard.CodeGeneration Enabling features Orchard.CodeGeneration Code Generation was

    enabled orchard> codegen module SlideShare /IncludeInSolution:true_
  37. 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> _
  38. ReadyGo

  39. UpdateManifest

  40. 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
  41. Step #2 Records & Parts

  42. RecordsAndParts AddNewClass

  43. namespace SlideShare.Models { public class SlideShareRecord { } } 1

    2 3 4 5 6 7 8 9 10
  44. using Orchard.ContentManagement.Records; namespace SlideShare.Models { public class SlideShareRecord : ContentPartRecord

    { } } 1 2 3 4 5 6 7 8 9 10
  45. 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
  46. Use Namespace MODELS or RECORDS

  47. Mark Properties VIRTUAL

  48. Mind Your BASE CLASS

  49. 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
  50. 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
  51. 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
  52. Compile Before Proceeding

  53. Step #3 Data Migration

  54. orchard> codegen datamigration SlideShare_

  55. orchard> codegen datamigration SlideShare Creating Data Migration for SlideShare Data

    migration created successfully in Module SlideShare orchard> _
  56. DataMigration

  57. 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
  58. // 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
  59. 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
  60. 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] “
  61. 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
  62. 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
  63. Step #4 Handlers & Drivers

  64. CreateHandler

  65. 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 “
  66. 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
  67. CreateDriver

  68. 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
  69. 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
  70. //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
  71. //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
  72. Step #5 Shape Templates

  73. DisplayTemplate

  74. Mind Your VIEW PATHS

  75. 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
  76. “Parts_SlideShare” Views/Parts/SlideShare.cshtml translates to

  77. <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 10
  78. @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
  79. @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
  80. EditorTemplate

  81. //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
  82. “Parts/SlideShare” Views/EditorTemplates/ Parts/SlideShare.cshtml translates to

  83. @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
  84. @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
  85. ShapePlacement

  86. <Placement> <Place Parts_SlideShare="Content:10" /> <Place Parts_SlideShare_Edit="Content:8" /> </Placement> 1 2

    3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  87. Celebrate

  88. EnableFeature

  89. AddContentDone

  90. Module DISTRIBUTION

  91. OrchardPackaging

  92. orchard> feature enable Orchard.Packaging Enabling features Orchard.Packaging Packaging was enabled

    orchard> _
  93. orchard> feature enable Orchard.Packaging Enabling features Orchard.Packaging Packaging was enabled

    orchard> package create SlideShare C:\Code_
  94. 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> _
  95. ...you’ll be able to achieve all kinds of glory and

    fame. “ ” orchardproject.net
  96. None
  97. None
  98. None
  99. None
  100. None
  101. None
  102. Profit

  103. github.com/aranasoft

  104. bit.ly/growingOrchard

  105. ...you’ll be able to achieve all kinds of glory and

    fame. “ ” orchardproject.net
  106. jay harris P R E S I D E N

    T jay@aranasoft.com #growingOrchard @jayharris
  107. Thanks