65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Orchard CMS 上进行焊接

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (26投票s)

2012年8月5日

CPOL

10分钟阅读

viewsIcon

110026

在不触碰他人代码的情况下扩展 CMS 的现有内容

Orchard CMS 

目录

 

简介 

本文将介绍如何扩展 Orchard CMS。Orchard 是一个基于 ASP.NET MVC 构建的内容管理系统,它是开源的,由社区驱动。






Orchard 网站使用不同类型的内容进行组合,每种内容类型都可以有一个或多个内容部分或字段。有预定义的内容类型,例如“page”、“blog”、“comment”、“widget”等。例如,“博客文章”就是由多个部分组成的
您也可以在 Orchard 中使用现有的部分和字段定义自定义内容类型,无需编码。所有功能都包含在模块中,因此您可以做任何事情。
使用 Orchard,您可以创建表示真实世界对象的内容类型,如“汽车”、“鲜花”、“邮票”、“DVD”等,并在您的网站上(作为集合)显示它们。

本文介绍如何创建一个模块来扩展 Orchard 网站的现有内容。首先,简单介绍一下 Orchard 为我解决的主要问题。然后,我们将开始构建一个部分。

背景

过去,当我使用内容管理系统(在此不便提及)时,我使用 CMS 附带的模块/功能,以及其他人创建的模块/扩展。当然,我也自己创建过模块。大多数情况下,这些模块本身工作得很好。每个模块都有自己的代码、数据库表、脚本、样式表等,模块所需的任何东西。  

但是,不同公司/开发者的模块之间没有协作。举个简单的例子;假设您使用一个模块来在您的网站上显示产品列表。每个产品都有标题、描述、图片、价格等。产品显示得很漂亮,用户也添加了更多产品。很好。
现在,过了一段时间(产品数量增加),网站所有者希望对产品进行分类。您该怎么办?如果您是产品模块的作者,您可以创建一个支持类别的新版本。如果您不是作者,您可以要求开发人员制作一个新版本。您还能做什么。难道不能在不更改现有代码的情况下,使用一个“类别模块”来对产品进行分类吗?这样您就不必修改现有代码了。如果网站所有者希望为每个产品看到一些与产品不那么相关的内容呢?您是否要将其塞入您的产品模块?

需求总是在变化。使用 Orchard,您可以将您的添加项“焊接”到现有内容中。当然,您也可以随时将其“打磨”掉。所有这些,都无需修改现有(或其他)代码!


让我们创建一个模块

已有许多模块和主题,并且可在中找到,可用于您的 Orchard 网站。当然,可能有些模块不能完全满足您的要求。您可以要求模块所有者制作一个新版本,或者您可以向该项目贡献代码。但任何人都可以自由地制作他们想要的东西,所以让我们开始构建吧!

这里创建的模块包含一个功能,可以使您将外部相册“焊接”到您的内容类型中。在这种情况下,是一个Picasa 网页相册。要访问 Picasa 网页相册,我们需要一个用户、相册的引用(ID)以及可选的授权密钥。您可以从相册 URL(通过 RSS 链接或 Google+ 链接)获取 ID。授权密钥用于非公开相册,即所谓的“只有链接才能访问”的保护。

架构  

那么我们如何构建一个模块呢?很明显,这当然是 MVC 模式。因为每个模块都实现为一个 ASP.NET MVC 区域。但是 Orchard 注入了更多功能,以支持更多功能。基本上,ContentPart 是模型,Driver 在某种程度上是(简化的)控制器,而 View 通过一个 Shape 来接收要显示的内容,无论是 ContentPart 还是 Driver 组合的内容。






设置开发环境

只是为了展示(再次再次)设置开发环境是多么容易。

开始下载代码

Orchard 代码在 CodePlex 上,您可以在此处下载最新版本
选择“Orchard.Source.x.x.x.zip”文件。目前最新的是 1.5.1。

在 Visual Studio 中加载

将文件解压到本地驱动器,然后双击 .sln 文件,前提是您已安装 Visual Studio。

生成并运行,设置网站

按 Control-F5,您将看到“入门”页面。给您的网站命名并设置一些凭据。当然要记住它们。

设置数据库。您可以使用 SQL Server Compact,但为了速度,请使用 SQL Server。
在 SQL Server Management Studio 中新建数据库。

然后您需要一个连接字符串。由于我过去经常搞砸这个问题,这里是我基于名为“OrchardModuleDev”的本地 SQL Server 数据库的连接字符串。

Data Source=localhost;Initial Catalog=OrchardModuleDev;Persist Security Info=True;Integrated Security=SSPI;Trusted_Connection=yes;

如果一切顺利,您将看到一个本地运行的新网站,使用默认主题。 

创建模块

添加新项目

为模块向解决方案添加新的类库项目。

给您的模块起一个好听的项目名称。确保您的项目位于“Modules”文件夹中。

删除 Class1.cs 文件。您将得到这个

添加 Module.txt

Name: External Album
AntiForgery: enabled
Author: Geert Doornbos
Website: http://orchardexternalalbum.codeplex.com
Version: 1.0
OrchardVersion: 1.5
Description: Show an external photo album on your website
Category: Media
Dependencies: Orchard.jQuery

由于我事先知道我需要 jQuery 模块,因此将其设置为我们唯一的模块依赖项。

添加 web.config(从源文件复制)

现在我们有了一个空的模块。转到您的网站,在 /admin/modules 处查看已安装的模块。在搜索框中键入“external”并按搜索。模块显示为“已安装”。Orchard 仅通过放置一个名为 module.txt 的文件即可识别该模块。

启用模块

现在模块显示为已安装,但尚未启用。转到 /admin/modules/features
在过滤器框中键入“external”并启用它。

添加代码

添加文件夹

MVC - 向您的项目添加以下文件夹:Models, ViewModels, Views, Drivers, Handlers, Scripts, Styles

有些文件夹有意义(约定),有些文件夹用于整齐地存储文件。

添加引用

添加对 Orchard.Core 和 Orchard.Framework 的项目引用。

添加内容部分类

向 Models 文件夹添加一个派生自 ContentPartRecord 的类。  用必要的虚拟属性填充它,以便访问 Picasa 相册。

using Orchard.ContentManagement.Records;
 
namespace Orchard.ExternalAlbum.Models
{
    public class PicasaAlbumPartRecord : ContentPartRecord
    {
        public virtual string UserID { get; set; }
        public virtual string AlbumID { get; set; }
        public virtual string AuthKey { get; set; }
    }
}

向 Models 文件夹添加一个派生自 ContentPart 的类。  添加属性并将它们链接到记录。

using Orchard.ContentManagement;
using Orchard.ExternalAlbum.Models;
 
namespace Orchard.ExternalAlbum.Models
{
    public class PicasaAlbumPart : ContentPart<PicasaAlbumPartRecord>
    {
        public string UserID
        {
            get { return Record.UserID; }
            set { Record.UserID = value; }
        }
        public string AlbumID
        {
            get { return Record.AlbumID; }
            set { Record.AlbumID = value; }
        }
        public string AuthKey
        {
            get { return Record.AuthKey; }
            set { Record.AuthKey = value; }
        }
    }
}

添加迁移类 

添加一个迁移类来指定数据库表和列。还可以定义内容部分并使其可附加。

using Orchard.Data.Migration;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
 
namespace Orchard.ExternalAlbum
{
    public class Migrations : DataMigrationImpl
    {
        public int Create()
        {
            SchemaBuilder.CreateTable("PicasaAlbumPartRecord", table => table
 
                // The following method will create an "Id" column for us and set it is the primary key for the table
                .ContentPartRecord()
 
                .Column<string>("UserID", column => column.WithLength(255))
                .Column<string>("AlbumID", column => column.WithLength(255))
                .Column<string>("AuthKey", column => column.WithLength(255))
                );
 
            // Create (or alter) a part called "PicasaAlbumPart" and configure it to be "attachable".
            ContentDefinitionManager.AlterPartDefinition("PicasaAlbumPart", part => part
                .Attachable());
 
            // Return the version that this feature will be after this method completes
            return 1;
        }
    }
}

生成项目后,我们的部分将可见于 /Admin/ContentTypes/ListParts

添加驱动程序类

为内容部分添加驱动程序类。  通过重写 Editor 和 Display,驱动程序决定在编辑或显示相册时应执行的操作。

using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.ExternalAlbum.Models;
 
namespace Orchard.ExternalAlbum.Drivers
{
    public class PicasaAlbumPartDriver : ContentPartDriver<PicasaAlbumPart>
    {
        protected override string Prefix
        {
            get { return "PicasaAlbum"; }
        }
 
        protected override DriverResult Editor(PicasaAlbumPart part, dynamic shapeHelper)
        {
            return ContentShape("Parts_PicasaAlbum_Edit", () => shapeHelper
                .EditorTemplate(TemplateName: "Parts/PicasaAlbum", Model: part, Prefix: Prefix));
        }
 
        protected override DriverResult Editor(PicasaAlbumPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            updater.TryUpdateModel(part, Prefix, null, null);
            return Editor(part, shapeHelper);
        }
 
        protected override DriverResult Display(PicasaAlbumPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_PicasaAlbum",
                () => shapeHelper.Parts_PicasaAlbum(
                    UserID: part.UserID,
                    AlbumID: part.AlbumID,
                    AuthKey: part.AuthKey
                    ));
        }
    }
}

添加编辑器模板

添加名为 PicasaAlbum.cshtml 的可视化编辑器,并将其放在 /Views/EditorTemplates/Parts 中。这正是驱动程序指定的,因此必须匹配。

@using System.Web.Mvc.Html
@model Orchard.ExternalAlbum.Models.PicasaAlbumPart
<fieldset>
    <legend>@T("Picasa Album Fields")</legend>
 
    <div class="editor-field">
        @Html.LabelFor(x => x.UserID, T("User ID"))
        @Html.EditorFor(x => x.UserID)
        @Html.ValidationMessageFor(x => x.UserID)
    </div>
    <div class="hint">@T("Enter the Picasa User ID")</div>
 
    <div class="editor-field">
        @Html.LabelFor(x => x.AlbumID, T("Album ID"))
        @Html.EditorFor(x => x.AlbumID)
        @Html.ValidationMessageFor(x => x.AlbumID)
    </div>
    <div class="hint">@T("Enter the Picasa Album ID")</div>
 
    <div class="editor-field">
        @Html.LabelFor(x => x.AuthKey, T("Authorization Key"))
        @Html.EditorFor(x => x.AuthKey)
        @Html.ValidationMessageFor(x => x.AuthKey)
    </div>
    <div class="hint">@T("If the album is shared using a key, enter the Picasa Authorization Key (including trailing #)")</div>
 
</fieldset>

添加对以下项的引用
System.Web
System.Web.Mvc
System.Web.WebPages

此时,您可以创建一个自定义内容类型,挂接 PicasaAlbumPart,创建该内容项,然后编辑字段。由于这是一个新创建的对象,因此还没有与相册部分的存储层进行交互。我们需要让 Orchard 知道我们要持久化该部分。这是通过处理程序完成的。

添加处理程序类

为内容部分添加处理程序类(用于持久化)

using Orchard.ContentManagement.Handlers;
using Orchard.Data;
using Orchard.ExternalAlbum.Models;
 
namespace Orchard.ExternalAlbum.Handlers
{
    public class PicasaAlbumPartHandler : ContentHandler
    {
        public PicasaAlbumPartHandler(IRepository<PicasaAlbumPartRecord> repository)
        {
            Filters.Add(StorageFilter.For(repository));
        }
    }
}

添加显示模板

为名为 PicasaAlbum.cshtml 的部分添加可视化显示,并将其放在 /Views/Parts 中。  

@{
    Script.Require("JQueryPicasa").AtHead();
    Style.Require("JQueryPicasa");
    Script.Require("prettyPhoto").AtHead();
    Style.Require("prettyPhoto");
 
    // genrerate a unique id for the tag for reference
    string albumId = DateTime.Now.Ticks.ToString();
}
<article id="@albumId">
</article>
 
@using(Script.Foot()) {
<script type="text/javascript">
//<![CDATA[
    $(function() {
 
        var gallery = $("#@albumId");
 
        var userID = '@Model.UserID';
        var albumID = '@Model.AlbumID';
        var authKey = '@Model.AuthKey';
 
        // adds an unordered list of the gallery images, ready for prettyPhoto
        gallery.picasaGallery(userID, albumID, authKey, function (images) {
 
            var animation, picasaAlbum;
            picasaAlbum = "<ul class='picasa-album'>\n";
            $.each(images, function(i, element) {
                picasaAlbum += "  <li class='picasa-image'>\n";
                picasaAlbum += "    <a class='picasa-image-large' rel='prettyPhoto[picasa]' href='" + element.url + "'>\n";
                picasaAlbum += "      <img class='picasa-image-thumb' src='" + element.thumbs[0].url + "'/>\n";
                picasaAlbum += "    </a>\n";
                return picasaAlbum += "  </li>\n";
                                    });
            picasaAlbum += "</ul>";
            gallery.append(picasaAlbum);
            animation = function() {
                $(".picasa-image").each(function(index, element) {
                        return $(element).css("visibility", "visible").css("opacity", 0).delay(50 * index).animate({
                            opacity: 1}, 300);
                        });
                    return $(".picasa-album a", gallery).prettyPhoto({
                        slideshow: 5000,
                        autoplay_slideshow: false,
                        social_tools: ''
                    });
            };
            return setTimeout(animation, 300);
        });
 
    });
//]]>
</script>
}

正如您所看到的,文件以添加样式表和 Javascript 文件开始。如果您需要查看更多内容,请下载代码。

现在,您可能会想“但我希望以不同的方式可视化相册”。这在您的主题中是可能的。如果向主题添加 CSS 不够,您可以通过添加备用项来覆盖视图。

试试看 

首先,我们将设置一个没有 Picasa 相册的场景。然后,我们将添加该部分,填写内容,并向您展示最终结果。

设置演示场景 

假设您有一个 DVD 收藏,并希望在您的网站上展示/销售它们。在 Orchard 中,您可以为它创建一个内容类型。为了简单起见,我们创建一个 DVD 类型,并添加标题、一些文本和一个价格。对于演示,我们手动创建内容类型,但请记住,它也可以是一个由其他人开发的模块。 

 

CommonPart 会自动添加到每个内容类型中。

转到 /admin 并创建一个名为 DVD 的新类型。

 

该类型已创建,选择您要添加的部分。我们选择标题和正文,然后单击保存。

 

我们将价格作为字段添加。

 

单击“添加字段”并选择字段类型。

 

保存后,打开“price”字段并设置一些属性,然后再次单击保存。

 

我们已经完成了内容类型的规范。让我们添加一个 DVD。在左侧,“新建”下方,单击“DVD”并填写。然后您可以保存它并稍后发布,或者单击“立即发布”直接完成。

 

在左侧,选择“内容”,您的 DVD 将显示出来。选择“查看”进行查看。

 


这是使用默认主题的外观。

添加相册部分 

现在我们有一个包含现有内容的场景。我们现在将添加 Picasa 相册,以便网站上的每个 DVD 都可以附加一个 Picasa 相册。

 

转到“Content Type”页面,然后单击 DVD 类型的“edit”。



添加 PicasaAlbum 部分,然后单击保存。

 

返回“Contents”,为 DVD 选择“edit”。填写此相册的 Picasa 属性。
https://picasaweb.google.com/data/feed/base/user/111570895459168143940/albumid/5511777957142111713?alt=rss&kind=photo&hl=nl
然后单击“立即发布”。

 

再次转到“Contents”页面,查看带有 Picasa 相册的最终结果。

 

 

最终结果

现在我们看到了我们的 DVD,下方是 Picasa 相册。

 

您可以单击一张照片以显示更大的照片,并在相册中导航。

 

 

该模块可在中使用。您可以手动下载它,或直接从您的 Orchard 网站上的 /Packaging/Gallery/Modules 进行安装。

它是一个NuGet包,因此只是一个包含代码的 zip 文件。

结论

在阅读本文时,您可能已经注意到 Orchard 本身已经可以做很多事情了。这是真的,本文只介绍了一些基础知识,只是为了说明我的观点。当然,它还向您展示了如何用自己的部分进行扩展。也可以看看,看看可用的模块。

最棒的是,我们现在可以将 Picasa 相册“焊接”到任何内容类型。好好想想。如果您要构建和维护这样一个网站,那将是选择 Orchard 的一个重要原因! 

您怎么看?通过投票或评论表达您的意见。欢迎任何反馈。

奖励

我继续在代码中添加了一些不错的东西。只需几行代码,您就可以使任何内容部分成为小部件。这样您就不必将相册部分附加到某物上。现在您可以将 Picasa 相册作为小部件放置在网站的任何地方。

下一步

我计划添加导入和导出支持,这样在将内容迁移到另一个网站时,您的相册信息就不会丢失。

支持“Projections”只是一个想法,我不知道它是否有用。通过它,您可以创建一个视图(一个 projection),其中包含所有已附加相册部分的内容类型,并创建一个相册概述/索引。 

我将通过将其放在codeplex上来将此项目贡献给社区。任何人都可以为那里的模块做出贡献。

还有其他 Web 相册可用,例如可以将 SkyDrive 部分添加到项目中。给它投票 Smile | :)

历史

  • 版本 1   - 初始版本




© . All rights reserved.