因为公司业务需要,经过本人几个月尝试,使用Orchard CMS 开发一个简单的企业门户(地址是http://www.ubof.cn)。在刚开始接触Orchard CMS,对于个性化的网页布局不知道怎么定义,通过google的帮助终于找到比较符合Orchard的二次开发方法的解决方案,就是在Theme定义布局Widgets Layout。需要注意的是,因为要在Theme写C#代码,所以要用Orchard 命令提示创建Theme的解决方案(命令:codegen theme <theme-name> /CreateProject:true)。

当Theme的解决方案方案建立好后我们就可以开始定义自己的Widgets Layout,在这里我以自己网站上首页的一个产品展示Widgets为例子说明(此为新开发没有同步到发布站点),最后实现好的效果如下:

下面开始介绍代码的编写,第一步需要在刚建好的Theme的解决方案里,新建名称为Providers的文件夹,第二步在建好Providers文件里新建ProductsListLayoutForms的类,它必须实现Orchard里的 IFormProvider 接口,这个类主要作用是为Widgets 中HTML DOM的Id,class 提供配置表单,为以后修改class和id提供方便,具体代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.DisplayManagement;
using Orchard.Forms.Services;
using Orchard.Localization; namespace UBOfficeTheme.Providers.Layouts
{
public class ProductsListLayoutForms : IFormProvider
{
protected dynamic Shape { get; set; }
public Localizer T { get; set; } public ProductsListLayoutForms(IShapeFactory shapeFactory)
{
Shape = shapeFactory;
T = NullLocalizer.Instance;
} public void Describe(DescribeContext context)
{
Func<IShapeFactory, object> form =
shape =>
{
var f = Shape.Form(
Id: "ProductsListLayout",
_HtmlProperties: Shape.Fieldset(
Title: T("Html properties"),
_ListId: Shape.TextBox(
Id: "culture-id",
Name: "CultureId",
Title: T("culture id"),
Description: T("The id to provide on the culture div element."),
Classes: new[] { "textMedium", "tokenized" }
),
_ListClass: Shape.TextBox(
Id: "culture-classes",
Name: "CultureClasses",
Title: T("culture classes"),
Description: T("The class to provide on the culture div element."),
Classes: new[] { "textMedium", "tokenized" }
),
_CultureTitle: Shape.TextBox(
Id: "culturetitle-classes",
Name: "CultureTitleClasses",
Title: T("culture title classes"),
Description: T("The class to provide on the culturetitle div element."),
Classes: new[] { "textMedium", "tokenized" }
),
_ProductsId:Shape.TextBox(
Id: "products-id",
Name: "ProductsId",
Title: T("products id"),
Description: T("The id to provide on the products div element."),
Classes: new[] { "textMedium", "tokenized" }
),
_ProductsClass:Shape.TextBox(
Id: "products-classes",
Name: "ProductsClasses",
Title: T("products classes"),
Description: T("The class to provide on the products div element."),
Classes: new[] { "textMedium", "tokenized" }
)
)
); return f;
};
context.Form("ProductsListLayout", form);
}
}
}

第三步需要新建一个ProductsListLayout的类,此类必须实现Orchard里ILayoutProvider接口,此类的作用有两点,第一:为Projections(后面章节有说明)选择布局供时提供布名称和说明,第二:调用构造具体布局的实现,并提供class 和 id传参,具体代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.ContentManagement;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Projections.Descriptors.Layout;
using Orchard.Projections.Models;
using Orchard.Projections.Services; namespace UBOfficeTheme.Providers.Layouts
{
public class ProductsListLayout : ILayoutProvider
{
private readonly IContentManager _contentManager;
protected dynamic Shape { get; set; }
public Localizer T { get; set; } public ProductsListLayout(IShapeFactory shapeFactory, IContentManager contentManager)
{
_contentManager = contentManager;
Shape = shapeFactory;
T = NullLocalizer.Instance;
} public void Describe(DescribeLayoutContext describe)
{
describe.For("Html", T("Html"), T("Html Layouts"))
.Element("ProductsList", T("ProductsList"), T("Organizes content items in a ProductsList."),
DisplayLayout,
RenderLayout,
"ProductsListLayout"
);
} public dynamic RenderLayout(LayoutContext context, IEnumerable<LayoutComponentResult> layoutComponentResults)
{
string cultureId = context.State.CultureId;
string cultureClasses = context.State.CultureClasses;
string culturetitleClasses = context.State.CultureTitleClasses;
string productsId = context.State.ProductsId;
string productsClasses = context.State.ProductsClasses; IEnumerable<dynamic> shapes =
context.LayoutRecord.Display == (int)LayoutRecord.Displays.Content
? layoutComponentResults.Select(x => _contentManager.BuildDisplay(x.ContentItem, context.LayoutRecord.DisplayType))
: layoutComponentResults.Select(x => x.Properties); return Shape.ProductsList(
Id: cultureId,
Items: shapes,
pcultureClasses: new[] { cultureClasses },
pculturetitleClasses: new[] { culturetitleClasses },
productsClasses: new[] { productsClasses },
productsId : productsId
);
} public Func<LayoutContext, LocalizedString> DisplayLayout { get; set; }
}
}

第四步新建一个LayoutShapes 此类必须实现Orchard里IDependency接口,此类的作用为本Theme解决方案里的所有自定义布局提供具体实现,定义布局方法是有三点需要注意,第一:在方法前必须要加上 [Shape]的属性,第二点:方法名称必须和ProductsListLayout类中的RenderLayout方法返回语句中Shape调用名称相同。第三点:定义的方法参数除去dynamic Display, TextWriter Output, HtmlHelper Html由Orchard本身框架提供,其余一部由ProductsListLayout类中的RenderLayout方法返回语句中的Shape调用方法传递,一部是自己定义给class使用的字典集合。具体代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Orchard;
using Orchard.Autoroute.Models;
using Orchard.ContentManagement;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.UB.Portal.Models;
using Orchard.UI.Resources; namespace UBOfficeTheme.Providers.Layouts
{
public class LayoutShapes : IDependency
{
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IContentManager _contentManager;
private readonly IOrchardServices _orchardServices; public Localizer T { get; set; } public LayoutShapes(IOrchardServices orchardServices,
IWorkContextAccessor workContextAccessor, IContentManager contentManager)
{
_workContextAccessor = workContextAccessor;
T = NullLocalizer.Instance;
_contentManager = contentManager;
_orchardServices = orchardServices;
} [Shape]
public void ProductsList(dynamic Display, TextWriter Output, HtmlHelper Html, string Id,
IEnumerable<dynamic> Items,
IEnumerable<string> pcultureClasses,
IDictionary<string, string> pcultureAttr,
IEnumerable<string> pculturetitleClasses,
IDictionary<string, string> pculturetitleAttr,
IEnumerable<string> productsClasses,
IDictionary<string, string> productsAttr,
string productsId)
{
if (Items == null) return; var items = Items.ToList();
var itemsCount = items.Count;
if (itemsCount < 1) return; string baseUrl = _workContextAccessor.GetContext().CurrentSite.BaseUrl;
var cultureDivTag = GetTagBuilder("div", Id, pcultureClasses, pcultureAttr);
var cultureDivTitleTag = GetTagBuilder("div", string.Empty, pculturetitleClasses, pculturetitleAttr); var proddetailDivTag = GetTagBuilder("div", productsId, productsClasses, productsAttr);
var proddetailUlTag = GetTagBuilder("ul", productsId, productsClasses, productsAttr); Output.Write(cultureDivTag.ToString(TagRenderMode.StartTag));
Output.Write(cultureDivTitleTag.ToString(TagRenderMode.StartTag));
Output.Write("<h6 class='fl'><strong></strong>新品推荐<span>NEW PRODUCTS</span></h6>");
Output.Write(string.Format("<a href='{0}/{1}' class='fr'></a>", baseUrl, "executivedesk"));
Output.Write(cultureDivTitleTag.ToString(TagRenderMode.EndTag));
Output.Write("<div>");
Output.Write(proddetailDivTag.ToString(TagRenderMode.StartTag));
Output.Write("<a href=\"javascript:void(0)\" id=\"prev\" ></a>");
Output.Write("<div>");
Output.Write(proddetailUlTag.ToString(TagRenderMode.StartTag));
for (int i = 0; i < items.Count; i++)
{
var pictureField = ((Orchard.ContentManagement.ContentItem)items[i].ContentItem).Parts.SelectMany(p => p.Fields).Where(f => f.Name.Contains("picture")).SingleOrDefault();
var imglist = ((Orchard.MediaLibrary.Fields.MediaLibraryPickerField)pictureField).MediaParts.OrderBy(c => c.AlternateText).ToList();
foreach (var item in imglist)
{
Output.Write("<li><a href='{2}/{3}'><img src='{0}' alt='优比家具-{1}'/></a></li>", item.MediaUrl, item.Title, baseUrl,item.Caption);
}
}
Output.Write(proddetailUlTag.ToString(TagRenderMode.EndTag));
Output.Write("</div>");
Output.Write("<a href=\"javascript:void(0)\" id=\"next\" ></a>"); Output.Write(proddetailDivTag.ToString(TagRenderMode.EndTag));
Output.Write(cultureDivTag.ToString(TagRenderMode.EndTag)); } static TagBuilder GetTagBuilder(string tagName, string id, IEnumerable<string> classes, IDictionary<string, string> attributes)
{
var tagBuilder = new TagBuilder(tagName);
tagBuilder.MergeAttributes(attributes, false);
foreach (var cssClass in classes ?? Enumerable.Empty<string>())
tagBuilder.AddCssClass(cssClass);
if (!string.IsNullOrWhiteSpace(id))
tagBuilder.GenerateId(id);
return tagBuilder;
}
}
}

到这里代码编写全部完成。下一篇介绍配置。

在Orchard CMS Theme 用代码定义布局Widgets的更多相关文章

  1. 在Orchard CMS Theme 用代码定义布局Widgets 配置

    在上篇中主要详细的叙述了代码的编写,这一篇主要讲解配置.可能有人会有疑问,在上一篇的代码里只有对数据的展示部分的编写,并没有提供数据源.这就是Orchard的强大之处,数据源是通过在后台配置的,那有人 ...

  2. [Orchard CMS系列] 创建主题(Writing a new theme)

    本文需要对Orchard CMS有基本了解. 开启模块 code generation 创建新的主题工程骨架 Codegen theme MyTheme 创建主题样式 src\Orchard.Web\ ...

  3. Orchard CMS中如何打包不带源码的模块

    在Orchard CMS的官网已经提供了文档说明如何打包,但是如果使用它的打包方式,打好的nuget包是带源代码的.如果是为开源系统写模块,不需要关注源代码是否可见.但是如果是用Orchard CMS ...

  4. SharePoint 2013 图文开发系列之代码定义列表

    在SharePoint的开发中,用Visual Studio自定义列表是经常会用到的,因为很多时候,我们并不会手动创建列表,而手动创建列表在测试服务器和正式机之间同步字段,也很麻烦,所以我们经常用代码 ...

  5. WPF中通过代码定义模板

    WPF中可以再XAML中定义模板,也可以通过C#代码定义模板,通过代码可能更清楚的看清其逻辑,而且代码的好处就是可以随时动态的去操作,而在XAML中定义的一般都是静态的. //控件呈现的显示内容1(这 ...

  6. SharePoint 2013 设置自己定义布局页

    在SharePoint中.我们常常须要自己定义登陆页面.错误页面.拒绝訪问等:不知道大家怎样操作,曾经自己常常在原来页面改或者跳转.事实上SharePoint为我们提供了PowerShell命令,来改 ...

  7. 在 Windows Azure 网站 (WAWS) 上对 Orchard CMS 使用 Azure 缓存

    编辑人员注释: 本文章由 Windows Azure 网站团队的项目经理 Sunitha Muthukrishna 撰写. 如果您当前的 OrchardCMS 网站在 Windows Azure 网站 ...

  8. Orchard CMS -Migration文件更新后数据库不更新的问题 new properties not updating after migrationData migration is not working?

    Orchard CMS - new properties not updating after migrationData migration is not working? If your mapp ...

  9. cucumber java从入门到精通(2)用代码定义步骤

    cucumber java从入门到精通(2)用代码定义步骤 上一节里我们定义了feature文件,feature文件就是自然语言描述的用例文件,它有一定的章法,具体的潜规则是: 使用Feature关键 ...

随机推荐

  1. 404 Not Found 由来

    404 NOT FOUND! 抱歉,沒有找到您需要的文章!! 什么是 404 Not Found 404页面是网站必备的一个页面,它承载着用户体验与SEO优化的重任.404页面通常为用户访问了网站上不 ...

  2. python接口自动化-multipart/form-data上传图片

    前言 在提交表单操作的时候,经常会遇到图片上传的操作,图片上传是一个单独的接口,本篇以禅道为例,介绍如何上传图片 上传接口 1.以禅道上提交bug为例,在选择图片时,点确定按钮,就是上传图片了 2.用 ...

  3. POJ 1080 Human Gene Functions 【dp】

    题目大意:每次给出两个碱基序列(包含ATGC的两个字符串),其中每一个碱基与另一串中碱基如果配对或者与空串对应会有一个分数(可能为负),找出一种方式使得两个序列配对的分数最大 思路:字符串动态规划的经 ...

  4. bzoj 1432 [ZJOI2009]Function 思想

    [bzoj1432][ZJOI2009]Function Description Input 一行两个整数n; k. Output 一行一个整数,表示n 个函数第k 层最少能由多少段组成. Sampl ...

  5. linux date 格式化时间和日期

    [root@108test ~]# date -d today +"%Y-%m-%d" 2008-05-07   [root@108test ~]# date -d today + ...

  6. MongoDB基本管理命令操作

    1. 查看所有数据库: show dbs 或: show databases 注意: 该命令不会显示新创建的空数据库,若想显示需要向空数据库插入一些数据. MongoDB中默认的数据库为test,若果 ...

  7. Python()- 面向对象三大特性----继承

    继承: 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类(基类或超类),新建的类是所继承的类的(派生类或子类) 人类和狗 有相同的属性, 提取了一个__init__方法,在这 ...

  8. POJ 3013 【需要一点点思维...】【乘法分配率】

    题意: (这题明显感觉自己是英语渣) 给n个点从1到n标号,下面一行是每个点的权,另外给出m条边,下面是每条边的信息,两个端点+权值,边是无向边.你的任务是选出一些边,使这个图变成一棵树.这棵树的花费 ...

  9. MongoDB学习day04--NodeJs操作数据库增删改查

    一.在Nodejs中使用Mongodb Nodejs需要引入的包 npm install mongodb --save -dev 或者使用镜像 cnpm install mongodb --save ...

  10. MySQL查询count(*)、count(1)、count(field)的区别收集

    经过查询研究得出这个和MySQL中用什么引擎有关,比如InnoDB和MyISAM在处理这count(*).count(1).count(field)都有不同的方式,还有就是和版本都有关系,不同的版本会 ...