Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding custom content part exception under edit #11314

Closed
mwpowellhtx opened this issue Mar 5, 2022 · 72 comments
Closed

Adding custom content part exception under edit #11314

mwpowellhtx opened this issue Mar 5, 2022 · 72 comments
Milestone

Comments

@mwpowellhtx
Copy link
Contributor

Version

Using Orchard Core Cms App, Module, etc, VS2019 project templates, 1.2.2.

Describe the bug

Exception adding custom part to out of the box boilerplate recipe item.

To Reproduce

I added the custom content part to the item type, tried to edit, then I got this.

Exception: Shape type 'AssemblyCredentialsPart_Edit' not found
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
CallSite.Target(Closure , CallSite , object )
AspNetCore.Views_ContentPart_Edit.ExecuteAsync()
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.RenderPartialCoreAsync(string partialViewName, object model, ViewDataDictionary viewData, TextWriter writer)
Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.PartialAsync(string partialViewName, object model, ViewDataDictionary viewData)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.<ProcessAsync>g__Awaited|11_0(Task<IHtmlContent> task)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
OrchardCore.DisplayManagement.Zones.ZoneShapes.ContentZone(IDisplayHelper DisplayAsync, object Shape, IShapeFactory ShapeFactory)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.<ProcessAsync>g__Awaited|11_0(Task<IHtmlContent> task)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
CallSite.Target(Closure , CallSite , object )
AspNetCore.Views_Content_Edit.ExecuteAsync()
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.RenderPartialCoreAsync(string partialViewName, object model, ViewDataDictionary viewData, TextWriter writer)
Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.PartialAsync(string partialViewName, object model, ViewDataDictionary viewData)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.<ProcessAsync>g__Awaited|11_0(Task<IHtmlContent> task)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
CallSite.Target(Closure , CallSite , object )
AspNetCore.Views_Admin_Edit+<>c__DisplayClass14_0+<<ExecuteAsync>b__1>d.MoveNext()
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder)
Microsoft.AspNetCore.Mvc.TagHelpers.RenderAtEndOfFormTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, int i, int count)
AspNetCore.Views_Admin_Edit.ExecuteAsync()
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|21_0(ResourceInvoker invoker, IActionResult result)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultFilters>g__Awaited|27_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
SixLabors.ImageSharp.Web.Middleware.ImageSharpMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
OrchardCore.Liquid.ScriptsMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
OrchardCore.Diagnostics.DiagnosticsStartupFilter+<>c__DisplayClass3_0+<<Configure>b__1>d.MoveNext()
Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
OrchardCore.ContentPreview.PreviewStartupFilter+<>c+<<Configure>b__1_1>d.MoveNext()
OrchardCore.Modules.ModularTenantRouterMiddleware.Invoke(HttpContext httpContext)
OrchardCore.Logging.SerilogTenantNameLoggingMiddleware.Invoke(HttpContext context)
OrchardCore.Modules.ModularTenantContainerMiddleware+<>c__DisplayClass4_0+<<Invoke>b__0>d.MoveNext()
OrchardCore.Environment.Shell.Scope.ShellScope.UsingAsync(Func<ShellScope, Task> execute, bool activateShell)
OrchardCore.Environment.Shell.Scope.ShellScope.UsingAsync(Func<ShellScope, Task> execute, bool activateShell)
OrchardCore.Environment.Shell.Scope.ShellScope.UsingAsync(Func<ShellScope, Task> execute, bool activateShell)
OrchardCore.Environment.Shell.Scope.ShellScope.UsingAsync(Func<ShellScope, Task> execute, bool activateShell)
OrchardCore.Modules.ModularTenantContainerMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Expected behavior

First should probably have an Edit view out of the box, or that there was one, the naming convention is misaligned, perhaps. The default I think was, AssemblyCredentialsPart.Edit.cshtml, but I'm not certain.

Follow on concerns

And perhaps corrolary to this, what is it that we should edit per se, for a custom part? And how does that relay through the model, view model, etc, what sort of bindings are there, what is serialized and persistent if at all, and so forth.

@Skrypt
Copy link
Contributor

Skrypt commented Mar 12, 2022

This means that you created a ContentPart and that this ContentPart is missing a Shape View to use for the Admin UI BuildEditorAsync() method. Of course, it will fail if there is no Shape View created for it to display. The ContentPart cannot be rendered without a View.

@mwpowellhtx
Copy link
Contributor Author

I'm not sure what that means. AFAIK it does have views out of the box, but I could be wrong about that. Obviously with the exception, something about that is askew.

@mwpowellhtx
Copy link
Contributor Author

Literally this is more or less what happened out of the box. I have not changed it. I added a couple more model elements in there anticipating integration of Assembly and derivative bits was all.

image

@Skrypt
Copy link
Contributor

Skrypt commented Mar 12, 2022

Can you show me your Startup.cs file code?

@mwpowellhtx
Copy link
Contributor Author

Module or app? Module...

public class Startup : StartupBase
{
    public override void ConfigureServices(IServiceCollection services)
    {
        services.Configure<TemplateOptions>(options =>
        {
            options.MemberAccessStrategy.Register<AssemblyCredentialsPartViewModel>();
        });

        services
            .AddContentPart<AssemblyCredentialsPart>()
            .UseDisplayDriver<AssemblyCredentialsPartDisplayDriver>()
            .AddHandler<AssemblyCredentialsPartHandler>()
            ;

        services.AddScoped<IContentTypePartDefinitionDisplayDriver, AssemblyCredentialsPartSettingsDisplayDriver>();
        services.AddScoped<IDataMigration, Migrations>();
    }

    public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
    {
        routes.MapAreaControllerRoute(
            name: "Home"
            , areaName: "Path.To.Cms.Module"
            , pattern: "Home/Index"
            , defaults: new { controller = "Home", action = "Index" }
        );
    }
}

App:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        static AssemblyCredentialsInfo CreateAssemblyCredentialsInfo(IServiceProvider _)
            => new AssemblyCredentialsInfo(typeof(Startup).Assembly)
            ;

        services.AddSingleton<IAssemblyCredentialsInfo>(CreateAssemblyCredentialsInfo);
            ;

        services.AddOrchardCms(config =>
        {
            config.RegisterStartup<Path.To.Cms.Module.Startup>();
        });
    }

    public void Configure(IApplicationBuilder app, IHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseStaticFiles();

        app.UseOrchardCore(config =>
        {
            config.UseSerilogTenantNameLogging();
        });

        const bool poweredByOrchardCore = true;
        app.UsePoweredByOrchardCore(poweredByOrchardCore);
    }
}

@mwpowellhtx
Copy link
Contributor Author

I've at least got the part listing with the other assets, which is a start. Just the editor is not connected for whatever reason.

@Skrypt
Copy link
Contributor

Skrypt commented Mar 12, 2022

Everything seems appropriate at first sight.

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 12, 2022

Everything seems appropriate at first sight.

Only at first sight. I get the exception however.

I definitely had to wire things a bit differently than I first thought in order for the Assembly DI to happen correctly, at least on the surface. Not sure what is happening with Edit however.

@Skrypt
Copy link
Contributor

Skrypt commented Mar 12, 2022

Does it run the Startup.cs file at all?

        services.AddOrchardCms(config =>
        {
            config.RegisterStartup<Path.To.Cms.Module.Startup>();
        });

This part here, not sure it is necessary.

@mwpowellhtx
Copy link
Contributor Author

Does it run the Startup.cs file at all?

That is evaluated, yes.

This part here, not sure it is necessary.

IIRC was required in order for the part to appear among the other assets i.e.

image

Which we can add it to a content type no problem. But when we try to edit said type, Exception.

@Skrypt
Copy link
Contributor

Skrypt commented Mar 12, 2022

Ok, I think I'm starting to understand the issue.

        static AssemblyCredentialsInfo CreateAssemblyCredentialsInfo(IServiceProvider _)
            => new AssemblyCredentialsInfo(typeof(Startup).Assembly)
            ;

        services.AddSingleton<IAssemblyCredentialsInfo>(CreateAssemblyCredentialsInfo);
            ;

        services.AddOrchardCms(config =>
        {
            config.RegisterStartup<Path.To.Cms.Module.Startup>();
        });

All of this won't work. because you are registering it at the app level while it needs to be registered at each tenant.
Of course, this won't throw any exceptions because nothing prevents you from doing it.

Though, when you create an Orchard Core module, its services are registered for each tenant that are enabled. So, the Startup.cs file is ran for each tenant context.

@mwpowellhtx
Copy link
Contributor Author

All of this won't work. because you are registering it at the app level while it needs to be registered at each tenant.

App versus tenant? Not sure what you mean. 'Tenant' just means an app, does it not? Multi-tenant meaning you have 1+ apps running alongside each other?

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 12, 2022

For a bit of additional context, in the part handler, I do this, which draws from the DI.

protected internal IAssemblyCredentialsInfo Info { get; }

// TODO: TBD: or at least this is the direction we think we need to go...
public AssemblyCredentialsPartHandler(IAssemblyCredentialsInfo assyCredentialsInfo)
{
    Info = assyCredentialsInfo;
}

public override Task InitializingAsync(InitializingContentContext context, AssemblyCredentialsPart part)
{
    part.Show = true;
    // // TODO: TBD: may need to be serializable, relay into the part instance...
    // part.Info = Info;

    return Task.CompletedTask;
}

@Skrypt
Copy link
Contributor

Skrypt commented Mar 12, 2022

A tenant is an instance of a host. You can have, let's say

Main tenant : domain.com
Sub tenant : domain2.com

Each of these tenants will have their own database configuration and will be an entire new Orchard Core instance.
So domain.com/admin will be different than domain2.com/admin.

@mwpowellhtx
Copy link
Contributor Author

Right, I get that, which is just another web app. i.e. www.ourdomain.com, versus shop.ourdomain.com, or blog.ourdomain.com each of which routed to a different app.

@Skrypt
Copy link
Contributor

Skrypt commented Mar 12, 2022

And in each of these tenants, we can enable Orchard Core modules. So, the tenant themselves manages the services that they are using. Here, if you register your Interface at the App level then I'm not sure the module from TenantA will know that the service exists. It expects to find it at the Tenant level, not the app level.

/cc @jtkech

@Skrypt
Copy link
Contributor

Skrypt commented Mar 12, 2022

Ok, we made some progress on that one. I will let @jtkech comment. I need to go for now. Time to get off the computer. It is the week-end ...

@mwpowellhtx
Copy link
Contributor Author

No problem, I appreciate the assist.

@mwpowellhtx
Copy link
Contributor Author

Well I do know this much at any rate, the info bits and such are being wired and connecting.

@mwpowellhtx
Copy link
Contributor Author

Considering other topics, this is turning into a semi-major block for us. Perhaps the fix is straightforward, just need a bit of verification, guidance, etc, if you please. However, also not sure we want the liquid after all; rather I think we prefer the Razor, for a whole host of reasons, not the least of which we think it is better supported up and down the entire tool chain.

@mwpowellhtx
Copy link
Contributor Author

At least prima facie, i.e. on its face, Occam's razor, should AssemblyCredentialsPart.Edit.cshtml be rather named AssemblyCredentialsPart_Edit.cshtml i.e. by convention? If so, easy enough to fix that, however, y'all probably want to patch the template as well, because AssemblyCredentialsPart.Edit.cshtml is what we got out of the box. Thank you...

@mwpowellhtx
Copy link
Contributor Author

At least...

Okay, that was not the issue. I really do not know what the issue is, but it seems like some glue is missing, perhaps, in the module, or perhaps even deeper into the OC/CMS framework itself.

@mwpowellhtx
Copy link
Contributor Author

Any ideas what might be causing this issue? Or what the resolution ought to be? White or black box... Transparent is preferred, OOB; but crossing that bridge, of course. Thank you...

@sebastienros
Copy link
Member

Can you check Lombiq's tutorial that explains how to create custom Parts and their view?
https://github.com/Lombiq/Orchard-Training-Demo-Module
https://github.com/Lombiq/Orchard-Training-Demo-Module/blob/dev/Startup.cs#L80

@sebastienros sebastienros added this to the backlog milestone Mar 17, 2022
@mwpowellhtx
Copy link
Contributor Author

Reviewing that, thank you.

@mwpowellhtx
Copy link
Contributor Author

Can you check Lombiq's tutorial that explains how to create custom Parts and their view...

In and of itself, I can piece that much together. However, how are the dots connected with a CMS web app?

@jtkech
Copy link
Member

jtkech commented Mar 17, 2022

@mwpowellhtx some unordered info

Yes, we have the main app running in a main container / pipeline, and each tenant (having their own settings e.g. url prefix) runs in an isolated container / pipeline, we have at least a Default tenant with an empty url prefix by default but you can setup additional ones. So yes a tenant is like an isolated app (notice that we have helpers allowing tenant1 to use services of tenant2) which is composed of modules which are composed of features (at least one main feature per module) that can be enabled / disabled per tenant. When setting up a tenant you can define the set of module features to be used through a recipe.

Then a module may need a manifest.cs file to define its features, as I remember you don't need it if you have only one main feature (the module code itself) but better to have one. Then your module project, to be recognized as a module / theme, needs to reference the OrchardCore.Module.Targets package or Theme.targets for a theme.

Notice that a module also has the structure of a "micro" app with its controllers / drivers / views / models, and this is the set of enabled module features that composes a given tenant "application".

Then you need a module startup (as you did) so that the module can collaborate to the building of a given tenant in which it is enabled, when the tenant container is building the module startup ConfigureServices() is called, when the tenant pipeline is building the module startup Configure() is called, notice that we have different way to control the order of startup calls through startup properties and features dependencies in the module manifest files.

When you register services at the app level, there are cloned to child tenant containers unless some as routing services that mutate global collections on startup, so because any tenant may be built at any time while other are running, each tenant needs its isolated routing services. We also filter some middlewares to prevent them from being executed multiple times, e.g. the default ones registered at the app level by aspnet, and so on.

But most of the time, what is intended to be used by a given tenant is registered at the tenant level through module startups that are enabled for this tenant. So you don't need at the app level to do.

config.RegisterStartup<Path.To.Cms.Module.Startup>();

That said, at the app level we have helpers that allows to register things at the tenant level, like a module / feature startup would do but then that would be enabled for all tenants. E.g. we have ConfigureServices() and Configure() helpers that act on an OrchardCoreBuilder, this builder being returned e.g. by AddOrchardCms()

        services.AddOrchardCms()
            .ConfigureServices(s =>
            {
                s.AddSomeServicesAtTenantLevel();
            },
            order: 100) // <= here an optional order property
            .AddSetupFeatures("OrchardCore.AutoSetup");

Returning back to the original issue,it seems that you already created driver / handler and the needed migration to define your custom part, so looks like you are very close to have an operational content Part.

When we create a Part composed of Fields through the Admin UI, we don't need a related part shape view template, and each field already has their oob shapes e.g. for editing, but when you define a part by code, as soon as you register a display driver and that drives a given shape whose default name uses the part name (can be overridden), you also need to provide the related view file. Notice that the same driver can combine multiple shapes with different view models, in that case each one should have a related shape view (a shape can be defined by .cs code).

And for each shape your driver can define different alternates view templates, the first one found by starting from the end of the list will be used. By convention template names don't use . and - characters, they use _ and __, so the SomePart_Edit template is related to the SomePart.Edit.cshtml file.

Finally you need to enable your module that should be listed under the Admin Features page, if not yet done automatically through a setup recipe.

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 17, 2022

@jtkech Maybe I am missing something there, it seems like more dots to connect following the ConfigureServices route versus RegisterStartup (?), i.e. if I can summarize, this does that, that does this, and the two meet in the middle.... Or why not just 'register'? Or for that matter, adding the reference and allowing OC to discover those module startups by convention transparently, if that's a thing; i.e. transparently discover them as 'plugins' (?). At any rate, I'll pick up with the 1.3.0 OC CMS module as my starting point and compare notes as necessary and see how far we get with that. Thanks for the response. 🍻

@mwpowellhtx
Copy link
Contributor Author

@jtkech Circling back on this one. The module itself, in and of itself, I think the parts are connected properly, out of the box, and with the bits that I want to lift into the part for rendering purposes.

I need to see the app side of things. How is that connected with the (an?) app?

@mwpowellhtx
Copy link
Contributor Author

From the App perspective, I tried this, with and without .AddLiquidViews(), exception in either case.

public void ConfigureServices(IServiceCollection services)
{
    services.AddOrchardCms(configure =>
    {
        configure.RegisterStartup<My.Cms.Module.Startup>().AddLiquidViews();
    });
}

@jtkech
Copy link
Member

jtkech commented Mar 23, 2022

Okay just tried the following, all from the command line

In MyModule folder

dotnet new ocmodulecms --AddPart true --PartName MyPart --orchard-version 1.3.0

MyPart is not a good name as it created a MyPartPart ;)

Then In MyApp folder

dotnet new occms --orchard-version 1.3.0

Then in MyApp.csproj I referenced MyModule

<ProjectReference Include="..\MyModule\MyModule.csproj" PrivateAssets="none" />

Then In MyApp folder

dotnet build
bin/debug/net6.0/MyApp.exe

Then I got the setup screen, selected the Blog recipe and run the setup

Then in the Admin, under content definitions MyPart was not yet exposed, so I went to the Features page to enable MyModule, then MyPart was listed under content definitions. Then I added MyPart to the existing Article Type, edited the About article and checked the boolean Show field.

Finally I went to the About page, MyPart was not rendered because the blog theme for the Article Type in its related Liquid file doesn't fully use the shape system, it renders only some parts explicitly so not new ones. So I went to the Admin > Design > Themes and selected The Default Theme, then MyPart was rendered.

When running the above ocmodulecms command a MyPartPart.Edit.cshtml was created for editing the part model only having a bool Show property (not a ContentField here), and a MyPartPart.liquid for rendering on the front end, here just to display the contentItemId if the Show field is true.

I also noticed a MyPartPart_Summary.liquid normally used when rendered in a list, but I think that the name is wrong, should not use an underscore I think, underscores are used internally by the code to manage template names, normally the related files should use - and . chars instead.

Hope this will help

@mwpowellhtx
Copy link
Contributor Author

So, if I understand the conventions correctly, dots . for cshtml, underscores _ for liquid, which 'is', but I could be wrong there. Which is what lands there OOB. No need to go to any of the admin, UI, etc. They just 'are' OOB. But there is an exception, maybe details, etc, need to be serializable? I'm not sure. How would I wire up a details, or a System.Reflection.Assembly, or AssemblyName, or do I need to mine and extract for read-only bits that I am interested to convey via the views and so forth? It is sounding to me more like that is the case.

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 23, 2022

@jtkech Show your app code, Startup, etc, please. Did you register anything there? I took out the registration for now and am receiving this exception during the app.UseOrchardCore(...) call:

System.InvalidOperationException: 'No service for type 'OrchardCore.Modules.IApplicationContext' has been registered.'

Details:

System.InvalidOperationException
  HResult=0x80131509
  Message=No service for type 'OrchardCore.Modules.IApplicationContext' has been registered.
  Source=Microsoft.Extensions.DependencyInjection.Abstractions
  StackTrace:
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.AspNetCore.Builder.ApplicationBuilderExtensions.UseOrchardCore(IApplicationBuilder app, Action`1 configure)
   at Emporium.Cms.Web.Startup.Configure(IApplicationBuilder app, IHostEnvironment env) in L:\Source\Emporium\Web-Master-Net6\src\Emporium.Cms.Web\Startup.cs:line 27
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass15_0.<UseStartup>b__1(IApplicationBuilder app)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.WebTools.BrowserLink.Net.HostingStartup.<>c__DisplayClass1_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Watch.BrowserRefresh.HostingStartup.<>c__DisplayClass1_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.<StartAsync>d__37.MoveNext()

I assume because 'nothing' has been registered, but 'something', i.e. the module, has been detected, i.e. via Project References.

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 23, 2022

Commented out the Assembly details stuff and retried. Still getting an exception!!!!!

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 23, 2022

@jtkech Did you try adding that part to the default Content Item? Then in the admin UI, try to edit that Content Item. Tell me what you get. Show your OC Cms App Startup, please. Thank you...

  1. Added part to the LandingPage in the Content Types, i.e. https://localhost:5001/Admin/ContentTypes/Edit/LandingPage
  2. Then in the LandingPage instance in the Content Items, visiting that example, trying to Edit, whatever, throws the exception

I remove the part from the Content Type, and no exception.

So, IMO, SOMETHING is blocking.

@jtkech
Copy link
Member

jtkech commented Mar 23, 2022

I took out the registration for now and am receiving this exception during the app.UseOrchardCore(...) call:
'No service for type 'OrchardCore.Modules.IApplicationContext'
I assume because 'nothing' has been registered

Yes, you need at least to call AddOrchardCms() that registers IApplicationContext.

Did you try adding that part to the default Content Item? Then in the admin UI, try to edit that Content Item.

Yes, and re-tried it with the Agency setup recipe, so that I could add MyPart to the LandingPage type.

Tell me what you get. Show your OC Cms App Startup, please. Thank you...

It works, I didn't do anything, just used the generated code.

MyApp startup

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOrchardCms();
    }
    
    public void Configure(IApplicationBuilder app, IHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseStaticFiles();
        app.UseOrchardCore();
    }
}

MyModule startup

public class Startup : StartupBase
{
    public override void ConfigureServices(IServiceCollection services)
    {
        services.Configure<TemplateOptions>(o =>
        {
            o.MemberAccessStrategy.Register<MyPartPartViewModel>();
        });

        services.AddContentPart<MyPartPart>()
            .UseDisplayDriver<MyPartPartDisplayDriver>()
            .AddHandler<MyPartPartHandler>();

        services.AddScoped<IContentTypePartDefinitionDisplayDriver, MyPartPartSettingsDisplayDriver>();
        services.AddScoped<IDataMigration, Migrations>();
    }

    public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
    {
        routes.MapAreaControllerRoute(
            name: "Home",
            areaName: "MyModule",
            pattern: "Home/Index",
            defaults: new { controller = "Home", action = "Index" }
        );
    }
}

Then in the LandingPage instance in the Content Items, visiting that example, trying to Edit, whatever, throws the exception

Is it the same exception saying that it didn't find the YourPart.Edit.cshtml ? Hmm, if so maybe because your App project file and Module project file are not targetting the same TFM.

Can you check that your project files are both targetting net6.0? This because in net6.0 razor views are embedded in the same module assembly, before e.g. in net5.0 they were embedded in a separate MyModule.Views.dll.

MyApp.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
  <TargetFramework>net6.0</TargetFramework>
  <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
  <RazorRuntimeCompilation>false</RazorRuntimeCompilation>
</PropertyGroup>

<ItemGroup>
  <Folder Include="wwwroot\" />
  <Folder Include="Localization\" />
</ItemGroup>

<!-- Watcher include and excludes -->
<ItemGroup>
    <Watch Include="**\*.cs" Exclude="Recipes\**;Assets\**;node_modules\**\*;**\*.js.map;obj\**\*;bin\**\*" />
</ItemGroup>

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="1.3.0"
                  Condition="'$(RazorRuntimeCompilation)' == 'true'" />
</ItemGroup>

<ItemGroup>
  <PackageReference Include="OrchardCore.Logging.NLog" Version="1.3.0" />
  <PackageReference Include="OrchardCore.Application.Cms.Targets" Version="1.3.0" />
  <ProjectReference Include="..\MyModule\MyModule.csproj" PrivateAssets="none" />
</ItemGroup>

</Project>

MyModule.csproj

<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
  <TargetFramework>net6.0</TargetFramework>
  <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
</PropertyGroup>

<ItemGroup>
  <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
  <PackageReference Include="OrchardCore.Module.Targets" Version="1.3.0" />
  <PackageReference Include="OrchardCore.ContentManagement" Version="1.3.0" />
  <PackageReference Include="OrchardCore.ContentTypes.Abstractions" Version="1.3.0" />
  <PackageReference Include="OrchardCore.DisplayManagement" Version="1.3.0" />
</ItemGroup>

</Project>

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 23, 2022

Is it the same exception saying that it didn't find the YourPart.Edit.cshtml?

Yes. Same exception. If/when that detail changes, I will say so.

Hmm, if so maybe because your App project file and Module project file are not targetting the same TFM.

TFM? You mean net6.0? If you followed the project template, I do not think there is any other choice in the matter. When you adopt 1.3.0, the decision was to abandon runtimes prior to net6.0, right. So we have no other choice than that,. Like I've said, literally, everything I am trying is virtually OOB.

Can you check that your project files are both targetting net6.0? ...

Let me stop you there. There is nothing to 'check' for the afore mentioned reasons. Now, if there is project template content that is obsolete now... (?) that is a whole other issue / can of worms, now isn't it?

Still, was failing before/after the 1.3.0 demarkation, so I don't exactly buy it, i.e. that line of reasoning, either.

As I've said, if I do not register the module Startup during the services.AddOrchardCms() then we do not see the part among the enumerated parts. Well... actually I stand 'partially corrected', but I wonder if that is an artifact of something serialized in the database, more than anything else. I am not seeing any of the module Startup lines hit under debugging, let alone any of the part handler injection happening. So I do not see any of the code actually happening.

Again, the key seems to be a difference between:

services.AddOrchardCms(configure =>
{
    // with or without liquid views... does not matter.
    configure.RegisterStartup<Elumination.Cms.Module.Startup>().AddLiquidViews();
});

Versus:

services.AddOrchardCms();

I am only gaining the benefit of the module Startup when I actually register.

@jtkech
Copy link
Member

jtkech commented Mar 23, 2022

Take care, in the doc we have the current warning

Due to a bug in the current published version, the following dotnet new commands will require the extra argument --orchard-version 1.3.0. For instance, instead of typing dotnet new occms use dotnet new occms --orchard-version 1.3.0

That's why I used this option for both installing MyApp and MyModule, and then I showed you the exact code I was using. But if you don't want to show us the exact code you are using, I can't help you anymore.

But no problem, at least it was very interesting to try to help someone like you ;)

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 23, 2022

That's why I used this option for both installing MyApp and MyModule ...

I am using the project templates; the result, AFAIK, is the same. This is via VS2022, right. There is not the same project template under a VS2019, because the target frameworks have changed circa 1.3.0, i.e. since VS2019 will not support net6.0, and hence the documented notes about the CLI dotnet new, for instance; maybe that is your confusion?

Kindly, we are 'there', IMO, in terms of zeroing in on possible root causes. Let's see that through.

@jtkech So... did you try registering your Startup? Without registering do you land on any debug breakpoints in your module?

services.AddOrchardCms(configure =>
{
    configure.RegisterStartup<Elumination.Cms.Module.Startup>().AddLiquidViews();
});

Concerning yours versus my csproj files themselves, they are identical in the ways that matter, re: package, project references, etc.

Thank you.

@mwpowellhtx
Copy link
Contributor Author

Re:

Due to a bug in the current published version, the following dotnet new commands will require...

That's not a bug, per se; that was a decision, IMO, to drop support for target frameworks prior to net6.0, which as I indicated above, is supported only by VS2022. Now, if that is in question, that opportunity is something else entirely, but that's not holding us up from drilling further into this issue.

@jtkech
Copy link
Member

jtkech commented Mar 23, 2022

Yes I used the dotnet CLI, just re-tried by installing the templates under VStudio 2022, same generated code, all works fine.

did you try registering your Startup?

No, this is done automatically when your module startup inherits from StartupBase, but for the fun just tried it, it still works, I just noticed by using a breakpoint that the module startup is more often called.

Edited

Never tried to install Templates under VStudio, it gave me the opportunity to try it, beautiful, thanks

@mwpowellhtx
Copy link
Contributor Author

No, this is done automatically when your module startup inherits from StartupBase...

Huh, well, I do derive from StartupBase, recalling from prior mentions. But the code is not running.

For grins I put a debug statement in the handler ctor, I never see that in the logs.

public AssemblyPartHandler(AssemblyPartDetails details)
{
    System.Diagnostics.Debug.WriteLine("here");
    Details = details;
}

@mwpowellhtx
Copy link
Contributor Author

@jtkech Would you be willing to join a brief mutual shared desktop? In 10-15 minutes maybe 30 on the outside comparing notes, maybe something will become obvious to one or both of us what is the discrepancy here. Thanks...

@mwpowellhtx
Copy link
Contributor Author

The only other variable that we have not discussed is database backing the Cms ... we are assuming postgres on this end.

@mwpowellhtx
Copy link
Contributor Author

For grins, tried to stand up a SQLite test.

  1. Started new project, compiled, ran, started with SQLite OOB; I guess all such sites land with a yessql.db under this condition, not sure if that's a good thing or not, i.e. if we wanted to share databases across multi-tenants. Neither here nor there re: this issue. Verified, site up and started.
  2. Add module, nothing new OOB, just added the module. Wired it up in the App with the configure => ... callback. Prima facie well and good, I have the part available, added it to the LandingPage.
  3. Tried to edit the part in the LandingPage, I get the same exception, "Exception: Shape type 'AssemblyPartSettings_Edit' not found".

OOB we have both AssemblyPartSettings.Edit.cshtml and AssemblyPart.Edit.cshtml accounted for. Looks reasonable when I inspect those, so I have no idea WTF is happening why those are not being discovered properly.

@sebastienros Can you verify this please. This is a major show stopper. So far is broken under SQLite, postgres. Assuming perhaps @jtkech is using SQL Server (?), but that is unconfirmed at this point.

Again, all this is OOB; if there is more in the way of plumbing we need to touch for it to 'just work', that's what we need to know here. Thank you...

@jtkech
Copy link
Member

jtkech commented Mar 24, 2022

Wired it up in the App with the configure => ...

Don't do that, just keep .AddOrchardCms() without any delegate.

You just need to add in your app .csproj file a ProjectReference to your module, e.g.

<ProjectReference Include="..\MyModule\MyModule.csproj" PrivateAssets="none" />

@mwpowellhtx
Copy link
Contributor Author

Okay, I did that much.

What else is PrivateAssets exposing into Web however by doing that?

Also, in either of the views, AssemblyPart.Edit.cshtml:

@model AssemblyPartViewModel

<div class="form-group">
    <div class="custom-control custom-checkbox">
        <input type="checkbox" class="custom-control-input" asp-for="Show" />
        <label class="custom-control-label" asp-for="Show">@T["Show"]</label>
    </div>
</div>

Or, AssemblyPartSettings.Edit.cshtml:

@model Spike.Cms.Module.Settings.AssemblyPartSettingsViewModel

<div class="form-group">
    <div class="w-xl-75">
        <label asp-for="MySetting">@T["Setting"]</label>
        <input asp-for="MySetting" class="form-control" />
    </div>
    <span class="hint">@T["The setting."]</span>
</div>

If those are the views, I do not see Show represented anywhere in the admin UI. Perhaps I am missing something about that.

@mwpowellhtx
Copy link
Contributor Author

@jtkech Furthermore, as I stated before, I do not think the actual code is being run in the module when you do that. Sure there are no exceptions, however, none of the Startup, none of the Handlers, etc, are being performed. I have breakpoints there, none of it is happening.

@jptissot
Copy link
Member

@jtkech Furthermore, as I stated before, I do not think the actual code is being run in the module when you do that. Sure there are no exceptions, however, none of the Startup, none of the Handlers, etc, are being performed. I have breakpoints there, none of it is happening.

Is your module enabled ?

@mwpowellhtx
Copy link
Contributor Author

mwpowellhtx commented Mar 24, 2022

@jptissot

Is your module enabled ?

What does that mean? I do not see it listed among the Features? First, I do not see it listed among Configuration/Features, if that's what you mean? Second, the part is listed among the parts. What else needs to be enabled beyond that?

Edit: Ah 💡 now I see it. Now code is running. It's a bit awkward OOB between the "The setting" and editing the content item Show checkbox, but I think I could piece it together after that.

Thanks!

@jtkech
Copy link
Member

jtkech commented Mar 24, 2022

What else is PrivateAssets exposing into Web however by doing that?

A Module has msbuild scripts (in .props and .targets files), this ensures that there are executed when building your app, in fact not necessary when you reference it directly, required when a module is referenced indirectly through another project/package.
It ensures that

@jptissot
Copy link
Member

Well if your part is in a module, you have to enable the module for the Shapes to be added to the list of available shapes.
The fact that the part shows up in the parts only means that your migration ran at some point.

@jtkech
Copy link
Member

jtkech commented Mar 24, 2022

Yes, because at some point the module startup was explicitly registered from the app startup

@mwpowellhtx
Copy link
Contributor Author

@jtkech @jptissot See above Edit. I found the module in the Configuration and enabled it. Now I see 👀 it. Thank you all for the follow up.

@jtkech
Copy link
Member

jtkech commented Mar 24, 2022

Yes, as said in a previous comment

Finally you need to enable your module that should be listed under the Admin Features page, if not yet done automatically through a setup recipe.

@jtkech
Copy link
Member

jtkech commented Mar 24, 2022

All good now !!!

@mwpowellhtx
Copy link
Contributor Author

Well, it's progress at any rate; I'll take me victories when/where I can find them 🎆 🥳 🍾

@mwpowellhtx
Copy link
Contributor Author

Yes, closed finally. Mini-saga. Satisfied now that I have a better understanding what is connecting where, how, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants