Welding parts

Jan 11, 2011 at 6:46 PM

Can some one expound on welding parts a little more? I'm trying to get an idea by looking at user roles, but I can't quite wrap my head around where this is happening.

 

A couple of specific questions I have are these:

  1. What is the IUserRoles interface for? Is it to use the As<I>()? And, if so, does it only work because of the dynamic nature of the CMS (I don't see any inheritance in the Roles module for the IUser interface)?
  2. Why the choice for a Roles Service rather than individual drivers and handlers for each "piece" (RoleRecord/PermissionRecord)?
  3. This little bit is confusing me. Are the User and UserRoles parts already welded at this point?
    var model = new UserRolesViewModel {
                               User = userRolesPart.As<IUser>(),
                               UserRoles = userRolesPart,
                               Roles = roles.ToList(),
                           };
Coordinator
Jan 11, 2011 at 7:11 PM

1. Not sure what you mean by that. IUserRoles is the interface that the UserRolesPart implements. You may use it in As, yes. I don't understand what you mean by "it only works because of the dynamic nature of the CMS". IUser is implemented by several classes in the solution so I''m not sure what you're referring to.

2. There *are* individual drivers so I'm not following you. In the same way that you don't put code that doesn't belong in a controller but promote better separation by moving code into a reusable service class or classes, you also don't keep service code in your drivers and handlers. Controllers, drivers and handlers are supposed to wire things together, not to perform domain operations.

3. Sure they are. How would you be able to do the As otherwise?

Jan 11, 2011 at 10:37 PM

1. Specifically, UserRolesPart doesn't implement IUser anywhere, but you can use userRolesPart.As<IUser>() and end up with a UserPart. It feels like a cast, but I guess it's just getting a UserPart from the amalgam of content items?

2. In the Roles module, there is only one driver, UserRolesPartDriver. I was just curious why there aren't individual drivers for Roles and Permissions.

3. So when are the welded together?? Is it in the UserRolesPartHandler?

Coordinator
Jan 11, 2011 at 10:47 PM

1. UserRolesPart does not have to implement IUser: it implements IUserRoles. It's a different part. A user is the aggregation of a user part and of a user roles part. This is so that users are not coupled to roles, and you could imagine users that do not have roles or that have a completely different mechanism altogether to determine permissions. The API is designed to feel like a cast because that's exactly the pattern we're trying to replicate, but with Orchard's own dynamic content type system, thatis composing types from parts.

2. Roles and Permissions are not parts, so they don't have drivers.

3. When the content manager builds the content item. The handler has nothing to do with this.

Jan 11, 2011 at 10:55 PM

OK. I'm clear on the first two, but I'm still not clear on how the Content Manager knows that a user is an aggregation of a user part and a user roles part, or at what point the system says "this user part has these roles".

Coordinator
Jan 11, 2011 at 10:57 PM

Because the database contains content type metadata that defines what parts enter their composition. That metadata can be modified from the "Content Types" admin screen or from code.

Jan 11, 2011 at 11:27 PM

Arg. Sorry. I'm just not seeing it :/

Where is the code/meta data definition that says a User is now composed of a user and a user roles part?

Coordinator
Jan 11, 2011 at 11:36 PM

In setupservice if I remember correctly.

Jan 12, 2011 at 5:59 PM

I spent some more time last night and this morning trying to figure this out, but I think it's just magic. I looked in the SetupService and I can see where some of the default content types are created (BlogPost, Page, etc), but there is no mention of the users part (other than enabling the Orchard.Users module).

I see the User type defined in the database, but I'm not sure how it gets there. I thought this happened in the data migrations implementation with ContentManager.AlterPartType().

I tried stepping through the very beginning (creating an orchard host and then setup) with a fresh site. I thought I'd get creative and not enable the roles module on setup (then enable it afterward to step through what happens) but when I do that, the the Authorizer can't be resolved by Autofac, so setup fails. 

Coordinator
Jan 12, 2011 at 6:30 PM

Check out the UserPartHandler and the ActivatingFilter (Orchard.ContentManagement.Handlers.ActivatingFilter) to see how the UserPart is welded on to a User type on creation (or “activation”).

-Nathan

Coordinator
Jan 12, 2011 at 6:31 PM

In Orchard.Roles you’ll find:

Filters.Add(new ActivatingFilter<UserRolesPart>("User"));

And in Orchard.Users you’ll find:

Filters.Add(new ActivatingFilter<UserPart>("User"));

So those cases (user and site) are hardcoded - most others are in metadata.

So if you call var x =_contentManager.New("User") is called (or you .Load or .Create a “User”) you’ll end up something like var x = new ContentItem { Parts = new [] {new UserPart(), new UserRolesPart()} } …

Not literally that c# code, of course, just saying that’s what you can expect to end up with in the Parts collection.

When you call the .As<T>() extension method on the ContentItem, or on any ContentPart, it’ll return the first thing that can be cast to T. The best way to think about the ContentItem and .Parts is like one large object, and the .As<T>() is a typecast that works over its whole surface.

Jan 12, 2011 at 8:04 PM

OK OK. I get the welding part now. UserRolesPart isn't actually welded to the UserPart, but they both get welded to the User Content Type (right? I hope). I think the one last thing that would help me get the WHOLE picture would be to know where the User Content type gets created.

I really appreciate all the help. It's this kind of knowledge transfer that will really get this project going!