Orchard CMS

Orchard 可扩展性

Bertrand Le Le

下载代码示例

大多数 Web 应用程序有很多共同点,但同时也表现出很大的分歧。 他们都有静态页面 ("使用条款""关于我们",依此类推)。 他们目前常见的布局中的内容。 他们有导航菜单。 有可能搜索、 注释、 评级和社会网络集成。 但有些博客、 一些卖书、 一些朋友保持联系和一些包含成百上千的参考文章对你最喜欢的技术。

内容管理系统 (CMS) 旨在提供通用的部分,而施加的正在建造的网站类型,对没有限制。 是一种微妙的练习中可扩展性。

果园 CMS 的创造者 (orchardproject.net) — 包括我 — 有选择大量依赖于组成与公约 》 的方法。 在本文中,我将介绍简单扩展,这应该是一个好的起点,为您自己的模块,系统的几个的例子。

动态类型系统

不管什么您使用来建设你的网站的 CMS,会有中央的内容实体经过不同的名称。 Drupal,在把它叫做一个节点,并在果园里,它是内容项。 原子的内容如博客张贴内容、 页面、 产品、 窗口小部件或状态更新内容项。 其中一些对应的 URL,而一些没有。 其架构差别很大,但他们的共同点是他们最小的内容单位在网站上。 还是他们吗?

分裂原子

作为开发人员,我们的第一反应是确定内容项目作为实例的类 (开机自检、 页面、 产品或构件),这是正确的程度。 类组成成员相同的方式 (字段、 属性和方法),内容类型 ("类"的内容项) 是自己的复合对象。 正在组成的简单属性本身就是类的类型,而不是,他们的内容部分,分别是你的内容的行为的原子组成。 这是一个重要的区别,我会用一个示例说明。

通常一篇博客文章组成的 URL、 标题、 日期、 丰富文本正文、 标记列表和列表中的用户注释。 这些部件都特定于一篇博客文章。 让一篇博客文章是特定的部件,不是随便一个部件组成。

大多数博客张贴内容有意见,但评论可能还用于商务站点中进行审查。 同样,标记是作为一种对任何内容项,而不仅仅是博客张贴内容进行分类的方式可能很有用的。 富文本形式的一篇文章是从一个页面的主体中没有什么不同。 还有很多。 它应该清楚此时单位的网站上的行为是小于内容的单位。

有关 CMSes 的另一个事实是不提前固定内容类型。 博客文章,用于简单的文本,但他们很快就更多。 他们现在经常包含视频、 播客或图像的画廊。 如果你博客你在世界各地的旅行,也许你要添加地理定位您的帖子。

再次,内容部分来救。 你需要的经度和纬度吗? 通过添加一个映射部分扩展博客发布类型 (我们模块库中,我们有几个可用:gallery.orchardproject。 净)。 当你认为这件事将最常执行此操作将部件添加到现有类型的网站,不是由开发人员的所有者,它迅速成为明显。 因此,它不可能只通过将复杂的属性添加到 Microsoft。NET 框架类型。 它已经并将元数据驱动,这样我们就可以建管理 UI 来做这件事发生在运行时 (请参见图 1)。

The Orchard Content Type Editor
图 1 果园内容类型编辑器

这是扩大果园的第一种方法:您可以创建和扩展的内容类型时将从管理用户界面。 当然,任何东西,你可以从管理用户界面,你可以从代码中,像这样:

item.Weld(part);

此代码动态焊接到内容项的一部分。 这是有趣的可能性,因为它允许在运行时动态扩展的类型的实例。 在动态语言中,这称为组合中,但这是一个几乎是闻所未闻的静态类型的语言如 C# 中的概念。 这将打开了新的可能性,但它并不是完全一样的事情,我们在做什么,从管理用户界面。 我们也希望能够将部件添加到类型一次而不是将其添加到每个实例,如下所示:

ContentDefinitionManager.AlterTypeDefinition(
  "BlogPost", ctb => ctb.WithPart("MapPart")
);

这实际上正是当初如何定义的博客帖子内容类型:

ContentDefinitionManager.AlterTypeDefinition("BlogPost",
  cfg => cfg
    .WithPart("BlogPostPart")
    .WithPart("CommonPart", p => p
      .WithSetting("CommonTypePartSettings.ShowCreatedUtcEditor", "true"))
      .WithPart("PublishLaterPart")
      .WithPart("RoutePart")
      .WithPart("BodyPart")
  );

您可能会注意此代码片断中标记和批注,似乎会丢失从博客张贴内容。 这是一个例子,仔细分离关注点。 博客模块实际上一无所知的标记和批注,最多的标记和模块做关于博客的评论。 第三方负责将它们放在一起。

美味食谱

在安装程序,方将执行,负责这种类型的任务。 它是该网站的初始配置的 XML 说明。 果园带有三个默认食谱:博客、 默认值、 核心。 下面的代码演示将标记和批注添加到博客的博客菜谱的部分:

<BlogPost ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
  <CommentsPart />
  <TagsPart />
  <LocalizationPart />
</BlogPost>

我展示了到目前为止的各种方式的内容部分可以组成内容项。 我下一步将解释如何,您可以构建您自己的零件。

建设部分

为了说明生成新部件的过程,我要去依赖我本行业模块的元功能的示例 (下载它从 bit.ly/u92283)。 元功能添加关键字和搜索引擎优化 (SEO) 用于描述属性 (请参阅图 2)。

The SEO Meta Data Editor
图 2 SEO 元数据编辑器

这些属性将呈现到页的文件头部分根据标准的元标记,搜索引擎的理解:

<meta content="Orchard is an open source Web CMS built on ASP.NET MVC."
  name="description" />
<meta content="Orchard, CMS, Open source" name="keywords" />

记录

首件之谜将数据将会保留到数据库的方式的说明。 严格来说,并不是所有的部件需要记录,因为并不是所有的部件果园数据库中存储的数据,但大多数人。 记录是只是一个普通的对象:

public class MetaRecord : ContentPartRecord {
  public virtual string Keywords { get; set; }
  public virtual string Description { get; set; }
}

MetaRecord 类是从 ContentPartRecord 派生的。 这并不是绝对必要的但它获取一些管道工程出的方式是绝对的方便。 类有两个字符串属性的关键字和描述。 这些属性必须将标记虚拟,所以框架可以"组合入"建立具体的类,将在运行时使用其自身的逻辑。

该记录的唯一责任是声明的持久性数据库,帮助下的存储机制,可以在 MetaHandler 中找到:

public class MetaHandler : ContentHandler {
  public MetaHandler(
    IRepository<MetaRecord> repository) {
    Filters.Add(
      StorageFilter.For(repository));
  }
}

存储也要进行初始化。 果园的早期版本被推断的数据库架构,从记录类,但猜测,只可以带你到目前为止,和这以来已更换更准确地迁移系统在架构修改已明确定义,如图所示,在图 3

图 3 显式定义的架构修改

public class MetaMigrations : DataMigrationImpl {
  public int Create() {
    SchemaBuilder.CreateTable("MetaRecord",
      table => table
        .ContentPartRecord()
        .Column("Keywords", DbType.String)
        .Column("Description", DbType.String)
    );
    ContentDefinitionManager.AlterPartDefinition(
      "MetaPart", cfg => cfg.Attachable());
    return 1;
  }
}

MetaRecord 类公约所创建的名称,系统将能够映射的 MetaRecord 表。 它通过调用 ContentPartRecord 方法,加上关键字和描述字符串列将自动映射对应属性的记录类公约所添加的内容部分记录有系统列。

迁移方法的第二部分表示新部件将从用户界面,管理员可连接到任何现有的内容类型。

创建方法始终代表中国最初始的迁移和通常返回 1,这是迁移的数字。 公约 》 是模块的在将来版本中的,开发人员可以添加 UpdateFromX 方法,其中 X 模块的被取代移民数目的方法进行操作。 该方法应返回对应的新移民编号架构的新移民数目。 该系统可平滑的、 独立的和灵活的升级的所有组件的系统中。

若要代表实际的一部分,一个单独的类使用,而现在我们看看。

Part 类

零件本身的表示形式是从 ContentPart <TRecord> 的另一类:

public class MetaPart : ContentPart<MetaRecord> {
  public string Keywords {
    get { return Record.Keywords; }
    set { Record.Keywords = value; }
  }
  public string Description {
    get { return Record.Description; }
    set { Record.Description = value; }
  }
}

部分充当代理记录的关键字和描述属性方便的但如果不记录和其属性将仍可通过 ContentPart 类的基类的公共记录属性。

已有 MetaPart 的部分的内容项的引用的任何代码将能够强类型访问关键字和描述属性的调用的方法,即果园的 CLR 类型系统中模拟转换操作:

var metaKeywords = item.As<MetaPart>().Keywords;

Part 类也是您将在其中执行的部分数据的任何特定的行为。 例如,一个复合产品可能会暴露的方法或属性访问其 subproducts 或计算总价格。

行为与相关的用户交互 (业务流程代码就会定期的 ASP。在控制器中找到净 MVC 应用程序) 是另一回事。 这是驱动程序是哪里来。

驱动程序

每个部分的内容项已获得机会参加请求生命周期和做好工作的 ASP。NET MVC 控制器,但它需要做到这一点的一部分,而不是做它的全部请求规模的规模。 内容部分驱动程序的缩微控制器的作用。 从路线有没有映射到其方法,它没有完整丰富的控制器。 相反,它是由处理明确定义的事件,如显示或编辑器的方法。 驱动程序只是从 ContentPartDriver 派生的类。

显示方法是获取称为果园需要以只读形式呈现部分时 (请参见图 4)。

图 4 显示驱动程序的方法准备一部分的呈现

protected override DriverResult Display(
  MetaPart part, string displayType, dynamic shapeHelper) {
  var resourceManager = _wca.GetContext().Resolve<IResourceManager>();
  if (!String.IsNullOrWhiteSpace(part.Description)) {
    resourceManager.SetMeta(new MetaEntry {
      Name = "description",
      Content = part.Description
    });
  }
  if (!String.IsNullOrWhiteSpace(part.Keywords)) {
    resourceManager.SetMeta(new MetaEntry {
      Name = "keywords",
      Content = part.Keywords
    });
  }
  return null;
}

此驱动程序其实是有点非典型,因为大多数驱动程序导致的简单的地方在渲染 (详细介绍,一会儿),而使其头部的元标记需要元部件。 HTML 文档的 head 部分是共享的资源,所以特别预防措施是必要的。 果园提供了 Api 来访问这些共享的资源,这就是你现在看到:我要通过资源管理器设置的元标记。 资源管理器将照顾实际标记呈现方式。

因为没有什么可呈现就地在此特定情形中,此方法返回 null。 大多数驱动程序方法而将返回调用的形状,它类似于在 ASP 中查看模型动态对象。NET MVC。 我就一会儿到形状回来时的时间来将它们转换为 HTML,但就目前而言,足以说明他们是非常灵活的对象,你可以坚持将会将呈现,而不必创建一个特殊的类,如下所示的模板相关的一切:

protected override DriverResult Editor(MetaPart part, dynamic shapeHelper) {
  return ContentShape("Parts_Meta_Edit",
    () => shapeHelper.EditorTemplate(
      TemplateName: "Parts/Meta",
      Model: part,
      Prefix: Prefix));
}

该编辑器方法负责准备部分用户界面编辑器的呈现方式。 通常,它返回一种特殊的形状适合于建筑复合版用户界面。

驱动程序的最后一件事是将处理的方法员额从编辑器:

protected override DriverResult Editor(MetaPart part,
  IUpdateModel updater, dynamic shapeHelper) {
  updater.TryUpdateModel(part, Prefix, null, null);
  return Editor(part, shapeHelper);
}

此方法中的代码调用 TryUpdateModel 自动更新,回发数据的部分。 一旦完成这项任务,它连接到编辑器的第一个方法以返回它所制造的编辑器形状相同。

绘制形状

通过对各部分的所有驱动程序调用中,果园是能够生成树的形状 — 整个请求大复合和动态视图模型。 其下一个任务是找出如何解决每一形状到模板,将能够使它们。 它不会因此而看每个形状 (Parts_Meta_Edit 的编辑方法的情况下) 的名称和尝试映射的文件系统,如当前主题的和模块的明确地浏览文件夹。 这是一个重要的可扩展性点,因为它使您能够通过只将具有适当名称的文件放到您的本地主题覆盖系统中的任何默认呈现。

在我的模块的 Views\EditorTemplates\Parts 文件夹中,我掉了一个名为 Meta.cshtml 的文件 (请参阅图 5)。

图 5 MetaPart 模板编辑器

@using Vandelay.Industries.Models
@model MetaPart
<fieldset>
  <legend>SEO Meta Data</legend>
  <div class="editor-label">
    @Html.LabelFor(model => model.Keywords)
  </div>
  <div class="editor-field">
    @Html.TextBoxFor(model => model.Keywords, new { @class = "large text" })
    @Html.ValidationMessageFor(model => model.Keywords)
  </div>
  <div class="editor-label">
    @Html.LabelFor(model => model.Description)
  </div>
  <div class="editor-field">
    @Html.TextAreaFor(model => model.Description)
    @Html.ValidationMessageFor(model => model.Description)
  </div>
</fieldset>

一切都是内容

我将移动到其他可扩展性的主题之前,我想提及的是,一旦你了解的内容项类型系统,根据您­站在果园中的最重要的概念。 许多重要的实体的系统被定义为内容的项目。 例如,用户是内容的项,可以将任意属性添加到他们的配置文件模块。 我们也有构件的内容项,可以呈现为一个主题定义的区域。 这是如何搜索表单中,博客存档,标签云及其它侧边栏果园创建用户界面。 但最令人惊讶的内容项使用可能会网站本身。 在果园里,这使得很有道理,一旦您了解如何在果园管理多重任务处理的有效内容项目站点设置。 如果您要添加您自己的站点设置,你要做的就是将部件添加到网站内容类型,并可以为它前面所概括的确切相同步骤后生成管理员版用户界面。 统一可扩展内容类型系统是一个极其丰富的概念。

包装扩展

我展示了如何构建您自己的零件的果园里,和我已经提到的模块和主题概念而不定义这些条款。 总括来说,他们是部署的单位在系统中。 扩展模块,分配和视觉外观分布式为主题。

主题通常是一堆图像、 样式和模板覆盖,打包成一个主题目录下的目录。 它还具有 theme.txt 清单文件的定义元数据如主题的作者的根源。

同样,一个模块是一个模块目录下的目录。 这也是 ASP。NET MVC 区,一些波折。 例如,它需要额外的 module.txt 清单文件声明模块,例如作者、 网站、 功能名称、 依赖项或版本号的某些元数据。

模块被只有一个领域的更大的站点,需要发挥好的几个共享资源。 例如,它使用的路由必须实施 IRouteProvider 一类的定义。 果园将生成完整的路由表,从什么由所有模块。 同样,模块可以有助于建立管理菜单中,通过实现 INavigationProvider 接口。

有趣的是,模块的代码通常不提供作为已编译的二进制文件 (尽管这是技术上可行)。 这是有意识的决定,我们设法鼓励模块黑客,在你开始从一个模块,您从库下载 tweak 它以满足您的特定需要。 能够进行任何修改代码是如 Drupal 或 WordPress,PHP CMS 的优点之一,我们想要提供相同的果园的灵活性。 当您下载一个模块时,您下载源代码,和该代码获取动态编译。 如果您更改了源文件中,获取捡起变化,重新编译的模块。

依赖注入

到目前为止,我一直致力于对一种类型的可扩展性,类型系统,因为表示大多数果园模块,但有很多其他的可扩展性点 — 太多,其实,我在这里列出。 我仍然想添加的一般原则,在整个框架的工作有关的几件事。

一个关键件高度模块化系统中的权利是松散耦合。 在果园里,几乎所有以上低级的管道是一个模块。 由一个模块管理甚至模块 ! 如果您希望从另一个独立工作,尽可能的这些模块 — 如果你想要的特点是可更换与另一个执行 — 你不能有硬的相关性。

要实现这一目标的一种关键方法是使用依赖注入。 当您需要使用从另一个类的服务时,你不要只是其实例化,如,将建立这类上硬的依赖项。 相反,您注入界面,此类实现,作为构造函数的参数 (请参见图 6)。

图 6 注通过构造函数参数的依赖关系

private readonly IRepository<ContentTagRecord> _contentTagRepository;
private readonly IContentManager _contentManager;
private readonly ICacheManager _cacheManager;
private readonly ISignals _signals;
public TagCloudService(
  IRepository<ContentTagRecord> contentTagRepository,
  IContentManager contentManager,
  ICacheManager cacheManager,
  ISignals signals)
  _contentTagRepository = contentTagRepository;
  _contentManager = contentManager;
  _cacheManager = cacheManager;
  _signals = signals;
}

您依赖项是对接口、 不是类和执行这种方式可以替换而无需更改您的代码。 特定实现的接口的获取注入不再是消费者的接口的决定。 控制逆在这里,并且它是作出该项决定的框架。

当然,这并不是限于果园定义的接口。 任何模块可以提供自己的可扩展性点只声明从 IDependency 派生的接口。 真的,就是这么简单。

一些其他的可扩展性点

仅仅是我已经在这里触及表面的可扩展性。 有许多接口,可在果园中用于创造性的方式来扩展系统。 甚至可以说果园基本上是只可扩展性的引擎。 可更换和可扩展的系统中的所有作品都。

结束这篇文章之前,我会提及一些最有用的接口中的系统,您可能需要签出。 我不走进这些深度,在这里有近足够的空间,但我可以给你的指针,和您可以转到代码,并按照使用的接口。 这是非常棒的顺便学习方式。

  • IWorkContextAccessor 使您的代码可以访问当前请求的工作上下文。 工作方面,反过来,提供访问 HttpContext,当前布局、 站点配置、 用户、 主题和文化。 它还提供了要实现接口,从这些地方你不依赖注入或您需要延迟施工后,直到它的设施。
  • IContentManager 提供了您可以查询和管理内容项目所需的一切。
  • IRepository <T> 提供对较低级别的数据访问方法,IContentManager 是不够的时候这些时间的访问。
  • IShapeTableProvider 使上飞形状操作情况下的一大笔钱。 基本上,你关于形状,事件挂钩,从中您可以创建备用的形状,在某些情况下使用,变换形状,将成员添加到他们,将它们周围移动布局中,等等。
  • IBackgroundTask、 IScheduledTask 和 IScheduled­TaskHandler 是,如果您需要在后台执行延迟或重复任务使用的接口。
  • IPermissionsProvider 使您的模块公开自己的权限。

了解更多信息

可扩展性的厂房,果园并提交所有政府必须提供在这有限的空间是一项挑战。 我希望是我给了你更多了解和深入到它的意愿。 我们对一个友好活泼的社区 orchard.codeplex.com/discussions ,会很乐意指导您和回答您的问题。 这么多的实施,和很多意见,探讨,这是一个伟大的机会,提供了重大的贡献。

Bertrand Le Roy · 开始他的专业开发人员职业生涯,在 1982 年当他发表了他的第一个视频游戏。他于二零零二年发表什么可能是第一要对 ASP 运行群集邮箱服务器。网。 一年后,他被雇由微软 ASP。网队和搬到美国。他一直在 ASP。净版本 2.0,4 及 ASP。NET AJAX ; 有助于使 jQuery 的官方的一部分。NET 开发人员的工具胸部 ; 与代表 Microsoft OpenAjax 联盟指导委员会。

多亏了以下技术专家审查这篇文章: Sebastien Ros