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.43k stars 2.4k forks source link

Adding custom content part exception under edit #11314

Closed mwpowellhtx closed 2 years ago

mwpowellhtx commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

Can you show me your Startup.cs file code?

mwpowellhtx commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

Everything seems appropriate at first sight.

mwpowellhtx commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

No problem, I appreciate the assist.

mwpowellhtx commented 2 years ago

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

mwpowellhtx commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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

mwpowellhtx commented 2 years ago

Reviewing that, thank you.

mwpowellhtx commented 2 years ago

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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

So to be clear, I do not see the module available in the site admin Configuration for it to be activated at all. So there still needs to be glue there, correct? Is the extension method literally called AddSomeServicesAtTenantLevel? I'm not clear what that means, per se.

image

Again, for the part to be visible, for starters, in the Content Parts, I needed to have registered its Startup, which stands to reason. I do not see any other way to connect those dots. What am I missing there?

mwpowellhtx commented 2 years ago

I add the part in the place that I want it in the Content Item definition, that is fine enough. But when we go to edit that item, that is when we get exceptions.

image

Exception: Shape type 'AssemblyPart_Edit' not found
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
OrchardCore.DisplayManagement.Implementation.DefaultHtmlDisplay.ExecuteAsync(DisplayContext context)
CallSite.Target(Closure , CallSite , object )
AspNetCoreGeneratedDocument.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 )
AspNetCoreGeneratedDocument.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 )
System.Dynamic.UpdateDelegates.UpdateAndExecute1<T0, TRet>(CallSite site, T0 arg0)
AspNetCoreGeneratedDocument.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)
AspNetCoreGeneratedDocument.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|22_0(ResourceInvoker invoker, IActionResult result)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_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|28_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_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|20_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.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)
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)

Again, this is all out of the box. Nothing spectacular there.

ns8482e commented 2 years ago

This is common error when

mwpowellhtx commented 2 years ago

Another observation, when the app is closing out, I get the following error in the console. Only apparently when I am registering the Startup.

\\Path\To\src\My.Cms.Web\bin\Debug\net6.0\My.Cms.Web.exe (process 30388) exited with code -1.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
mwpowellhtx commented 2 years ago

This is common error when

  • you do not have reference of OrchardCore.Module.Targets On your module project OR

I do not see how that is possible. I started the project literally using the OC Cms Module project template.

  • You have not used dotnet command line to create occmsmodule or you have manually changed to content OR
  • Your shape is in main app and missing in publishes folder

Don't know what you mean shape. Literally this is OOB stuff. Nothing fancy there, using the default Show etc bits. So you're saying that the template OOB is broken? Then there needs to be more detailed instructions what to connect.

Now, I do not intend for that to be the destination, of course. I am just trying to spin it up and get something on its feet that I can compare notes against.

Again by shape isn't that the point of standing this up in its own module? The module knows the model, view model, i.e. shape, if I understand you correctly. The only thing the app needs to do is be connected with the feature, module, etc, correct?

Edit: Also, in this particular case, I simply want to display some assembly information for Copyright and credentialing purposes. Not unlike some sites do. So the app really has no knowledge, nor should it, on that issue, the model, the view model, etc. Rather, that is the purpose of the views provided by the module.

mwpowellhtx commented 2 years ago

I am trying to inject System.Reflection.Assembly bits, of course. That plumbing seems to be just fine, in and of itself. But whatever is asking for an editor, does not like that, and I do not know what. I wrap the Assembly instance in a Details class, and that I connect with the handler, which in turn feeds that instance to the Content Part on Initialization. That appears to be working. Would someone be able to review this further without necessarily going public? Thanks...

Skrypt commented 2 years ago

Have you looked at @jtkech suggestions before saying that it's a OOB content part? We have plenty of those working in the source code right now so there is maybe something with the DI that you are doing wrong as we suggested.

mwpowellhtx commented 2 years ago

Yes. I am not finding anything that different from the examples. I'm not sure what the exception is to be honest. Nothing wrong with the DI as I said, the plumbing connects the dots I would expect, including and through Initialization. Then I get the exception.

mwpowellhtx commented 2 years ago

How about this, just try to start a module OOB and see if it stands up "as-is". It is looking for an edit view (?). But, apparently, there "is one" OOB. So how is it this is not hooking up properly? What I need to see is from the app perspective for comparison.

jtkech commented 2 years ago

Hmm, I see above that you defined AssemblyCredentialsPart.Edit.cshtml so normally it should work.

Can you share on github a simple app referencing your module just to repro, so that we could try it and I hope fix it.

mwpowellhtx commented 2 years ago

Hmm, I see above that you defined AssemblyCredentialsPart.Edit.cshtml so normally it should work.

Can you share on github a simple app referencing your module just to repro, so that we could try it and I hope fix it.

Again TO BE CLEAR, I did not define ANYTHING. Okay, I wired up some details which connected to the Assembly appropriately, as expected. But OTHER THAN THAT, it was all boilerplate OOB. Like I said, start from the OC Cms Module project template, and let's compare some notes, please.

mwpowellhtx commented 2 years ago

@jtkech What I need to see is how the PersonPart module is integrated, by an OC Cms App, presumably, ostensibly. Is there that example as well? Comparing notes with that, how is the Startup registered, etc.

jtkech commented 2 years ago

Okay, I would need to retry our OC Cms Project Templates, I will try to do it asap ;)

To integrate a given Module project, your OC Cms app should reference it as a project, then if your module references the Module.Targets package it will be automatically recognized as a module, then as soon as your module is enabled in the current tenant (here the default one), its startup will be used when building the tenant container.

mwpowellhtx commented 2 years ago

...then if your module references the Module.Targets package...

Starting from the OC Cms Module does all that for you, I think, appropriate targets, etc. I showed that the part does actually appear to the admin screens above. So that much "just works".

...your OC Cms app should reference it as a project...

Yep; I did do the reference; then AFAIK I register the Startup. That was it (?). If there is other glue I need to provide, that's what I need to know. But which also beggars a string of other questions, but let's see that first. 👍 Thanks!

mwpowellhtx commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

@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 commented 2 years ago

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

mwpowellhtx commented 2 years ago

@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 commented 2 years ago

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>