0
votes

In Orchard CMS, I'm trying to create a custom Field which is an "extension" of the Taxonomy Field.

So far, I've simply created another field, and basically copied all the code which runs the Taxonomy Field - and when I have both a normal Taxonomy Field and my custom Taxonomy Field attached to a Content Type, it actually works. The selected Terms in my custom field save and work as expected.

I run across issues when I remove the standard Taxonomy Field, and just use my custom field alone. This is because there is no longer the required TermsPart being welded onto the Content Item. The error logged is:

Orchard.ContentManagement.Drivers.Coordinators.ContentFieldDriverCoordinator - NullReferenceException thrown from <>f__AnonymousType4`2 by <>f__AnonymousType4`2[[Orchard.ContentManagement.ContentPart, Orchard.Framework, Version=1.7.1.0, Culture=neutral, PublicKeyToken=null],[Fusion.ContentTiles.Fields.TaxonomyTileField, Fusion.ContentTiles, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]
System.NullReferenceException: Object reference not set to an instance of an object.
   at Orchard.Taxonomies.Services.TaxonomyService.UpdateTerms(ContentItem contentItem, IEnumerable`1 terms, String field)
   at Fusion.ContentTiles.Drivers.TaxonomyTileFieldDriver.Editor(ContentPart part, TaxonomyTileField field, IUpdateModel updater, Object shapeHelper)
   at Orchard.ContentManagement.Drivers.ContentFieldDriver`1.<>c__DisplayClass13.<Orchard.ContentManagement.Drivers.IContentFieldDriver.UpdateEditorShape>b__12(ContentPart part, TField field) in c:\Projects\Demos\Orchard\src\Orchard\ContentManagement\Drivers\ContentFieldDriver.cs:line 47
   at Orchard.ContentManagement.Drivers.ContentFieldDriver`1.<>c__DisplayClass2d.<Process>b__2b(<>f__AnonymousType4`2 pf) in c:\Projects\Demos\Orchard\src\Orchard\ContentManagement\Drivers\ContentFieldDriver.cs:line 86
   at Orchard.InvokeExtensions.<Invoke>d__0`2.MoveNext() in c:\Projects\Demos\Orchard\src\Orchard\InvokeExtensions.cs:line 39

Which when I set a breakpoint and look in the TaxonomyService, when it tries to load the Content Item as a TermsPart, it returns null, thus throwing the NRE.

I'm not entirely sure how I can weld this TermsPart onto the Content Item. I'm guessing that I need to do it in a Handler of some sort, but I haven't been able to work this out. Just looking at my code I know it's wrong and really need to be pointed in the right direction with an example or something.

My non-working Handler (which is so far off it doesn't even hit a breakpoint):

public class TaxonomyTileFieldHandler : ContentHandler {
    private readonly IContentDefinitionManager _contentDefinitionManager;

    public TaxonomyTileFieldHandler(
        IContentDefinitionManager contentDefinitionManager) {
        _contentDefinitionManager = contentDefinitionManager;
    }

    protected override void Activating(ActivatingContentContext context) {
        base.Activating(context);

        // weld the TermsPart dynamically, if a field has been assigned to one of its parts
        var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(context.ContentType);
        if (contentTypeDefinition == null) {
            return;
        }

        if (contentTypeDefinition.Parts.Any(
            part => part.PartDefinition.Fields.Any(
                field => field.FieldDefinition.Name == typeof(TaxonomyTileField).Name))) {

            context.Builder.Weld<TermsPart>();
        }
    }
}

I am yet to try welding the part on in the field Driver... but that just seems so wrong (which is why I'm yet to attempt it).

My question is: How can I weld a Part onto a Content Item with a Field?

1

1 Answers

1
votes

So the above handler does actually work, once I added the [UsedImplicitly] data annotation to the top of the Handler. For reference, my Field Handler now looks like:

namespace Fusion.ContentTiles.Handlers {
    [UsedImplicitly]
    [OrchardFeature("Fusion.ContentTiles.TaxonomyExtensions")]
    public class TaxonomyTileFieldHandler : ContentHandler {
        private readonly IContentDefinitionManager _contentDefinitionManager;

        public TaxonomyTileFieldHandler(
            IContentDefinitionManager contentDefinitionManager) {
            _contentDefinitionManager = contentDefinitionManager;
        }

        protected override void Activating(ActivatingContentContext context) {
            base.Activating(context);

            // weld the TermsPart dynamically, if a field has been assigned to one of its parts
            var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(context.ContentType);
            if (contentTypeDefinition == null) {
                return;
            }

            if (contentTypeDefinition.Parts.Any(
                part => part.PartDefinition.Fields.Any(
                    field => field.FieldDefinition.Name == typeof(TaxonomyTileField).Name))) {

                context.Builder.Weld<TermsPart>();
            }
        }
    }
}

Another facepalm moment - but I'm glad I was able to figure it out.