OrchardCMS / OrchardCore

Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
https://orchardcore.net
BSD 3-Clause "New" or "Revised" License
7.44k stars 2.4k forks source link

Ability to override the default editor for a field when attaching a part to a content type #10764

Open MikeAlhayek opened 2 years ago

MikeAlhayek commented 2 years ago

Discussed in https://github.com/OrchardCMS/OrchardCore/discussions/10754

I can't seems to find a way. Can this be added as a feature to add to the flexibility of the platform?

Originally posted by **CrestApps** November 24, 2021 I have a part called `LocationPart` that is added using the following migration ``` _contentDefinitionManager.AlterPartDefinition(nameof(LocationPart), part => part .Attachable() .reusable() .WithField(nameof(LocationPart.Location), field => field .OfType(nameof(ContentPickerField)) .WithDisplayName("Location") .MergeSettings(settings => { settings.Multiple = false; settings.DisplayedContentTypes = new[] { LocationConstants.Location }; }) ) ); ``` I attached the `locationPart` to a `Vehicle` content-type using the following migration. However, I somehow want to apply an editor called "Dropdown" on the `Location` field so the location field uses a `Dropdown` menu instead of the default content-picker. Note, I want to keep the default editor for other content type. `LocationPart` is being attached to other content types where the default editor is good. So I only want the editor changed when it's attached to the `Vehicle` type. ``` _contentDefinitionManager.AlterTypeDefinition("Vehicle", type => type .WithPart(nameof(LocationPart), part => part .WithDisplayName("Location") .WithPosition("1") // here somehow, I want to apply "Dropdown" editor for the "Location" field in the LocationPart ) ); ``` How can I override field settings when attaching a part to a content-type?
deanmarcussen commented 2 years ago

Use WithEditor("name")

_contentDefinitionManager.AlterTypeDefinition("Vehicle", type => type
    .WithPart(nameof(LocationPart), part => part
         .WithDisplayName("Location")
         .WithPosition("1")
         .WithEditor("Dropdown") <-- just apply the editor you want.
       // here somehow, I want to apply "Dropdown" editor for the "Location" field in the LocationPart
      )
);
MikeAlhayek commented 2 years ago

@deanmarcussen that does not work. .WithEditor("Dropdown") applies the Dropdown to the part not the field. In my case, I want to apply the Dropdown editor to the field that is inside the LocationPart

Here is how the UI looks like when applying the Dropdown to the part as per your suggestion image

and here is how the part appears when adding a new Vehicle using the UI.

image

I see no way to override the default editor of a field when attaching a part to a type "not even using the UI"

MikeAlhayek commented 2 years ago

@deanmarcussen I still can't find a way. You may want to reopen this issue until a valid solution is provided

deanmarcussen commented 2 years ago

Use the field builder on the part to specify the field settings

MikeAlhayek commented 2 years ago

@deanmarcussen how can I do that? .WithField method is not available on the Part at that level.

            _contentDefinitionManager.AlterTypeDefinition("Vehicle", type => type
                .WithPart(nameof(LocationPart), part => part
                        .WithDisplayName("Location")
                        .WithPosition("1")
                        .WithField(nameof(LocationPart.Location), field => field
                            .MergeSettings<ContentPickerFieldSettings>(settings =>
                            {
                                settings.Required = true;
                                settings.Multiple = false;
                            }))
                        )

            );

image

jtkech commented 2 years ago

Need to be done in the context of an AlterPartDefinition instead.

MikeAlhayek commented 2 years ago

@jtkech that will alter the content part itself. I don’t want to modify the default part. I want to override the part when it’s attached it the Vehicle type. So the locationPart does not change everywhere it’s attached. I only want to to change when it’s attached to Vehicle type.

jtkech commented 2 years ago

Yes, all our parts / partDrivers / partHandlers manage some "field" properties for editing / updating and so on, but that are not content fields, content fields have their own drivers / handlers. E.g. you can add a field to the AutoroutePart, you will see it while editing but the AutoroutePart will not manage it.

So, you would need to have a dedicated part per content type, otherwise to be attachable your part would need to be more standalone, or some custom content fields (maybe derived from the oob ones) that would be aware and able to use the settings of their current parent part.

MikeAlhayek commented 2 years ago

@jtkech Why can't we make OC more flexible to allow the user to override the setting for each field during the attachment if needed?

Assume I have the following

public class TeamPart : ContentPart
{
    public ContentPickerField Team { get; set; }
}

And lets say by default this is how I migrate it

_contentDefinitionManager.AlterPartDefinition(nameof(TeamPart), part => part
        .Attachable()
        .WithField(nameof(TeamPart.Team), field => field
            .OfType(nameof(ContentPickerField))
            .WithDisplayName("Team")
            .MergeSettings<ContentPickerFieldSettings>(settings =>
            {
                settings.Multiple = false;
                settings.DisplayedContentTypes = new[] { "Teams" };
            })
        )
);

Since this part is attachable, I want to reuse it on different content-types. In some case I want to make it required and other not. Also, in some case I may want to allow multiple and other not. as per your suggestion, I would have to create 4 different part to handle all the scenarios. It would be very powerful and would eliminate lots of redundancy to be able to change the settings for each field on a part when attaching the part to the type. The idea is not to have to create multiple part each for specific requirement, instead one configurable part.

jtkech commented 2 years ago

Yes I agree, I already had the same concern ;)

MikeAlhayek commented 2 years ago

@jtkech if you agree, I suggest we open this issue back up and tag it with enhancement label, so one day it'll be fulfilled

jtkech commented 2 years ago

Okay, feel free to re-open it if you want

MikeAlhayek commented 2 years ago

@jtkech i don’t not have the option too reopen the ticket or add a label

sebastienros commented 2 years ago

I don't think the data structure supports it, and it was done on purpose. Today we can configure parts attached to types, fields on parts, but not fields on part per type. The idea is that if you change the field settings on a part, then all the types that use this part will reflect the change. If you were to be able to customize fields at the type level, then you would lose this feature, and IMO it's more important to not lose it.

MikeAlhayek commented 2 years ago

@sebastienros currently we have attachable and/or reusable parts which gives us what you mentioned above and is great. But, what if we create a new configurable behavior part which by definition allow you to attach with configuration when assigned to a type. The only difference is that a configurable part will have a flag called something like override-default and when it’s set, changing the defaults on the part won’t impact this part on the type. We can accomplish this by by adding an extension called something like WithConfigurablePart that would allow us to override the settings we want to override for the part.

Note, if someone is using a configurable part they’ll understand the consequence so any override provided during the attachment process will always take affect over the default. Keep in mind anything we don’t override, will still inherit from the current default “which can be changed at the part itself”.

sebastienros commented 2 years ago

It would mean that the current Part editors would be changed to also handle this complexity. So you need to duplicate all part and type views to handle this case, and switch the implementation to store the information in the type and not the part. That's a lot of work, with potential bugs that will be added in the current logic. And very few people current reuse parts across types. You'd better create a custom part for each combination of field configuration you want. Maybe a compromise could be to be able to "copy" a part. You can also directly attach the fields to the type, which in the end is doing the same thing since each type has a custom part to store its fields.