后台默认提供了 Content 的管理,但是,所有的内容类型揉杂在一起,而我们需要更深度的定制一些功能,比如,我们只想管理订单,又比如,我们需要对注册用户进行管理。本篇的内容包括:

1:自定义 admin menu;

2:使用 content query;

3:使用 paging utilities;

4:创建 list 与 edit;

一:扩展 Admin Menu

首先我们必须要实现 INavigationProvider 接口,我们创建 AdminMenu.cs:

using Orchard.Environment;
using Orchard.Localization;
using Orchard.UI.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Routing;

namespace TMinji.Shop
{
    public class AdminMenu : INavigationProvider
    {
        private readonly Work<RequestContext> _requestContextAccessor;

public string MenuName
        {
            get { return "admin"; }
        }

public AdminMenu(Work<RequestContext> requestContextAccessor)
        {
            _requestContextAccessor = requestContextAccessor;
            T = NullLocalizer.Instance;
        }

private Localizer T { get; set; }

public void GetNavigation(NavigationBuilder builder)
        {

var requestContext = _requestContextAccessor.Value;
            var idValue = (string)requestContext.RouteData.Values["id"];
            var id = 0;

if (!string.IsNullOrEmpty(idValue))
            {
                int.TryParse(idValue, out id);
            }

builder

// Image set
                .AddImageSet("webshop")

// "Webshop"
                .Add(item => item

.Caption(T("Webshop"))
                    .Position("2")
                    .LinkToFirstChild(false)

// "Customers"
                    .Add(subItem => subItem
                        .Caption(T("Customers"))
                        .Position("2.1")
                        .Action(new RouteValueDictionary
                        {
                            {"area", "TMinji.Shop"},
                            {"controller", "CustomerAdmin"},
                            {"action", "Index"}
                        })

.Add(T("Details"), i => i.Action("Edit", "CustomerAdmin", new { id }).LocalNav())
                        .Add(T("Addresses"), i => i.Action("ListAddresses", "CustomerAdmin", new { id }).LocalNav())
                        .Add(T("Orders"), i => i.Action("ListOrders", "CustomerAdmin", new { id }).LocalNav())
                    )

// "Orders"
                    .Add(subItem => subItem
                        .Caption(T("Orders"))
                        .Position("2.2")
                        .Action(new RouteValueDictionary
                        {
                            {"area", "TMinji.Shop"},
                            {"controller", "OrderAdmin"},
                            {"action", "Index"}
                        })
                    )
                );
        }
    }

}

代码中,指示我们需要加两个资源,一个是 menu.webshop.png,一个是 menu.webshop-admin.css,这是需要我们手动添加的,

Styles/menu.webshop-admin.css:

.navicon-webshop {
background-image:url('../images/menu.webshop.png') !important;
}
.navicon-webshop:hover {
background-position:0 -30px !important;
}

这个时候,我们的后台,就会变成这样了:

二:后台控制器

现在,添加后台的控制器。

Controllers/CustomerAdminController.cs:

using Orchard.ContentManagement;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Settings;
using Orchard.UI.Admin;
using Orchard.UI.Navigation;
using Orchard.UI.Notify;
using Orchard.Users.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
using TMinji.Shop.Services;
using TMinji.Shop.ViewModels;
using TMinji.Shop.Helpers;

namespace TMinji.Shop.Controllers
{
    [Admin]
    public class CustomerAdminController : Controller, IUpdateModel
    {
        private dynamic Shape { get; set; }
        private Localizer T { get; set; }
        private readonly ICustomerService _customerService;
        private readonly ISiteService _siteService;
        private readonly IContentManager _contentManager;
        private readonly INotifier _notifier;
        private readonly IOrderService _orderService;

public CustomerAdminController
        (
            ICustomerService customerService,
            IShapeFactory shapeFactory,
            ISiteService siteService,
            IContentManager contentManager,
            INotifier notifier,
            IOrderService orderService
        )
        {
            Shape = shapeFactory;
            T = NullLocalizer.Instance;
            _customerService = customerService;
            _siteService = siteService;
            _contentManager = contentManager;
            _notifier = notifier;
            _orderService = orderService;
        }

public ActionResult Index(PagerParameters pagerParameters, CustomersSearchVM search)
        {

// Create a basic query that selects all customer content items, joined with the UserPartRecord table
            var customerQuery = _customerService.GetCustomers().Join<UserPartRecord>().List();

// If the user specified a search expression, update the query with a filter
            if (!string.IsNullOrWhiteSpace(search.Expression))
            {

var expression = search.Expression.Trim();

customerQuery = from customer in customerQuery
                                where
                                    customer.FirstName.Contains(expression, StringComparison.InvariantCultureIgnoreCase) ||
                                    customer.LastName.Contains(expression, StringComparison.InvariantCultureIgnoreCase) ||
                                    customer.As<UserPart>().Email.Contains(expression)
                                select customer;
            }

// Project the query into a list of customer shapes
            var customersProjection = from customer in customerQuery
                                      select Shape.Customer
                                      (
                                        Id: customer.Id,
                                        FirstName: customer.FirstName,
                                        LastName: customer.LastName,
                                        Email: customer.As<UserPart>().Email,
                                        CreatedAt: customer.CreatedAt
                                      );

// The pager is used to apply paging on the query and to create a PagerShape
            var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters.Page, pagerParameters.PageSize);

// Apply paging
            var customers = customersProjection.Skip(pager.GetStartIndex()).Take(pager.PageSize);

// Construct a Pager shape
            var pagerShape = Shape.Pager(pager).TotalItemCount(customerQuery.Count());

// Create the viewmodel
            var model = new CustomersIndexVM(customers, search, pagerShape);

return View(model);
        }

public ActionResult Edit(int id)
        {
            var customer = _customerService.GetCustomer(id);
            var model = _contentManager.BuildEditor(customer);

// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
            return View((object)model);
        }

[HttpPost, ActionName("Edit")]
        public ActionResult EditPOST(int id)
        {
            var customer = _customerService.GetCustomer(id);
            var model = _contentManager.UpdateEditor(customer, this);

if (!ModelState.IsValid)
                return View(model);

_notifier.Add(NotifyType.Information, T("Your customer has been saved"));
            return RedirectToAction("Edit", new { id });
        }

public ActionResult ListAddresses(int id)
        {
            var addresses = _customerService.GetAddresses(id).ToArray();
            return View(addresses);
        }

public ActionResult ListOrders(int id)
        {
            var orders = _orderService.GetOrders(id).ToArray();
            return View(orders);
        }

bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)
        {
            return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
        }

void IUpdateModel.AddModelError(string key, LocalizedString errorMessage)
        {
            ModelState.AddModelError(key, errorMessage.Text);
        }
    }
}

Views/CustomerAdmin/Index.cshtml:

@model TMinji.Shop.ViewModels.CustomersIndexVM
@{
    Script.Require("ShapesBase");
    Layout.Title = T("Customers").ToString();
}

@using (Html.BeginForm("Index", "CustomerAdmin", FormMethod.Get))
{
    <fieldset class="bulk-actions">
        <label for="search">@T("Search:")</label>
        @Html.TextBoxFor(m => m.Search.Expression)
        <button type="submit">@T("Search")</button>
        <a href="@Url.Action("Index")">@T("Clear")</a>
    </fieldset>
}
<fieldset>
    <table class="items" summary="@T("This is a table of the customers in your application")">
        <colgroup>
            <col id="Col1" />
            <col id="Col2" />
            <col id="Col3" />
            <col id="Col4" />
            <col id="Col5" />
            <col id="Col6" />
        </colgroup>
        <thead>
            <tr>
                <th scope="col">&nbsp;&darr;</th>
                <th scope="col">@T("FirstName")</th>
                <th scope="col">@T("LastName")</th>
                <th scope="col">@T("Email")</th>
                <th scope="col">@T("Created")</th>
                <th scope="col">@T("Actions")</th>
            </tr>
        </thead>
        @foreach (var customer in Model.Customers)
        {
            <tr>
                <td>@customer.Id</td>
                <td>@customer.FirstName</td>
                <td>@customer.LastName</td>
                <td>@customer.Email</td>
                <td>@customer.CreatedAt</td>
                <td>
                    <div>
                        <a href="@Url.Action("Edit", new {customer.Id})" title="@T("Edit")">@T("Edit")</a>@T(" | ")
                        <a href="@Url.Action("List", "AddressAdmin", new {customerId = customer.Id})" title="@T("Addresses")">@T("Addresses")</a>@T(" | ")
                        <a href="@Url.Action("Delete", new {customer.Id, returnUrl = Request.Url.PathAndQuery})">@T("Delete")</a>
                    </div>
                </td>
            </tr>
        }
    </table>
    @Display(Model.Pager)
</fieldset>

Services/ICustomerService.cs:

public interface ICustomerService : IDependency
{
    CustomerPart CreateCustomer(string email, string password);
    AddressPart GetAddress(int customerId, string addressType);
    AddressPart CreateAddress(int customerId, string addressType);

IContentQuery<CustomerPart> GetCustomers();

CustomerPart GetCustomer(int id);

IEnumerable<AddressPart> GetAddresses(int customerId);

AddressPart GetAddress(int id);
}

Services/CustomerService.cs:

using Orchard;
using Orchard.ContentManagement;
using Orchard.Security;
using Orchard.Services;
using Orchard.Users.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TMinji.Shop.Models;

namespace TMinji.Shop.Services
{
    public class CustomerService : ICustomerService
    {
        private readonly IOrchardServices _orchardServices;
        private readonly IMembershipService _membershipService;
        private readonly IClock _clock;

public CustomerService(IOrchardServices orchardServices, IMembershipService membershipService, IClock clock)
        {
            _orchardServices = orchardServices;
            _membershipService = membershipService;
            _clock = clock;
        }

public CustomerPart CreateCustomer(string email, string password)
        {
            // New up a new content item of type "Customer"
            var customer = _orchardServices.ContentManager.New("Customer");

// Cast the customer to a UserPart
            var userPart = customer.As<UserPart>();

// Cast the customer to a CustomerPart
            var customerPart = customer.As<CustomerPart>();

// Set some properties of the customer content item (via UserPart and CustomerPart)
            userPart.UserName = email;
            userPart.Email = email;
            userPart.NormalizedUserName = email.ToLowerInvariant();
            userPart.Record.HashAlgorithm = "SHA1";
            userPart.Record.RegistrationStatus = UserStatus.Approved;
            userPart.Record.EmailStatus = UserStatus.Approved;

// Use IClock to get the current date instead of using DateTime.Now (see http://skywalkersoftwaredevelopment.net/orchard-development/api/iclock)
            customerPart.CreatedAt = _clock.UtcNow;

// Use Ochard's MembershipService to set the password of our new user
            _membershipService.SetPassword(userPart, password);

// Store the new user into the database
            _orchardServices.ContentManager.Create(customer);

return customerPart;
        }

public AddressPart GetAddress(int customerId, string addressType)
        {
            return _orchardServices.ContentManager.Query<AddressPart, AddressPartRecord>().Where(x => x.CustomerId == customerId && x.Type == addressType).List().FirstOrDefault();
        }

public AddressPart CreateAddress(int customerId, string addressType)
        {
            return _orchardServices.ContentManager.Create<AddressPart>("Address", x =>
            {
                x.Type = addressType;
                x.CustomerId = customerId;
            });
        }

public IContentQuery<CustomerPart> GetCustomers()
        {
            return _orchardServices.ContentManager.Query<CustomerPart, CustomerPartRecord>();
        }

public CustomerPart GetCustomer(int id)
        {
            return _orchardServices.ContentManager.Get<CustomerPart>(id);
        }

public IEnumerable<AddressPart> GetAddresses(int customerId)
        {
            return _orchardServices.ContentManager.Query<AddressPart, AddressPartRecord>().Where(x => x.CustomerId == customerId).List();
        }

public AddressPart GetAddress(int id)
        {
            return _orchardServices.ContentManager.Get<AddressPart>(id);
        }
    }

}

Helpers/StringExtensions.cs:

public static class StringExtensions
{
    public static string TrimSafe(this string s)
    {
        return s == null ? string.Empty : s.Trim();
    }

public static bool Contains(this string source, string value, StringComparison comparison)
    {
        return source.IndexOf(value, comparison) >= 0;
    }
}

ViewModels/CustomersIndexVM.cs:

public class CustomersIndexVM
{
    public IList<dynamic> Customers { get; set; }
    public dynamic Pager { get; set; }
    public CustomersSearchVM Search { get; set; }

public CustomersIndexVM()
    {
        Search = new CustomersSearchVM();
    }

public CustomersIndexVM(IEnumerable<dynamic> customers, CustomersSearchVM search, dynamic pager)
    {
        Customers = customers.ToArray();
        Search = search;
        Pager = pager;
    }
}

ViewModels/CustomersSearchVM.cs:

public class CustomersSearchVM
{
    public string Expression { get; set; }
}

Views/CustomerAdmin/Edit.cshtml:

@{
    Layout.Title = "Edit Customer";
}

@using (Html.BeginFormAntiForgeryPost())
{
    @Display(Model)
}

Drivers/CustomerDriver.cs:

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

var user = part.User;
    updater.TryUpdateModel(user, Prefix, new[] { "Email" }, null);

return Editor(part, shapeHelper);

}

Views/EditorTemplates/Parts/Customer.cshtml:

@using System.Web.Mvc.Html
@model TMinji.Shop.Models.CustomerPart
@{
    var user = Model.User;
}
<fieldset>
    <div class="editor-label">@Html.LabelFor(x => x.Title)</div>
    <div class="editor-field">@Html.TextBoxFor(x => x.Title, new { @class = "text" })</div>

<div class="editor-label">@Html.LabelFor(x => x.FirstName)</div>
    <div class="editor-field">
        @Html.TextBoxFor(x => x.FirstName, new { @class = "large text" })
        @Html.ValidationMessageFor(x => x.FirstName)
    </div>

<div class="editor-label">@Html.LabelFor(x => x.LastName)</div>
    <div class="editor-field">
        @Html.TextBoxFor(x => x.LastName, new { @class = "large text" })
        @Html.ValidationMessageFor(x => x.LastName)
    </div>

<div class="editor-label">@Html.LabelFor(x => user.Email)</div>
    <div class="editor-field">
        @Html.TextBoxFor(x => user.Email, new { @class = "large text" })
        @Html.ValidationMessageFor(x => user.Email)
    </div>
</fieldset>

最终效果:

Orchard模块开发全接触8:改造后台的更多相关文章

  1. Orchard模块开发全接触1:起步

    在<http://www.cnblogs.com/luminji/p/3831281.html>中简单介绍了 Orchard 的模块开发,接下来,我们需要做个更复杂的例子,Orchard ...

  2. Orchard模块开发全接触7:订单与支付之Event Bus

    在这部分,我们要完成的工作有: 1:将购物车内的商品变成真正的订单: 2:理解 父子及一对多关系: 3:写一个针对 Event Bus 的扩展点: 4:实现一个针对该扩展点的模拟的 支付服务: 一:创 ...

  3. Orchard模块开发全接触4:深度改造前台

    这里,我们需要做一些事情,这些事情意味着深度改造前台: 1:为商品增加 添加到购物车 按钮,点击后功能实现: 2:商品排序: 3:购物车预览,以及添加 结算 按钮: 4:一个显式 购物车中有*个 商品 ...

  4. Orchard模块开发全接触5:深度改造前台第二部分

    在这一部分,我们继续完善我们的购物车,我们要做以下一些事情: 1:完成 shoppingcart.cshtml: 2:让用户可以更新数量及从购物车删除商品: 3:创建一个 widget,在上面可以看到 ...

  5. Orchard模块开发全接触3:分类的实现及内容呈现(Display)

    一:分类用现有技术怎么实现? 实际就是创建 Query 和 Projection,如果不知道怎么做,参考:Orchard之在前台显式一个属于自己的列表(在这篇里,还进行了稍稍拓展),当然,基础的知道, ...

  6. Orchard模块开发全接触2:新建 ProductPart

    一:创建 Part 1:项目引用 Orchard.Framework: 2:创建 Models 文件夹: 3:在 Models 文件夹下创建类 ProductPartRecord,如下: public ...

  7. Orchard模块开发全接触6:自定义用户注册

    我们都知道 Orchard 的用户注册相当简单,现在,我们需要一个自定义的用户注册,现在,开始吧. 一:定义实体 Models/CustomerPartRecord.cs: public class ...

  8. Orchard 模块开发学习笔记 (1)

    创建模块 首先,打开Bin目录下的Orchard.exe 等到出现orchard>后, 看看命令列表中是否存在 codegen module 如果不存在,则需要先执行:feature enabl ...

  9. Odoo9.0模块开发全流程

    构建Odoo模块 模块组成 业务对象 业务对象声明为Python类, 由Odoo自己主动加载. 数据文件 XML或CSV文件格式, 在当中声明了元数据(视图或工作流).配置数据(模块參数).演示数据等 ...

随机推荐

  1. PhoneGap学习地址 / PhoneGap API介绍:Events

    http://blog.csdn.net/phonegapcn 事件类型: backbutton deviceready menubutton pause resume searchbutton on ...

  2. gym 101986

    A - Secret of Chocolate Poles 队友写的. 好像水水的. //#pragma GCC optimize(2) //#pragma GCC optimize(3) //#pr ...

  3. Java之路(一) 一切皆对象

    Java语言假设我们只进行面向对象的程序设计,即在开始用Java进行设计前,我们需要将思想切换到面向对象的世界中. 1.用引用操纵对象 每种编程语言都有自己操纵内存中元素的方式.是直接操纵元素还是用某 ...

  4. CSS3选择器01—CSS2.1部分选择器

    这篇文章主要用于存储CSS以及CSS3的选择器部分知识,以便日后查阅及记忆. 该内容分为两部分,第一部分为css选择器的一些基本知识.第二部分为CSS3新增加的选择器. 在开始之前,先简单介绍一下选择 ...

  5. 统计Mongo数组中相同对象的属性之和

    统计Mongo数组中相同对象的属性之和 需求 需要统计app端用户的行为,按天分表,存入mongo.每次用户进行操作的时候,将数据存入app本地,下次用户启动的时候,提交存入mongo,删除app本地 ...

  6. 干货: 可视化项目实战经验分享,轻松玩转 Bokeh (建议收藏)

    作者 | Will Koehrsen 翻译 | Lemon 译文出品 | Python数据之道 (ID:PyDataRoad) 本文通过一个项目案例,详细的介绍了如何从 Bokeh 基础到构建 Bok ...

  7. Java 多线程 - synchronize 关键字

    目录 Java 多线程 - synchronize 关键字 Java 多线程 - synchronize 关键字 学习自 http://cmsblogs.com/?p=2071 https://www ...

  8. windows下elasticsearch启动

    windows下启动elasticsearch,依赖于配置好JAVA_HOME D:\Program Files\Java\jdk1.7.0_71 命令行启动elasticsearch.bat即可实现 ...

  9. 【WIN10】移植opencc到WIN10-UWP,實現自己的繁簡轉換工具

    花了週末兩天時間,將opencc移植成WIN10-UWP可用的庫,並完成自己的繁簡轉換工具. 我的繁簡轉換工具下載地址為:https://www.microsoft.com/store/apps/9n ...

  10. ==与equals与hashCode的区别联系。

    前言:对于引用类型的变量,它涉及到两块内存,一块是堆中的内存,用于存放new出来的对象(就是对象的具体内容):另一块是栈内存,用来存放变量在堆内存中的地址. 1,“==” 判断两个变量是否是同一个变量 ...