Ensuring a content part is always assigned to a content type

Topics: Writing modules
Jul 13, 2012 at 11:58 AM

Hi, How do I ensure that a specific content type always contains a specific content part?

Currently I have this in my Migrations class of a custom module called EventsManagement:

 ContentDefinitionManager.AlterTypeDefinition("Venue", builder => builder
                .WithPart("CommonPart")
                .WithPart("TitlePart")
                .WithPart("AutoRoutePart", partBuilder => partBuilder
                    .WithSetting("AutorouteSettings.AutomaticAdjustmentOnEdit", "false")
                    .WithSetting("AutorouteSettings.AllowCustomPattern", "true")
                    .WithSetting("AutorouteSettings.PatternDefinitions", "[{Name:'Venue Title', Pattern:'venue/{Content.Slug}', Description:'venue/venue-title'}]")
                    .WithSetting("AutorouteSettings.DefaultPatternIndex", "0"))
                .WithPart("BodyPart", partBuilder => partBuilder
                    .WithSetting("BodyTypePartSettings.Flavor", "text"))
                .WithPart("AddressPart")
                .WithPart("VenuePart")
                .Creatable()
                .Draftable());

AddressPart is a custom part that I created in a different module called Addresses. The EventsManagement feature has a dependency on the Address feature,  but what is to stop a user from removing the Address content part from the Venue content type from the Dashboard? Is there a way that I can define the type to be dependent on the part?

Coordinator
Jul 13, 2012 at 12:01 PM

By design, you can't ensure that. Never make that assumption. You never should have to anyways as parts are supposed to be self-contained. Any logic that pertains to the part should be encapsulated in it, its drivers etc. Address and Venue should maybe not be separate parts. That doesn't prevent you from re-using code if that address is useful in other parts, but the parts themselves should not depend on other parts.

Jul 13, 2012 at 12:19 PM

Ok thanks, that makes sense, but how do you reuse the address part on it's own in for instance a Visitor type?

Coordinator
Jul 13, 2012 at 12:22 PM

It's not the part that you re-use, it's its code. You can re-use templates, factor code into services, etc.

Developer
Jul 13, 2012 at 1:08 PM
Edited Jul 13, 2012 at 1:12 PM

Bertrand's totally right. But there are certain situations in which a given part has to be there on a given content item, because otherwise that item would be useless (like User content item has to contain a UserPart) .

In order to achieve that you should weld your part onto a given content type from handler, not from migrations.

Please look how it's done with UserPart in Orchard.Users/Handlers/UserPartHandler.cs.

Coordinator
Jul 13, 2012 at 1:18 PM

That's a good point. Welding parts from code (a handler for example) is a legitimate way of ensuring a part on a type. Keep in mind though that parts should be self-contained as much as possible.

Jul 13, 2012 at 1:21 PM

Thank you! That's very helpful. Here is the code from the users module:

[UsedImplicitly]
    public class UserPartHandler : ContentHandler {
        public UserPartHandler(IRepository<UserPartRecord> repository) {
            Filters.Add(new ActivatingFilter<UserPart>("User"));
            Filters.Add(StorageFilter.For(repository));
        }

        protected override void GetItemMetadata(GetContentItemMetadataContext context) {
            var part = context.ContentItem.As<UserPart>();

            if (part != null) {
                context.Metadata.Identity.Add("User.UserName", part.UserName);
            }
        }
    }

Which part of this handles the functionality? I can't find anything in the documentation about ActivatingFilter or GetItemMetadata. Not sure what they are/do.

Developer
Jul 13, 2012 at 1:36 PM
Edited Jul 13, 2012 at 1:38 PM

This line does it:

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

Agreed, docs should be updated if that info is not there:

  • ActivatingFilter allows to attach a given part to a given content type. It's basically a one-line shortcut to calling item.Weld(part) during item Activating step (OnActivating handler method), ie. when a content item is constructed and about to be loaded from database.
  • GetItemMetadata allows to alter given item's metadata (like customizing edit/admin/display routes, identity and so on).
Coordinator
Jul 13, 2012 at 1:40 PM

"docs should be updated" go for it ;)

Developer
Jul 13, 2012 at 1:40 PM

:) Sure will!

Jul 13, 2012 at 1:45 PM

Thanks @pszmyd!

Really very helpful.

Developer
Jul 13, 2012 at 1:53 PM
Edited Jul 13, 2012 at 1:54 PM

No problem - glad I could help!:)

@Bertrand Updated docs on GitHub and sent a PR.

Coordinator
Jul 13, 2012 at 2:00 PM

Thanks, I'll accept the pull request as soon as you've fixed the typo I commented about. Cheers!