Viewing Roles for Customer Users

Topics: Customizing Orchard, Writing modules
Dec 18, 2013 at 5:31 PM
I am creating my own users, with custom Roles. When I view an Orchard-created User on the Users Admin page, all of the possible roles are listed in edit mode. However, when I edit one of my users, only the User Name, Email, and Owner are listed. No Roles.

I know that the roles exist for these users, because I can see them in the database. Why am I not able to see the list of possible roles in edit mode? I've stepped through the debugger, but I can't figure out what's missing.
Dec 18, 2013 at 6:25 PM
How do you create the users, by code? Are they of content type "User", or did you create your own type with UserPart?


Dec 18, 2013 at 8:14 PM
kassobasi wrote:
How do you create the users, by code? Are they of content type "User", or did you create your own type with UserPart?
I have a ContactPart/ContactPartRecord, with a UserPart. All created by code.
Dec 18, 2013 at 8:21 PM
You need to have a UserRolesPart attached to your content type, do you have it? It's done for User content type in UserRolesPartHandler class using an ActivatingFilter.


Dec 18, 2013 at 8:37 PM
kassobasi wrote:
You need to have a UserRolesPart attached to your content type, do you have it? It's done for User content type in UserRolesPartHandler class using an ActivatingFilter.
I'm not quite following. Here's a more complete description of what I've got:

1) I have a ContactPart, which has:
...
        public UserPart User {
            get { return this.As<UserPart>(); }
        }
...
2) I then have a CustomerPart, which has:
...
        public ContactPart Contact {
            get { return this.As<ContactPart>(); }
        }
...
3) Are you saying I should have an additional property in ContactPart:
...
        public UserRolesPart UserRolesPart {
            get { return this.As<UserRolesPart>(); }
        }
...
4) And I am storing these as Records in the database. Do I need to modify my Migrations.cs class to this:
...
            ContentDefinitionManager.AlterTypeDefinition("Contact",
                type => type
                    .WithPart("ContactPart")
                    .WithPart("UserPart")
                    .WithPart("UserRolesPart")  <<<<<<
            );
...
5) And do I need to modify ContactPartHandler:
...
        public ContactPartHandler(IRepository<ContactPartRecord> repository) {
            Filters.Add(StorageFilter.For(repository));
            Filters.Add(new ActivatingFilter<UserPart>("Contact"));
            Filters.Add(new ActivatingFilter< UserRolesPart >("Contact"));  <<<<<<

            OnRemoved<ContactPart>((context, part) => repository.Delete(part.Record));
...
Hope this makes sense. Thanks for helping me here.
Dec 18, 2013 at 8:54 PM
You *don't need* the additional property, but you need to have UserRolesPart attached to your content type, "Contact". UserRolesPart keeps the relation btw roles and users.

Regarding how to attach the part to the content type, I'm not sure. Adding it in migration using AlterTypeDefinition().WithPart (as in 4) can be enough, or using only the activating can be enough (as in 5), or you might need to do both. I would be glad if you try and let me know which way worked for you.


Dec 18, 2013 at 9:25 PM
kassobasi wrote:
You don't need the additional property, but you need to have UserRolesPart attached to your content type, "Contact". UserRolesPart keeps the relation btw roles and users. Regarding how to attach the part to the content type, I'm not sure. Adding it in migration using AlterTypeDefinition().WithPart (as in 4) can be enough, or using only the activating can be enough (as in 5), or you might need to do both. I would be glad if you try and let me know which way worked for you.
Neither one worked. Here's how I updated the Migration:
        public int UpdateFrom1() {
            ContentDefinitionManager.AlterTypeDefinition("Contact",
                type => type
                    .WithPart("ContactPart")
                    .WithPart("UserRolesPart")
            );

            return 2;
        }
Did I do that right? I'm still feeling my way around the nuances of Migrations.
Dec 18, 2013 at 9:43 PM
You added the ContactPart twice to your content type, I guess. Do you have WithPart("ContactPart") in the Create method of migration as well? If so migration update might have failed, anything in the logs? If not, do you see UserRolesPart in Contact type definition on admin dashboard?


Dec 18, 2013 at 11:59 PM
kassobasi wrote:
You added the ContactPart twice to your content type, I guess. Do you have WithPart("ContactPart") in the Create method of migration as well? If so migration update might have failed, anything in the logs? If not, do you see UserRolesPart in Contact type definition on admin dashboard?
No errors in the log, and the DataMigrationRecord table was updated properly. However, when I re-ran the process, after removing the ContactPart from the second update, I saw the same (lack of) results.

On the other hand, I am seeing User Roles in the Contact type def. So I think I'm making progress. :-)

Do I need to do anything with respect to the CustomerPart?
Dec 19, 2013 at 12:17 AM
Well I can't think of anything else.

Let me ask, what is your user content type here? I assumed it's Contact but, I'm not sure now. How do you create users by code, can you post the exact piece. It should be something like:

var item = _contentManager.New<PartClass>("Content Type Name");
// set properties here
_contentManager.Create(item);

As for CustomerPart, I assumed it's attached to the same content type as ContactPart, since there is a property for these parts using As() method. As() method is like an accessor to get different parts of the same content item. So when you call As<ContactPart>() on a CustomerPart object, you get a non-null value only if the content item that CustomerPart object belongs to also contains a CustomerPart.




Dec 19, 2013 at 12:33 AM
Edited Dec 19, 2013 at 12:35 AM
Edit: I wrote this up independently of your latest response.

Got it.

Okay. The setup is even a little more complicated than I described, but once I got a sense of what you were describing, it was just a matter of trying things out until I got it to the point where it worked.

Here's what I've got:

1) In a stand-alone module, I have my ContactPart/ContactPartRecord. I'm eventually planning on turning this into a contact management module, but that's for later. I separated things out for that eventuality, but the structure is still very simple. ContactPart also has WithPart("UserPart"). More on this later....

2) In my main module, I have a CustomerPart/CustomerPartRecord. Customer "inherits" (in the Orchard sense) from Contact. I use Customer to manage the customers specific to this app. They will have certain privileges that are distinct from general Contacts.

3) I also have StaffPart/StaffPartRecord. This is to manage the staff members who work for the organization. They "inherit" from Customer, because staff members can be customers of the app as well.

Okay, here's where I've noticed some complications, and how I resolved the above issue:

At one point, with the customer content type, I was able to access its Contact.UserPart:
    var customer = _orchardServices.ContentManager.New("Customer");
...
    var userPart = customer.As<UserPart>();
...
However, at some point, all of a sudden this starting failing, because userPart would come back as null. I have no idea what happened, but it worked, and then it didn't. The really interesting thing is, at first it still worked for Customer, but it started failing for Staff. I then rebuilt everything from scratch, and it started failing for Customer too. So whatever "inheritance" I was picking up from Contact and propagating down to Customer and then Staff, ended up disappearing.

The only way I was able to resolve this was to add the following to Customer and Staff:
...
    .WithPart("UserPart")
...
It was only at that point that everything started working again. I honestly don't know why, and I'm not real happy that I have to do it. It feels like a major kludge to me, and I'm fearful of what underlying principals I'm violating because I'm doing this. It seems like the original way that I had it working made perfect sense (in thinking about it in an object oriented way).

So ... given this background, what I did was add UserRolesPart to Customer and to Staff; in the Drivers as well. Now everything works fine.

Does this all make sense to you? If you can make sense of why, and why the original structure at first worked and then failed, I'd really be interested in your explanation.

Be that as it may--it's now working the way I need it to. This going to be a big help with user management, now that I'm getting my head around roles.

Thanks for sticking with me on this. And if I've uncovered some misbehaviors with Orchard, let me know. I'll file a bug report.
Dec 19, 2013 at 1:22 AM
Glad it works now.

I don't think Orchard has a problem here. I can't explain why it worked at first and then it didn't, though.

User content type and related parts (UserPart, UserRolesPart) are complicated in the sense that they are handled in a special way but they are also regular types and parts that can be altered or reused. I think this is necessary for being extensible. I would rather have more restriction on this, as attaching a UserRolesPart to a content type without UserPart is meaningless.

The issue here I think is you are thinking in inheritance terms. I don't see an Orchard sense of parts or types inheriting from each other. Types are composed of parts (and fields), and parts should be independent of each other (in practice they are not, as seen by UserRolesPart). So when you create a content type that will represent a user of the site, you need to attach UserPart. If you want to assign roles to those users, you need to attach UserRolesPart as well. You see, composition only.

So, if the Contacts will not login to the site, you can (or should) take the UserPart out of it. I don't know what a Customer is in this context, but if it's a kind of a user, and Staff is another kind, you might have two user types (as you did adding UserPart to both content types) or add one part (like MyAppUserTypesPart) to User content type to mark the user kind (e.g. with an enumeration). I recently asked here https://orchard.codeplex.com/discussions/468580 to get some advices or best practices on this topic. Only Bertrand replied, and I figure "whatever sails your boat" is the best practice.

Good luck!

Dec 19, 2013 at 6:55 PM
kassobasi wrote:
Glad it works now.

I don't think Orchard has a problem here. I can't explain why it worked at first and then it didn't, though.

User content type and related parts (UserPart, UserRolesPart) are complicated in the sense that they are handled in a special way but they are also regular types and parts that can be altered or reused. I think this is necessary for being extensible. I would rather have more restriction on this, as attaching a UserRolesPart to a content type without UserPart is meaningless.

The issue here I think is you are thinking in inheritance terms. I don't see an Orchard sense of parts or types inheriting from each other. Types are composed of parts (and fields), and parts should be independent of each other (in practice they are not, as seen by UserRolesPart). So when you create a content type that will represent a user of the site, you need to attach UserPart. If you want to assign roles to those users, you need to attach UserRolesPart as well. You see, composition only.

So, if the Contacts will not login to the site, you can (or should) take the UserPart out of it. I don't know what a Customer is in this context, but if it's a kind of a user, and Staff is another kind, you might have two user types (as you did adding UserPart to both content types) or add one part (like MyAppUserTypesPart) to User content type to mark the user kind (e.g. with an enumeration). I recently asked here https://orchard.codeplex.com/discussions/468580 to get some advices or best practices on this topic. Only Bertrand replied, and I figure "whatever sails your boat" is the best practice.

Good luck!
Thanks for the feedback. Yeah, I get the distinction between inheritance and composition. Seems like inheritance could be a good way to go on this, because it is a good way to take advantage of pre-existing structures. But I see your point.

I really am curious why it initially worked the way I thought it should. It really makes no sense, given your points. Weird.

Thanks again.