New Content items not appearing

Topics: Customizing Orchard, Writing modules
Aug 20, 2015 at 3:33 PM
Okay, last question for a while (I hope!)

I'm writing a custom module that is linked to the user part (PMUser) so I can display some extra information about the user, e.g. Job Title, phone number, country etc. So I created my part, added the user part to it, created the views and created a user through orchard. He (Mr Test) appears when I go into the Dashboard and click on content.

However, I then decided to take it a step further, and in a separate module I have a web API which I can post data too from another application - it quite happily(!) creates the user, creates my extra part with it's data in it, and links them together. I can see the records in the database (I'm using SQL server) and all looks well and good until I go into Orchard, go to the dashboard and look for my new person... They don't seem to be there. If, however, I click on Mr Test, and edit the link to use my newly added id, I can make them appear.

The only thing that seems to be different is that Mr Test (created through the UI) has the same userpartrecord id as my PMUser, but the ones created via the web API have successive id numbers....


Handler
 public class PMUserPartHandler : ContentHandler
    {
        public PMUserPartHandler(IRepository<PMUserPartRecord> repository)
        {
            Filters.Add(StorageFilter.For(repository));
            Filters.Add(new ActivatingFilter<PMUserPart>("PMUser"));
            Filters.Add(new ActivatingFilter<TitlePart>("PMUser"));
        }
    }
Driver
 public class PMUserPartDriver : ContentPartDriver<PMUserPart>
    {
        private readonly INotifier _notifier;
        private const string TemplateName = "Parts/PMUserPart";
        private readonly IPMUserService _pmuserService;

        public Localizer T { get; set; }

        protected override string Prefix
        {
            get
            {
                return "tbl";
            }
        }
        public PMUserPartDriver(INotifier notifier,
            IPMUserService pmuserService)
        {
            _notifier = notifier;
            T = NullLocalizer.Instance;
            _pmuserService = pmuserService;
        }

        protected override DriverResult Display(PMUserPart part, string displayType, dynamic shapeHelper)
        {
            //var model = BuildModel(part);

            return ContentShape("Parts_PMUserPart",
                () => shapeHelper.Parts_PMUserPart(
                    UserID: part.UserID,
                Gender: part.Gender,
                Company: part.Company,
                Division: part.Division,
                Street: part.Street,
              //omitted to save space, more fields go here
                UserName:part.User.UserName ,
                Email:part.User.Email 
            ));
              
        }

        protected override DriverResult Editor(PMUserPart part, dynamic shapeHelper)
        {
            return ContentShape("Parts_PMUserPart",
                    () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: part, Prefix: Prefix));
        }

        protected override DriverResult Editor(PMUserPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            if (updater.TryUpdateModel(part, Prefix, null, null))
            {
                _notifier.Information(T("PMUserPart edited successfully"));
            }
            else
            {
                _notifier.Error(T("Error during PMUserPart update!"));
            }
            return Editor(part, shapeHelper);
        }
Models
public class PMUserPart : ContentPart<PMUserPartRecord> {
        public int UserID { get { return Record.UserID; } set { Record.UserID = value; } }
        public string Gender { get { return Record.Gender; } set { Record.Gender = value; } }
        public string Company { get { return Record.Company; } set { Record.Company = value; } }
        public string Division { get { return Record.Division; } set { Record.Division = value; } }
   //again omitted to save space
        public UserPartRecord User { get { return Record.UserPartRecord; } set { Record.UserPartRecord = value; } }
    }

  public class PMUserPartRecord : ContentPartRecord {
        public virtual int UserID { get; set; }
        public virtual string Gender { get; set; }
        public virtual string Company { get; set; }
        public virtual string Division { get; set; }
        //omitted to save space
        public virtual UserPartRecord UserPartRecord { get; set; }
    }
Services
 public interface IPMUserService:IDependency
    {
        PMUserPart CreatePMUser(CreatePMUserParams createPMUserParams);
    }

 public class PMUserService : IPMUserService
    {
        private readonly IOrchardServices _orchardServices;
        private readonly IRepository<PMUserPartRecord> _pmuserRepository;
        private readonly IRepository<UserToPMUserPartRecord> _userToUserRepository;
        private readonly IRepository<UserPartRecord> _userRepository; 

        public PMUserService(IOrchardServices orchardServices, IRepository<PMUserPartRecord> pmuserRepository,
            IRepository<UserToPMUserPartRecord> userToUserRepository, IRepository<UserPartRecord> userRepository)
        {
            _orchardServices = orchardServices;
            Logger = NullLogger.Instance;
            _pmuserRepository = pmuserRepository;
            _userToUserRepository = userToUserRepository;
            _userRepository = userRepository;
        }

        public ILogger Logger { get; set; }
        public Localizer T { get; set; }

        public PMUserPart CreatePMUser(CreatePMUserParams createPMUserParams)
        {
            Logger.Information("CreatePMUser {0}", createPMUserParams.UserID.ToString());

            var pmu = _orchardServices.ContentManager.New("PMUser");
            var pmuser = pmu.As<PMUserPart>();

            pmuser.AsstEmail = createPMUserParams.AsstEmail;
            pmuser.AsstFax = createPMUserParams.AsstFax;
            pmuser.AsstFirstName = createPMUserParams.AsstFirstName;
            pmuser.AsstLastName = createPMUserParams.AsstLastName;
            pmuser.AsstPhone = createPMUserParams.AsstPhone;
            pmuser.City = createPMUserParams.City;         
        //omitted to save space
            pmuser.User = createPMUserParams.OrchardUser;

            pmu.As<TitlePart>().Title = createPMUserParams.UserName;

            _orchardServices.ContentManager.Create(pmuser); 

            return pmuser;
        }
}
Web API controller (in separate Module)
``` public class UsersController : ApiController
{
    private readonly IContentManager _contentManager;
    private readonly IOrchardServices _orchardServices;
    private readonly IMembershipService _membershipService;
    private readonly IRepository<UserRolesPartRecord> _userRolesRepository;
    private readonly IRepository<RoleRecord> _roleRepository;
    private readonly IRoleService _roleService;
    private readonly IRepository<PMUserPartRecord> _pMUserRepository;
    private readonly IRepository<UserToPMUserPartRecord> _userToPMUserRepository;
    private readonly IPMUserService _pmUserService;
    private readonly IRepository<UserPartRecord> _userRepository;

    public UsersController(IContentManager contentManager,
        IOrchardServices orchardServices,
        IMembershipService membershipService,
        IRepository<UserRolesPartRecord> userRolesRepository,
         IRepository<RoleRecord> roleRepository,
        IRoleService roleService,
        IRepository<PMUserPartRecord> pMUserRepository,
        IRepository<UserToPMUserPartRecord> userToPMUserRepository,
        IPMUserService pmUserService,
        IRepository<UserPartRecord> userRepository)
    {
        _contentManager = contentManager;
        _orchardServices = orchardServices;
        _membershipService = membershipService;
        _userRolesRepository = userRolesRepository;
        _roleRepository = roleRepository;
        _roleService = roleService;
        _pMUserRepository = pMUserRepository;
        _userToPMUserRepository = userToPMUserRepository;
        _pmUserService = pmUserService;
        _userRepository = userRepository;
    }

    public HttpResponseMessage Post([FromBody] JToken JsonString)
    {
        try
        {
            var vm = new System.Web.Script.Serialization.JavaScriptSerializer()
              .Deserialize<List<UserViewModel>>(JsonString.ToString());


            IEnumerable<IUser> users = _orchardServices.ContentManager
             .Query<UserPart, UserPartRecord>()
             .Where(u => u.NormalizedUserName == "admin")
             .List<IUser>();

            foreach (UserViewModel uvm in vm)
            {


                CreatePMUserParams cpmup = new CreatePMUserParams(uvm.Preferred1stName, 
                        uvm.AsstFax, uvm.AsstEmail, uvm.Location, uvm.LineMngrFirstName, uvm.LineMngrLastName, uvm.LineMngrEmail,
                        uvm.Level,uvm.SPU, uvm.Company,uvm.UserID, uvm.UserName,users, createdUser );

                var pmuseritem = _pmUserService.CreatePMUser(cpmup);

                _userToPMUserRepository.Create(
                    new UserToPMUserPartRecord
                    {
                        PMUserPartRecord = pmuseritem.Record,
                        UserPartRecord = item.As<UserPart>().Record
                    }
                 );


            }

            var response = Request.CreateResponse<List<UserViewModel>>(HttpStatusCode.Created, vm);
            return response;
        }
        catch (Exception)
        {
            throw new SystemException();
        }

    }
Aug 21, 2015 at 2:04 PM
I've got a little further with this, thanks to the wonderful article here by Sipke Schoorstra.

I've rejigged my models slightly as in his example of the customer being linked to the user, and redefined my service in a similar way, and my web API controller as well. So now I get both my user part and my PMUser Part having the same ID, but they still won;'t display when I go into the dashboard, click on content and filter it to see my new part....

Can't figure that last part out, so any assistance would be valued.
Developer
Aug 24, 2015 at 10:01 AM
This seems suspicious:
_userToPMUserRepository.Create(
                    new UserToPMUserPartRecord
                    {
                        PMUserPartRecord = pmuseritem.Record,
                        UserPartRecord = item.As<UserPart>().Record
                    }
                 );
Unless I misunderstood, you seem to be linking two part records together where you probably intend these parts to be part of the same content item.
If the goal is to extend the User content type with additional information (via PMUserPart), all you need to do is attach that part to the User type and let Orchard handle persistence of the part records, as you did correctly in PMUserService.
Aug 24, 2015 at 10:18 AM
It's very possible I've misunderstood this - I'm still feeling my way around the Orchard and the code behind it ;) But that is how I learn, by trying, screwing up, then fixing the screwed up code !

I've got to a point now where I have re-jigged the service and the controller so I can create a properly linked PMUser that has a user part attached to it. I now find that if I go into the dashboard and try to edit a user I've created with that controller, I can see the title part, the PMUser part, the User Part, but not the Roles part....

Service Code
  public PMUserPart CreatePMUser(CreatePMUserParams createPMUserParams)
        {
            Logger.Information("CreatePMUser {0}", createPMUserParams.UserID.ToString());

            var pmu = _orchardServices.ContentManager.New("PMUser");
            var pmuser = pmu.As<PMUserPart>();

            var newUser = pmu.As<UserPart>();
            newUser.UserName = createPMUserParams.OrchardUser.Username;
            newUser.CreatedUtc = _clock.UtcNow;
            newUser.Email = createPMUserParams.OrchardUser.Email;
            newUser.NormalizedUserName = createPMUserParams.OrchardUser.Username.ToLowerInvariant();
            newUser.HashAlgorithm = "PBKDF2";
            _membershipService.SetPassword(newUser, createPMUserParams.OrchardUser.Password);
            newUser.RegistrationStatus = UserStatus.Approved;
            newUser.EmailStatus = UserStatus.Approved;
            newUser.RegistrationStatus = UserStatus.Approved;
            newUser.EmailStatus = UserStatus.Approved;


            pmu.As<TitlePart>().Title = createPMUserParams.UserName;
            _orchardServices.ContentManager.Create(pmuser);
            if (createPMUserParams.OrchardRoles.Count() > 0)
            {
                foreach (string s in createPMUserParams.OrchardRoles)
                {
                    _userRolesRepository.Create(
                        new UserRolesPartRecord
                        {
                            Role = _roleService.GetRoleByName(s),
                            UserId = newUser.Id
                        }
                        );
                }
            }
            return pmuser;
        }
Controller code
   public HttpResponseMessage Post([FromBody] JToken JsonString)
        {

            try
            {
                var vm = new System.Web.Script.Serialization.JavaScriptSerializer()
                  .Deserialize<List<UserViewModel>>(JsonString.ToString());


                IEnumerable<IUser> users = _orchardServices.ContentManager
                 .Query<UserPart, UserPartRecord>()
                 .Where(u => u.NormalizedUserName == "admin")
                 .List<IUser>();

                foreach (UserViewModel uvm in vm)
                {

                    CreateUserParams cup = new CreateUserParams(uvm.UserName, uvm.Password, uvm.Email, "", "", true);
                  

                    CreatePMUserParams cpmup = new CreatePMUserParams(uvm.Preferred1stName, uvm.Gender, uvm.Division, uvm.JobTitle,
                            uvm.WorkPhone, uvm.WorkFax, uvm.WorkMobile, uvm.WorkPager, uvm.Street, uvm.Street1, uvm.Street2,
                            uvm.City, uvm.StateRegion, uvm.Country, uvm.ZipPostal, uvm.AsstFirstName, uvm.AsstLastName, uvm.AsstPhone,
                            uvm.AsstFax, uvm.AsstEmail, uvm.Location, uvm.LineMngrFirstName, uvm.LineMngrLastName, uvm.LineMngrEmail,
                            uvm.Level, uvm.SPU, uvm.Company, uvm.UserID, uvm.UserName, users, cup, uvm.Roles);

                    var pmuseritem = _pmUserService.CreatePMUser(cpmup);

                    pmuseritem.AsstEmail = cpmup.AsstEmail;
                    pmuseritem.AsstFax = cpmup.AsstFax;
                    pmuseritem.AsstFirstName = cpmup.AsstFirstName;
              // omitted to save space - more PMUser information
                    pmuseritem.ZipPostal = cpmup.ZipPostal;
                  
                }

                var response = Request.CreateResponse<List<UserViewModel>>(HttpStatusCode.Created, vm);
                return response;
            }
            catch (Exception)
            {
                throw new SystemException();
            }
        }
Migrations
  public int Create()
        {
            // Creating table tbl_PMUser_PMUserRecord
            SchemaBuilder.CreateTable("PMUserPartRecord",
                table => table
                    .ContentPartRecord()
                    .Column("UserID", DbType.Int32)
                    .Column("Gender", DbType.String)
                    .Column("Company", DbType.String)
                    .Column("Division", DbType.String)
                    .Column("Street", DbType.String)
                    .Column("Street1", DbType.String)
                    .Column("Street2", DbType.String)
                    .Column("City", DbType.String)
                    .Column("StateRegion", DbType.String)
                    .Column("Country", DbType.String)
                    .Column("ZipPostal", DbType.String)
                    .Column("WorkPhone", DbType.String)
                    .Column("WorkFax", DbType.String)
                    .Column("WorkMobile", DbType.String)
                    .Column("WorkPager", DbType.String)
                    .Column("AsstFirstName", DbType.String)
                    .Column("AsstLastName", DbType.String)
                    .Column("AsstPhone", DbType.String)
                    .Column("AsstFax", DbType.String)
                    .Column("AsstEmail", DbType.String)
                    .Column("JobTitle", DbType.String)
                    .Column("FirstNamePref", DbType.String)
                    .Column("SPU", DbType.String)
                    .Column("Level", DbType.String)
                    .Column("LineMngrFirstName", DbType.String)
                    .Column("LineMngrLastName", DbType.String)
                    .Column("LineMngrEmail", DbType.String)
                    .Column("Location", DbType.String)
                    .Column("UserPartRecord_Id", DbType.Int32 ) 
            );

            ContentDefinitionManager.AlterPartDefinition(
                typeof(PMUserPart).Name, cfg => cfg.Attachable());


    ContentDefinitionManager.AlterTypeDefinition(
               "PMUserPart", builder =>
                   builder
                  .WithPart("TitlePart")  
                  .WithPart("PMUserPart")
    .WithPart("UserPart")
 .WithPart("UserRolesPart")
                  .Creatable()
                  .Listable()
                   );

            return 1;
        }
Aug 24, 2015 at 11:39 AM
And just when I was about to go give in and try to find some chocolate...

Turns out it was my handler... I hadn't added a filter for the roles part ;)

modified working handle - in case this helps anyone else
 public PMUserPartHandler(IRepository<PMUserPartRecord> repository)
        {
            Filters.Add(StorageFilter.For(repository));
            Filters.Add(new ActivatingFilter<PMUserPart>("PMUser"));
            Filters.Add(new ActivatingFilter<TitlePart>("PMUser"));
            Filters.Add(new ActivatingFilter<UserPart>("PMUser"));
            Filters.Add(new ActivatingFilter<UserRolesPart>("PMUser"));
        }
Developer
Aug 25, 2015 at 11:54 AM
I think that's the best way to learn. :)
Glad to see you figured it out.