如何扩展Orchard

翻译自:
 
 
动态类型系统
Content item是Orchard中的原子, 比如blog post, pages, products, widgets
 
探索Content item原子
作为开发者,我们首先会想到Content item是一个类的实例(比如blog post类), 类中包含了property, method等. 实际的Content item不是由简单类型的属性等组成的, 而是由content part组成,这是Orchard中的重要概念.
 
一个blog post典型的由URL, title, date, rich text body, tags和comments这些parts组成. 但是这些parts不只是为blog post所用. 比如tag, rich text body也会被用在page上.
 
另外, CMS中的content type并不是一成不变的. Blog post以前是simple text, 但是很快就发展的更加复杂. 可以包含videos, podcasts或者image galleries. 甚至, 如果你在旅行, 你会在blog post中提供位置服务.
 
Content parts是解决变化的key, 需要位置服务, 那么只需要给blog post content item添加上mapping part. 这些不能有developer来做,而是admin来. 所以, 扩展不能是依赖于.net的一些类型的, 而是metadata-driven, 在运行时创建, 能够通过后台页面管理. 下图就是Orchard的content type编辑页.
 
 
 
Orchard扩展方法:
使用后台的Orchard Content type editor
界面能做的事情,也可以通过代码实现, 如下:
item.Weld(part);
 
上面的代码是在content item中动态的添加了part. 但这只是给instance添加了part, 如果我们想给该Content type都添加上part, 可以这样:
    ContentDefinitionManager.AlterTypeDefinition(
      "BlogPost", ctb => ctb.WithPart("MapPart")
    );
 
上面的方式也是实际上blog post content type的构建方式:
 
  ContentDefinitionManager.AlterTypeDefinition("BlogPost",
      cfg => cfg
        .WithPart("BlogPostPart")
        .WithPart("CommonPart", p => p
          .WithSetting("CommonTypePartSettings.ShowCreatedUtcEditor", "true"))
          .WithPart("PublishLaterPart")
          .WithPart("RoutePart")
          .WithPart("BodyPart")
      );
 
你可能注意到blog post content type中没有包含tags和comments. 的确是这样, orchard系统是通过其它方式来实现添加tags和comments的.
 
 
点菜
site有个初始化的xml文件, 在site setup的时候, 会根据这个配置来初始化content type, 下面是blog post的配置.
 
<BlogPost ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
  <CommentsPart />
  <TagsPart />
  <LocalizationPart />
</BlogPost>
 
 
创建Part
这是一个为添加keyword和description的part, 可以用来提高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" />
 
The Record
第一步是要解决如何存储这些信息到数据库. 严格的说, 并不是所有的part都需要record, 因为不是所有的part的数据都存在数据库中.
下面是我们用来存储keywords和description的MetaRecord:
public class MetaRecord : ContentPartRecord {
      public virtual string Keywords { get; set; }
      public virtual string Description { get; set; }
}
 
继承自ContentPartRecord不是必须的, 但是继承的话比较方便. 这个类有2个属性, 都是virtual的, 这是为了让orchard在运行的时候能够方便的生成proxy.
添加MetaHandler来实现数据库存储.
 
  public class MetaHandler : ContentHandler {
      public MetaHandler(IRepository<MetaRecord> repository) {
        Filters.Add(StorageFilter.For(repository));
      }
    }
 
对于数据库的修改migration
 
  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;
      }
    }
 
上面的代码, 第一部分是创建表, 并且和record匹配.
第二部分是说明任何content type都可以从Admin UI上attach MetaPart
 
The Part Class
实际的Part是另外一个类, 继承自ContentPart
    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; }
      }
    }
 
这里的这个Part做成了MetaRecord的代理类. 如果没有代理MetaRecord的属性, 也可以通过父类ContentPart的Record属性访问到.
 
任何使用了MetePart的Content item, 都能够方便的访问到Keywords和Description属性
var metaKeywords = item.As<MetaPart>().Keywords;
 
Parts也应该是你具体实现一些behavior的地方, 比如, 一个组装机器的part, 可以添加方法或者property来列举它的部件或者计算总价.
 
至于Part添加的这些behavior如何和用户交互,这就是drivers要做的事情.
 
The Driver
Content item中的part都有机会参与到request的生命周期中和在asp.net MVC controller起作用. 但是只能在request的部分生命周期中起作用, 而不是全部.
Content part driver扮演这个角色, 它不是一个完整意义上的controller, 没有route匹配到它的方法.
它是为一些事先定义好的events写响应的方法, 比如Display, Editor.
Driver继承自ContentPartDriver.
 
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;
}

 
这个driver实际上不是典型的, 因为大部分的drivers result是rendering, 这里要把meta part呈现到head的meta标签中.
HTML的head section是shared resource, 所以有一些特殊处理.
Orchard提供了API访问这些shared resource. 这里我使用了resource manager来设置metatags. resource manager会呈现实际的tags.
 
之所以这个方法返回null是因为它不需要呈现任何东西到页面上. 一般driver的方法需要返回一个称做shape的dynamic type, 类似于asp.net mvc中的 view model.
它是一个非常flexible的对象, 你能够接上任何东西, 匹配对应的template来呈现它, 并不需要创建一个特定的view model类
 
protected override DriverResult Editor(MetaPart part, dynamic shapeHelper) {
      return ContentShape("Parts_Meta_Edit",
        () => shapeHelper.EditorTemplate(
          TemplateName: "Parts/Meta",
          Model: part,
          Prefix: Prefix));
}
 
Editor方法是用来呈现改part editor UI的. 比较典型的是返回一个特殊的shape对象用来, 组合成edition UI.
 
driver中的最后一个方法是处理从editor发送回来的post请求:
 
protected override DriverResult Editor(MetaPart part, IUpdateModel updater, dynamic shapeHelper) {
    updater.TryUpdateModel(part, Prefix, null, null);
    return Editor(part, shapeHelper);
}
 
这个方法中的代码调用了TryUpdateModel自动地更新part数据. 当更新完成, 再调用Editor方法来返回同样的editor shape.
 
Rendering Shapes
 
通过调用所有的parts的drivers, Orchard能够创建一个shapes tree, 在整个request中创建一个庞大的动态的view model.
下一个任务是搞清楚如何通过templates来呈现这些shapes. 它是通过每个shape的名字(如果是editor方法, 就是Parts_Meta_Edit)在整个系统定义范围内寻找模板文件, 比如当前的theme和module的views文件夹.
这是非常重要的扩展点, 因为它允许你能够重写系统的呈现, 只需要你在你的theme中放入正确name的template 文件即可.
 
在Views\EditorTemplates\Parts下的我的module文件夹下, 我写了一个Meta.cshtml文件
 
   
@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>
 
所有的都是content
在我讲到其它扩展的时候, 我想提醒一旦你懂了content item type系统, 你就懂得了Orchard中最重要的部分. 很多系统中重要的部分都是定义成了content item.
比如, a user是content item, 这样我们能够方便的添加properties到profile modules中. 我们也有widget content item, 能够呈现到theme定义的zones中. 这是orchard中如何显示search form, blog archives, tag clouds和其它的sidebar UI的方式.
但最使人意外应该是site本身也使用了content items. orchard中的 site settings也是content items, 这也是orchard的优势所在. 当你想添加自己的site settings的时候, 你所要做的就只是再添加一个新的part到site content type中, 然后添加一个admin edition UI. 一个统一扩展的content type系统是一个非常好的概念.
 
 
打包Extensions
Extensions都是以modules的方法分发的, 和实现相关的是以themes分发.
典型的Theme是一堆图片, stylessheets和templates, 打包放到Themes文件夹下. 一般也有一个theme.txt文件来定义一些metadata信息.
类似的, module是在Modules文件夹下. 它也是一个asp.net mvc area, 包含了一些配置. 比如, 它需要一个module.txt来说明module的metadata。
作为一个大网站的area, module需要能够处理好和一些share resources的关系. 比如, routes必须定义在一个继承IRouteProvider的类中. Orchard会取所有modules的route来构建整个网站的route. 类似地, modules能够实现INavigationProvider来创建新的admin menu.
 
有意思的是, module的代码通常不是以编译好的二进制发布(虽然这是可行的). 这是一个有意的安排, 我们鼓励对于module的修改, 当你从gallery下载一个module的时候,你可以动手修改来满足你的需求.
可以自己动手修改代码是一些PHP CMS的强大之处, 比如Drupal, wordpress, 所以我们也想在Orchard中提供这个. 当你下载一个module的时候,你下载了它的源代码, 然后动态编译. 当你改动了代码, 改动立马生效,然后module会重新编译.
 
 
依赖注入
目前,我们都在关注type system扩展, 因为这是orchard modules的主要功能, 但是orchard有很多其它的扩展点. 我想说一些使用这个框架的重要的原则.
一个重要的是在这个松散的module系统中做正确的事情. 在Orchard中, 机会所有低级别至上的都是module. 甚至modules都是由某个module来管理的. 如果你想这些modules之间不互相耦合, 那么你实现的时候,就要注意解耦合.
实现这个的方法是使用依赖注入. 当你需要使用一个class的时候, 你不要直接实例化它, 那样会导致你直接依赖于那个class. 你需要创建一个接口来抽象那个class, 然后用这个接口对象作为构造参数.
 
 
   
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;
    }
 
通过上面的例子, 你依赖于接口,而不是class, 不管这个接口的实现如何, 你都不需要修改你的代码.
具体使用这个接口的代码中不需要具体指明如何实例化. 这样就实现了控制反转, 具体提供什么实例则是有framework来决定的.
 
当然, 这不限于orchard定义的接口. 任何module都能够提供它们自己的扩展点, 只需要指明继承自IDependency, 非常简单.
 
一些其它的扩展点
这里, 我只是揭示一下扩展点. Orchard中有很多的接口可以用于扩展. 更加夸张的说, orchard什么都不是, 只是一个可以随意扩展的engine. 系统中所有的东西都包装成了扩展.
 
在结束本文之前, 我想说一下系统中一些非常有用的接口. 由于没有更大的篇幅来说的更深, 但是可以指明一个方向, 你可以深入代码中去看看这些接口的用法.
 
  • IWorkContextAccessor 可以让你的代码来访问当前request的work context. work context提供了HttpContext, current layout, site configuration, user, theme和culture信息. 它也提供了方法取得某个接口的具体实现.
  • IContentManager提供了你要查询和管理content items的所有东西
  • IRepository<T> 提供了方法访问更低级别的数据, 如果IContentManager不够用, 可以用这个.
  • IShapeTableProvider 是用来提供shape的. 你可以再shapes中拦截事件, 可以修改shapes, 添加members, 在layout上移动shapes.
  • IBackgroundTask, IScheduledTask and IScheduled­TaskHandler 是一些接口来实现诸如在后台执行一些时间长,或者定期执行的任务的
  • IPermissionsProvider 运行你自己的module来定义你module中的权限.

<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据来源:中经数据库 主要指标110多个(全部都是纯粹的 市辖区 指标),大致是: GDP GDP增速 第一产业增加值占GDP比重 第二产业增加值占GDP比重 第三产业增加值占GDP比重 人均GDP 社会消费品零售总额 固定资产投资(不含农户) 新设外商投资企业数_外商直接投资 实际利用外资金额(美元) 一般公共预算收入 一般公共预算支出 一般公共预算支出_教育 一般公共预算支出_科学技术 金融机构人民币各项存款余额_个人储蓄存款 金融机构人民币各项存款余额 金融机构人民币各项贷款余额 规模以上工业企业单位数 规模以上工业企业单位数_内资企业 规模以上工业企业单位数_港澳台商投资企业 规模以上工业企业单位数_外商投资企业 规模以上工业总产值 规模以上工业总产值_内资企业 规模以上工业总产值_港澳台商投资企业 规模以上工业总产值_外商投资企业 规模以上工业企业流动资产合计 规模以上工业企业固定资产合计 规模以上工业企业利润总额 规模以上工业企业应交增值税 规模以上工业企业主营业务税金及附加 户籍人口数 年均户籍人口数 户籍人口自然增长率 第一产业就业人员占全部城镇单位就业人员比重 第二产业就业人员占全部城镇单位就业人员比重 第三产业就业人员占全部城镇单位就业人员比重 城镇非私营单位就业人员数 城镇非私营单位就业人员数_第一产业 城镇非私营单位就业人员数_第二产业 城镇非私营单位就业人员数_第三产业 城镇非私营单位就业人员数_农、林、牧、渔业 城镇非私营单位就业人员数_采矿业 城镇非私营单位就业人员数_制造业 城镇非私营单位就业人员数_电力、热力、燃气及水生产和供应业 城镇非私营单位就业人员数_建筑业 城镇非私营单位就业人员数_批发和零售业 城镇非私营单位就业人员数_交通运输、仓储和邮政业 城镇非私营单位就业人员数_住宿和餐饮业 城镇非私营单位就业人员数_信息传输、软件和信息技术服务业 城镇非私营单位就业人员数_金融业 城镇非私营单位就业人员数_房地产业 城镇非私营单位就业人员数_租赁和商务服务业 城镇非私营单位就业人员数_科学研究和技术服务业 城镇非私营单位就业人员数_水利、环境和公共设施管理业 城镇非私营单位就业人员数_居民服务、修理和其他服务业 城镇非私营单位就业人员数_教育 城镇非私营单位就业人员数_卫生和社会工作 城镇非私营单位就业人员数_文化、体育和娱乐业 城镇非私营单位就业人员数_公共管理、社会保障和社会组织 城镇非私营单位在岗职工平均人数 城镇就业人员数_私营企业和个体 城镇非私营单位在岗职工工资总额 城镇非私营单位在岗职工平均工资 城镇登记失业人员数 建成区面积 建设用地面积 建设用地面积_居住用地 液化石油气供气总量 液化石油气供气总量_居民家庭 人工煤气、天然气供气总量 人工煤气、天然气供气总量_居民家庭 液化石油气用气人口 人工煤气、天然气用气人口 城市公共汽电车运营车辆数 城市出租汽车运营车辆数 城市公共汽电车客运总量 道路面积 排水管道长度 建成区绿化覆盖面积 建成区绿化覆盖率 绿地面积 公园绿地面积 维护建设资金支出 土地面积 生活用水供水量 供水总量 全社会用电量 城乡居民生活用电量 工业生产用电量 房地产开发投资 房地产开发投资_住宅 限额以上批发和零售业法人单位数 限额以上批发和零售业商品销售总额 普通中学学校数 中等职业教育学校数 普通小学学校数 普通高等学校专任教师数 普通中学专任教师数 中等职业教育专任教师数 普通小学专任教师数 普通高等学校在校生数 普通中学在校生数 中等职业教育在校生数 普通小学在校生数 电视节目综合人口覆盖率 公共图书馆总藏量_图书 医疗卫生机构数_医院和卫生院 卫生人员数_执业(助理)医师 医疗卫生机构床位数_医院和卫生院 城镇职工基本养老保险参保人数 职工基本医疗保险参保人数 失业保险参保人数
机器学习是一种人工智能(AI)的子领域,致力于研究如何利用数据和算法让计算机系统具备学习能力,从而能够自动地完成特定任务或者改进自身性能。机器学习的核心思想是让计算机系统通过学习数据中的模式和规律来实现目标,而不需要显式地编程。 机器学习应用非常广泛,包括但不限于以下领域: 图像识别和计算机视觉: 机器学习在图像识别、目标检测、人脸识别、图像分割等方面有着广泛的应用。例如,通过深度学习技术,可以训练神经网络来识别图像中的对象、人脸或者场景,用于智能监控、自动驾驶、医学影像分析等领域。 自然语言处理: 机器学习在自然语言处理领域有着重要的应用,包括文本分类、情感分析、机器翻译、语音识别等。例如,通过深度学习模型,可以训练神经网络来理解和生成自然语言,用于智能客服、智能助手、机器翻译等场景。 推荐系统: 推荐系统利用机器学习算法分析用户的行为和偏好,为用户推荐个性化的产品或服务。例如,电商网站可以利用机器学习算法分析用户的购买历史和浏览行为,向用户推荐感兴趣的商品。 预测和预测分析: 机器学习可以用于预测未来事件的发生概率或者趋势。例如,金融领域可以利用机器学习算法进行股票价格预测、信用评分、欺诈检测等。 医疗诊断和生物信息学: 机器学习在医疗诊断、药物研发、基因组学等领域有着重要的应用。例如,可以利用机器学习算法分析医学影像数据进行疾病诊断,或者利用机器学习算法分析基因数据进行疾病风险预测。 智能交通和物联网: 机器学习可以应用于智能交通系统、智能城市管理和物联网等领域。例如,可以利用机器学习算法分析交通数据优化交通流量,或者利用机器学习算法分析传感器数据监测设备状态。 以上仅是机器学习应用的一部分,随着机器学习技术的不断发展和应用场景的不断拓展,机器学习在各个领域都有着重要的应用价值,并且正在改变我们的生活和工作方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值