how to get the added part properties?

Topics: Customizing Orchard
Aug 8, 2013 at 5:24 AM
for example,I create a employee content part,the employee has a realName properties,
and I attacted the employee part to the user part.
I want to add the employee realName properties in the orchard.User.Views.Admin.Index.cshtml file,

@foreach (var entry in Model.Users) {
        <tr>
            <td>
                <input type="hidden" value="@Model.Users[userIndex].User.Id" name="@Html.NameOf(m => m.Users[userIndex].User.Id)"/>
                <input type="checkbox" value="true" name="@Html.NameOf(m => m.Users[userIndex].IsChecked)"/>
            </td>
            <td>
                @if (entry.User.RegistrationStatus == UserStatus.Approved && entry.User.EmailStatus == UserStatus.Approved) { 
                <img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/online.gif") " alt="@T("Approved") " title="@T("User is approved") " /> 
                } else { 
                <img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/offline.gif") " alt="@T("Moderated") " title="@if (entry.User.EmailStatus == UserStatus.Approved) { @T("User is moderated") } else { @T("E-mail validation is pending") }" />
                } 
                @Html.ActionLink(entry.User.UserName, "Edit", new { entry.User.Id }) 
            </td>
             <td>
                @entry.User
            </td>
                <td>
               @entry.User.realName
            </td>
            <td>
                @entry.User.Email 
            </td>
            <td>
                @Html.ActionLink(T("Edit").ToString(), "Edit", new { entry.User.Id }) |
                @Html.ActionLink(T("Delete").ToString(), "Delete", new { entry.User.Id}, new { itemprop = "RemoveUrl UnsafeUrl" }) |
                @if (entry.User.RegistrationStatus == UserStatus.Pending) {
                    @Html.ActionLink(T("Approve").ToString(), "Approve", new { entry.User.Id })
                } else {
                    @Html.ActionLink(T("Disable").ToString(), "Moderate", new { entry.User.Id })
                }
                @if (entry.User.EmailStatus == UserStatus.Pending) { <text>|</text>
                    @Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id })
                } 
            </td>
        </tr>
but it didn't work,show @entry.User.realName is wrong. how to modify the wrong msg?
Developer
Aug 8, 2013 at 6:33 AM
You need to access your custom part to get to its properties.
If your part is called EmployeePart, attached to the User content type, then you access it like this:
var user = (dynamic)entry.User;
@user.EmployeePart.RealName
so it's: contentItem.[MyPart].[MyProperty].
Note: this only works when your content item is accessed via a variable typed as dynamic.
If you have a strongly typed content item and have a reference to your module providing your type, you can access your parts using the .As<TPart> extension method, e.g.:
@entry.User.As<EmployeePart>().RealName
Aug 8, 2013 at 7:41 AM
I tried the two method as you said,but is still didn't work.
Developer
Aug 8, 2013 at 8:48 AM
Can you show the code where you are accessing the part? Also provide the exact setup of your content type definition and content part class.
Aug 8, 2013 at 11:04 PM
I use the UserPersonalInformation module for test,
and the Migrations.cs code:
  public int Create() {
            SchemaBuilder.CreateTable("UserPersonalInformationRecord", table => table
                .ContentPartRecord()
                .Column("FirstName", DbType.String)
                .Column("MiddleName", DbType.String)
                .Column("LastName", DbType.String)
                
                .Column("NickName", DbType.String)
                .Column("DisplayName", DbType.String)
                .Column("Gender", DbType.String)
            );
            ContentDefinitionManager.AlterTypeDefinition("User",
                cfg => cfg
                    .WithPart(typeof(UserPersonalInformationPart).Name)
                );
            return 1;
        }
and I changed the orchard.Users.Views.Admin.Index.cshtml like that:
@model Orchard.Users.ViewModels.UsersIndexViewModel
@using Orchard.Users.Models;
@using Orchard.Users.ViewModels;


@{
    var userIndex = 0;

    Layout.Title = T("Users").ToString();
}

@using (Html.BeginFormAntiForgeryPost()) { 
    @Html.ValidationSummary()
    <div class="manage">@Html.ActionLink(T("Add a new user").ToString(), "Create", new { }, new { @class = "button primaryAction" })</div>

    <fieldset class="bulk-actions">
        <label for="publishActions">@T("Actions:")</label>
        <select id="publishActions" name="@Html.NameOf(m => m.Options.BulkAction)">
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.None, T("Choose action...").ToString())
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Approve, T("Approve").ToString()) 
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Disable, T("Disable").ToString())
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.ChallengeEmail, T("Send challenge E-mail").ToString())
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Delete, T("Delete").ToString())
        </select>
        <button type="submit" name="submit.BulkEdit" value="@T("Apply")">@T("Apply")</button>
    </fieldset>
    <fieldset class="bulk-actions">
        @Html.TextBoxFor(m => m.Options.Search, new { @class = "text" })        
        <label for="filterResults">@T("Filter:")</label>
        <select id="filterResults" name="@Html.NameOf(m => m.Options.Filter)">
            @Html.SelectOption(Model.Options.Filter, UsersFilter.All, T("All Users").ToString())
            @Html.SelectOption(Model.Options.Filter, UsersFilter.Approved, T("Approved Users").ToString())
            @Html.SelectOption(Model.Options.Filter, UsersFilter.Pending, T("Pending Users").ToString())
            @Html.SelectOption(Model.Options.Filter, UsersFilter.EmailPending, T("Pending Emails").ToString())
        </select>
        <label for="sortResults">@T("Sort by:")</label>
        <select id="sortResults" name="@Html.NameOf(m => m.Options.Order)">
            @Html.SelectOption(Model.Options.Order, UsersOrder.Name, T("Name").ToString())
            @Html.SelectOption(Model.Options.Order, UsersOrder.Email, T("Email").ToString())
        </select>

        <button type="submit" name="submit.Filter" value="@T("Filter")">@T("Filter")</button>
    </fieldset>
    <fieldset>
        <table class="items">
            <thead>
                <tr>
                    <th scope="col">&nbsp;&darr;</th>
                    <th scope="col">@T("Name")</th>
                    <th scope="col">@T("Email")</th>
                     <th scope="col">@T("DisplayName")</th>
                    <th scope="col">@T("Actions")</th>
                </tr>
            </thead>
            @foreach (var entry in Model.Users)
            { 
            <tr>
                <td>
                    <input type="hidden" value="@Model.Users[userIndex].User.Id" name="@Html.NameOf(m => m.Users[userIndex].User.Id)"/>
                    <input type="checkbox" value="true" name="@Html.NameOf(m => m.Users[userIndex].IsChecked)"/>
                </td>
                <td>
                    @if (entry.User.RegistrationStatus == UserStatus.Approved && entry.User.EmailStatus == UserStatus.Approved) { 
                    <img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/online.gif") " alt="@T("Approved") " title="@T("User is approved") " /> 
                    } else { 
                    <img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/offline.gif") " alt="@T("Moderated") " title="@if (entry.User.EmailStatus == UserStatus.Approved) { @T("User is moderated") } else { @T("E-mail validation is pending") }" />
                    } 
                    @Html.ActionLink(entry.User.UserName, "Edit", new { entry.User.Id }) 
                </td>
                
                <td>
                    @entry.User.Email 
                </td>
                  <td>
                    @entry.User.As<UserPersonalInformationPart>().DisplayName
                </td>
                <td>
                    @Html.ActionLink(T("Edit").ToString(), "Edit", new { entry.User.Id }) |
                    @Html.ActionLink(T("Delete").ToString(), "Delete", new { entry.User.Id}, new { itemprop = "RemoveUrl UnsafeUrl" }) |
                    @if (entry.User.RegistrationStatus == UserStatus.Pending) {
                        @Html.ActionLink(T("Approve").ToString(), "Approve", new { entry.User.Id })
                    } else {
                        @Html.ActionLink(T("Disable").ToString(), "Moderate", new { entry.User.Id })
                    }
                    @if (entry.User.EmailStatus == UserStatus.Pending) { <text>|</text>
                        @Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id })
                    } 
                </td>
            </tr>
                    userIndex++;
            }
        </table>

    @Display(Model.Pager)

    </fieldset>
} 
the wrong msg:
"CS1502: 与“System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)”最匹配的重载方法具有一些无效参数"
行 73:                 </td>
行 74:                   <td>
行 75:                     @entry.User.As<UserPersonalInformationPart>().DisplayName
行 76:                 </td>
行 77:                 <td>
thank you very muck
Aug 9, 2013 at 1:36 AM
I have resolve my problem by change some code,
first, in order to use the As method,you need to add "@using Orchard.ContentManagement" in orchard.Users.Views.Admin.Index.cshtml.
then ,because the model in Orchard.Users.ViewModels.UsersIndexViewModel UserEntry is a UserPartRecord, not a UserPart, so I change it to UserPart.
so the code need to change in Orchard.Users.Controllers.AdminController.cs of the Index method. the final code is:
orchard.Users.Views.Admin.Index.cshtml:
@model Orchard.Users.ViewModels.UsersIndexViewModel
@using Orchard.Users.Models;
@using Orchard.Users.ViewModels;

@using Orchard.ContentManagement



@{
    var userIndex = 0;

    Layout.Title = T("Users").ToString();

 
}

@using (Html.BeginFormAntiForgeryPost()) { 
    @Html.ValidationSummary()
    <div class="manage">@Html.ActionLink(T("Add a new user").ToString(), "Create", new { }, new { @class = "button primaryAction" })</div>

    <fieldset class="bulk-actions">
        <label for="publishActions">@T("Actions:")</label>
        <select id="publishActions" name="@Html.NameOf(m => m.Options.BulkAction)">
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.None, T("Choose action...").ToString())
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Approve, T("Approve").ToString()) 
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Disable, T("Disable").ToString())
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.ChallengeEmail, T("Send challenge E-mail").ToString())
            @Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Delete, T("Delete").ToString())
        </select>
        <button type="submit" name="submit.BulkEdit" value="@T("Apply")">@T("Apply")</button>
    </fieldset>
    <fieldset class="bulk-actions">
        @Html.TextBoxFor(m => m.Options.Search, new { @class = "text" })        
        <label for="filterResults">@T("Filter:")</label>
        <select id="filterResults" name="@Html.NameOf(m => m.Options.Filter)">
            @Html.SelectOption(Model.Options.Filter, UsersFilter.All, T("All Users").ToString())
            @Html.SelectOption(Model.Options.Filter, UsersFilter.Approved, T("Approved Users").ToString())
            @Html.SelectOption(Model.Options.Filter, UsersFilter.Pending, T("Pending Users").ToString())
            @Html.SelectOption(Model.Options.Filter, UsersFilter.EmailPending, T("Pending Emails").ToString())
        </select>
        <label for="sortResults">@T("Sort by:")</label>
        <select id="sortResults" name="@Html.NameOf(m => m.Options.Order)">
            @Html.SelectOption(Model.Options.Order, UsersOrder.Name, T("Name").ToString())
            @Html.SelectOption(Model.Options.Order, UsersOrder.Email, T("Email").ToString())
        </select>

        <button type="submit" name="submit.Filter" value="@T("Filter")">@T("Filter")</button>
    </fieldset>
    <fieldset>
        <table class="items">
            <thead>
                <tr>
                    <th scope="col">&nbsp;&darr;</th>
                    <th scope="col">@T("Name")</th>
                    <th scope="col">@T("Email")</th>
                     <th scope="col">@T("DisplayName")</th>
                    <th scope="col">@T("Actions")</th>
                </tr>
            </thead>
            @foreach (var entry in Model.Users)
            {
                var d = entry.User.As<UserPersonalInformation.Models.UserPersonalInformationPart>().DisplayName;
            <tr>
                <td>
                    <input type="hidden" value="@Model.Users[userIndex].User.Id" name="@Html.NameOf(m => m.Users[userIndex].User.Id)"/>
                    <input type="checkbox" value="true" name="@Html.NameOf(m => m.Users[userIndex].IsChecked)"/>
                </td>
                <td>
                    @if (entry.User.RegistrationStatus == UserStatus.Approved && entry.User.EmailStatus == UserStatus.Approved)
                    { 
                    <img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/online.gif") " alt="@T("Approved") " title="@T("User is approved") " /> 
                    }
                    else
                    { 
                    <img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/offline.gif") " alt="@T("Moderated") " title="@if (entry.User.EmailStatus == UserStatus.Approved)
                                                                                                                                             { @T("User is moderated") }
                                                                                                                                             else
                                                                                                                                             { @T("E-mail validation is pending") }" />
                    } 
                    @Html.ActionLink(entry.User.UserName, "Edit", new { entry.User.Id }) 
                </td>
                
                <td>
                    @entry.User.Email 
                </td>
                  <td>
                
                 @d
                </td>
                <td>
                    @Html.ActionLink(T("Edit").ToString(), "Edit", new { entry.User.Id }) |
                    @Html.ActionLink(T("Delete").ToString(), "Delete", new { entry.User.Id}, new { itemprop = "RemoveUrl UnsafeUrl" }) |
                    @if (entry.User.RegistrationStatus == UserStatus.Pending) {
                        @Html.ActionLink(T("Approve").ToString(), "Approve", new { entry.User.Id })
                    } else {
                        @Html.ActionLink(T("Disable").ToString(), "Moderate", new { entry.User.Id })
                    }
                    @if (entry.User.EmailStatus == UserStatus.Pending) { <text>|</text>
                        @Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id })
                    } 
                </td>
            </tr>
                    userIndex++;
            }
        </table>

    @Display(Model.Pager)

    </fieldset>
} 
Orchard.Users.ViewModels.UsersIndexViewModel.cs
using System.Collections.Generic;
using Orchard.Users.Models;

namespace Orchard.Users.ViewModels {

    public class UsersIndexViewModel  {
        public IList<UserEntry> Users { get; set; }
        public UserIndexOptions Options { get; set; }
        public dynamic Pager { get; set; }
    }

    public class UserEntry {
        public UserPart User { get; set; }
        public bool IsChecked { get; set; }
    }
    

    public class UserIndexOptions {
        public string Search { get; set; }
        public UsersOrder Order { get; set; }
        public UsersFilter Filter { get; set; }
        public UsersBulkAction BulkAction { get; set; }
    }

    public enum UsersOrder {
        Name,
        Email
    }

    public enum UsersFilter {
        All,
        Approved,
        Pending,
        EmailPending
    }

    public enum UsersBulkAction {
        None,
        Delete,
        Disable,
        Approve,
        ChallengeEmail
    }
}
and the Orchard.Users.Controllers.AdminController.cs 's index method:
    public ActionResult Index(UserIndexOptions options, PagerParameters pagerParameters) {
            if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list users")))
                return new HttpUnauthorizedResult();

            var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);

            // default options
            if (options == null)
                options = new UserIndexOptions();

            var users = Services.ContentManager
                .Query<UserPart, UserPartRecord>();

            switch (options.Filter) {
                case UsersFilter.Approved:
                    users = users.Where(u => u.RegistrationStatus == UserStatus.Approved);
                    break;
                case UsersFilter.Pending:
                    users = users.Where(u => u.RegistrationStatus == UserStatus.Pending);
                    break;
                case UsersFilter.EmailPending:
                    users = users.Where(u => u.EmailStatus == UserStatus.Pending);
                    break;
            }

            if(!String.IsNullOrWhiteSpace(options.Search)) {
                users = users.Where(u => u.UserName.Contains(options.Search) || u.Email.Contains(options.Search));
            }

            var pagerShape = Shape.Pager(pager).TotalItemCount(users.Count());

            switch (options.Order) {
                case UsersOrder.Name:
                    users = users.OrderBy(u => u.UserName);
                    break;
                case UsersOrder.Email:
                    users = users.OrderBy(u => u.Email);
                    break;
            }

            var results = users
                .Slice(pager.GetStartIndex(), pager.PageSize)
                .ToList();

            var model = new UsersIndexViewModel {
                Users = results
                    .Select(x => new UserEntry { User = x })
                    .ToList(),
                    Options = options,
                    Pager = pagerShape
            };

            // maintain previous route data when generating page links
            var routeData = new RouteData();
            routeData.Values.Add("Options.Filter", options.Filter);
            routeData.Values.Add("Options.Search", options.Search);
            routeData.Values.Add("Options.Order", options.Order);

            pagerShape.RouteData(routeData);
            
            return View(model);
        }
Aug 9, 2013 at 1:45 AM
Edited Aug 9, 2013 at 1:46 AM
thanks to sfmskywalker very muck. some suggestion: would you change the code of the Orchard.Users.ViewModels.UsersIndexViewModel.cs and Orchard.Users.Controllers.AdminController.cs? so when someone want to add some additional properties in the orchard.Users.Views.Admin.Index.cshtml,he didn't need to change the source code of orchard,just change the orchard.Users.Views.Admin.Index.cshtml. thanks again.
Developer
Aug 12, 2013 at 3:58 AM
I'm afraid I am not going to do that. If you wish to customize the users overview without changing the core code, I would advise to copy the code to your own module and have your routes override the ones from Orchard.Users.