$30 off During Our Annual Pro Sale. View Details »

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. Orchard
    MODULES
    GROWING YOUR

    View Slide

  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

    View Slide

  3. Content
    FUNDAMENTALS

    View Slide

  4. ContentFields

    View Slide

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

    View Slide

  6. ContentParts

    View Slide

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

    View Slide

  8. ContentTypes

    View Slide

  9. ContentTypes
    are Consumable by Users

    View Slide

  10. Types Parts Fields
    Data

    View Slide

  11. Types
    Fields
    Data

    View Slide

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

    View Slide

  13. Module/Feature

    View Slide

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

    View Slide

  15. View Slide

  16. Module
    FUNDAMENTALS

    View Slide

  17. RecordsAndParts
    records are Data Models
    parts are View Models

    View Slide

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

    View Slide

  19. ShapeTemplates

    View Slide

  20. DependencyInjection

    View Slide

  21. Controllers

    View Slide

  22. ModuleManifest

    View Slide

  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

    View Slide

  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

    View Slide

  25. Orchard
    EXECUTABLE

    View Slide

  26. WebSiteAndConsoleApp

    View Slide

  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

    View Slide

  28. C:\Orchard\src\Orchard.Web\bin> Orchard.exe_

    View Slide

  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> _

    View Slide

  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, ...

    View Slide

  31. Module
    DEVELOPMENT

    View Slide

  32. Step #1
    Code Generation

    View Slide

  33. CodeGeneration

    View Slide

  34. orchard> feature enable Orchard.CodeGeneration_

    View Slide

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

    View Slide

  36. orchard> feature enable Orchard.CodeGeneration
    Enabling features Orchard.CodeGeneration
    Code Generation was enabled
    orchard> codegen module SlideShare /IncludeInSolution:true_

    View Slide

  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> _

    View Slide

  38. ReadyGo

    View Slide

  39. UpdateManifest

    View Slide

  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

    View Slide

  41. Step #2
    Records & Parts

    View Slide

  42. RecordsAndParts
    AddNewClass

    View Slide

  43. namespace SlideShare.Models {
    public class SlideShareRecord {
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    View Slide

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

    View Slide

  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

    View Slide

  46. Use Namespace
    MODELS
    or
    RECORDS

    View Slide

  47. Mark Properties
    VIRTUAL

    View Slide

  48. Mind Your
    BASE CLASS

    View Slide

  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

    View Slide

  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 {
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    View Slide

  51. public class SlideSharePart :
    ContentPart {
    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

    View Slide

  52. Compile
    Before Proceeding

    View Slide

  53. Step #3
    Data Migration

    View Slide

  54. orchard> codegen datamigration SlideShare_

    View Slide

  55. orchard> codegen datamigration SlideShare
    Creating Data Migration for SlideShare
    Data migration created successfully in Module SlideShare
    orchard> _

    View Slide

  56. DataMigration

    View Slide

  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

    View Slide

  58. // column methods can use generic types...
    !!!! .Column("SlideShareId")
    !!!! .Column("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

    View Slide

  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

    View Slide

  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]

    View Slide

  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

    View Slide

  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

    View Slide

  63. Step #4
    Handlers & Drivers

    View Slide

  64. CreateHandler

    View Slide

  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

    View Slide

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

    View Slide

  67. CreateDriver

    View Slide

  68. using System;
    using Orchard.ContentManagement;
    using Orchard.ContentManagement.Drivers;
    using SlideShare.Models;
    namespace SlideShare.Drivers {
    public class SlideShareDriver :
    ContentPartDriver {
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  72. Step #5
    Shape Templates

    View Slide

  73. DisplayTemplate

    View Slide

  74. Mind Your
    VIEW PATHS

    View Slide

  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

    View Slide

  76. “Parts_SlideShare”
    Views/Parts/SlideShare.cshtml
    translates to

    View Slide

  77. 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">

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    View Slide

  78. @if(!string.IsNullOrWhiteSpace(Model.SlideShareId as
    string))
    {
    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">

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9

    View Slide

  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);
    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

    View Slide

  80. EditorTemplate

    View Slide

  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

    View Slide

  82. “Parts/SlideShare”
    Views/EditorTemplates/
    Parts/SlideShare.cshtml
    translates to

    View Slide

  83. @model SlideShare.Models.SlideSharePart

    SlideShare Fields

    @Html.LabelFor(m => m.SlideShareId)


    @Html.TextBoxFor(m => m.SlideShareId)
    @Html.ValidationMessageFor(m => m.SlideShareId)


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View Slide

  84. @model SlideShare.Models.SlideSharePart

    SlideShare Fields

    @Html.LabelFor(m => m.SlideShareId)


    @Html.TextBoxFor(m => m.SlideShareId)
    @Html.ValidationMessageFor(m => m.SlideShareId)


    @Html.LabelFor(m => m.StartFromSlide)


    @Html.TextBoxFor(m => m.StartFromSlide)
    @Html.ValidationMessageFor(m => m.StartFromSlide)


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    View Slide

  85. ShapePlacement

    View Slide





  86. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    View Slide

  87. Celebrate

    View Slide

  88. EnableFeature

    View Slide

  89. AddContentDone

    View Slide

  90. Module
    DISTRIBUTION

    View Slide

  91. OrchardPackaging

    View Slide

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

    View Slide

  93. orchard> feature enable Orchard.Packaging
    Enabling features Orchard.Packaging
    Packaging was enabled
    orchard> package create SlideShare C:\Code_

    View Slide

  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> _

    View Slide

  95. ...you’ll be able to achieve all kinds of
    glory and fame.
    “ ” orchardproject.net

    View Slide

  96. View Slide

  97. View Slide

  98. View Slide

  99. View Slide

  100. View Slide

  101. View Slide

  102. Profit

    View Slide

  103. github.com/aranasoft

    View Slide

  104. bit.ly/growingOrchard

    View Slide

  105. ...you’ll be able to achieve all kinds of
    glory and fame.
    “ ” orchardproject.net

    View Slide

  106. jay harris
    P R E S I D E N T
    [email protected]
    #growingOrchard
    @jayharris

    View Slide

  107. Thanks

    View Slide