IActionProvider and ContentPart

Topics: Customizing Orchard, Writing modules
Apr 25, 2012 at 11:12 PM

I am attempting to wire up an action to be executed each time a particular content type is created.  I have successfully implemented this in a custom module, however I am now stuck on getting the value from the Orchard.Rules.Models.ActionContext that is passed as the context value.  I was looking at the Close method of the CommentsActions.cs file in Orchard.Comments and I am stuck at this point:

private bool Close(dynamic context) {
  var contentId = Convert.ToInt32(context.Properties["ContentId"]);
  ContentItem content = _contentManager.Get(contentId);

  if (content != null) {
    var comments = content.As<CommentsPart>();
    if (comments != null) {
      comments.CommentsActive = false;
    }
  }
  return true;
}

The difference between the above code and my own is that context doesn't have any "Properties", it only has a "Token".  When I look at the value of context.Tokens["Content"] it appears to be the actual ContentItem I want to work with and basically eliminates the step of having to call _contentManager.Get(contentId); because I already have it.

But, the code above does this:

var comments = content.As<CommentsPart>();

My problem is the part I need to retrieve doesn't have a class in my solution file, I simply created it like this in my migration file:

ContentDefinitionManager.AlterPartDefinition("MyCustomPart", builder => builder
                .WithField("MyCustomFieldName", fieldBuilder => fieldBuilder.OfType("MediaPickerField"))
                .Attachable()
            );

//Create the content type...
ContentDefinitionManager.AlterTypeDefinition("MyCustomContentName", cfg => cfg
  .WithPart("CommonPart")
  .WithPart("MyCustomPart")
  .WithPart("TagsPart")
  .WithPart("RoutePart")
  .Creatable()
);

...which means I can't do this:

var customPart = content.As<MyCustomPart>();

All I really need/want is to be able to get the value of MyCustomFieldName on MyCustomPart, which basically is the url/path to the media that was selected/uploaded when the content was created.

I tried to get it from the parts collection on the ContentItem, but I never really saw the actual value in there...just the fieldname.

Any help would be GREATLY appreciated.

Thanks.

Apr 26, 2012 at 4:22 PM

I have a little update and perhaps this just boils down to me not understanding the inner workings yet...

After some playing around, it appears that I'm not quite as stupid as I thought...at least I was looking in the correct place for the values.

Originally my action was triggered when content was created so I decided to add a trigger for when content is published as well just to see what happens.  Then I created a content and stepped through the debugger and checked everywhere for the URL value on the MediaPickerField and could not find anything buy null values.  Then I published the same content and what do ya know...the url value was right there waiting for me!

Is this the way the system is designed?  Is this even how I should be attempting to accomplish my goal or is there a better way?

My goal is to be able to get the url property (/path/To/The/Selected/Image.ext) from the mediapicker so that I can perform some manipulation of said image.

Coordinator
Apr 26, 2012 at 4:42 PM

Well, at creation time the drivers have not yet had a chance to run and persist values, so yes, everything is null at this point, pretty much. So you can hook into publication events, but another thing you may want to try is to write your own driver: there can be more than one driver for any given part or field.

Apr 27, 2012 at 5:41 PM

Thanks for the feedback...I've started working on the driver and am now stuck once again. :)

Here is my part:

    public class DZPart : ContentPart<DZRecord>
    {
        [Required]
        public string Title
        {
            get { return Record.Title; }
            set { Record.Title= value; }
        }

        [Required]
        public string Description
        {
            get { return Record.Description; }
            set { Record.Description = value; }
        }
    }

 Here is my migration:

            SchemaBuilder.CreateTable(
                typeof(DZRecord).Name,
                table => table
                    .ContentPartRecord()
                    .Column("Title", DbType.String)
                    .Column("Description", DbType.String)
            );

            ContentDefinitionManager.AlterPartDefinition(
                typeof(DZPart).Name,
                builder => builder
                    .WithField("Thumbnail", fieldBuilder => fieldBuilder.OfType("MediaPickerField"))
                    .WithField("Image", fieldBuilder => fieldBuilder.OfType("MediaPickerField"))
                    .Attachable()
            );

Finally, here is my driver:

        protected override DriverResult Editor(DZPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            updater.TryUpdateModel(part, Prefix, null, null);
            return Editor(part, shapeHelper);
        }

Everything is working great, the form is being rendered and saved AND I can get the values of the Title and Description because I have defined them in my DZPart class, however the value I really need (Image) doesn't appear to be available to me.  I assume because it has been added to the DZPart definition via the migration file?  I did it this way because I wanted to be able to use a MediaPickerField in the editor and could not figure out any other way to make that happen.  

I just can't seem to win...am I going about this all wrong?  Isn't there someplace I can hook to get access to all the values of the fields I see on the screen the moment some content is created/saved for the first time?

Coordinator
Apr 27, 2012 at 5:47 PM

Try to add the fields to a part that has the same name as the content type.

Apr 27, 2012 at 7:21 PM

Please excuse my ignorance, but I'm not following this last suggestion.

So under the Content Types tab, I would have a content with a ContentTypeId of 'Map' and a display name of "Map". Then under the Content Parts tab, I would have an item with the Name of "MapPart" or would it simply be 'Map' as well?

Also, the fields I should be adding are to the Map(Part) and they should be the Thumbnail/Image fields?

Coordinator
Apr 27, 2012 at 8:17 PM

No, I thought you wanted to do it from the migration. If you want to do it from the admin UI, then just add the fields from the edit screen of the type.

Apr 27, 2012 at 8:29 PM

Yes, I did/do want to do it from the migration.  I was just trying to understand what it should look like once the migration completed.

Coordinator
Apr 27, 2012 at 9:01 PM

If you add it to a part that has the same name as the type (but that doesn't need to correspond to any CLR type), it will appear in the editor.