Closed sebastienros closed 5 years ago
Okay, i will check it.
I'm working on other view locations related to #1921.
@sebastienros
Just tried with the dev branch, it works on my side with or without the route.
What doesn't work is if you put in the _ViewStart
: Layout = "../Views/Shared/_Layout.cshtml"
. When you put it in the page it works, the requested view path is still relative to the app's module virtual folder. But if you put it in the view start it fallbacks directly to /Views/Shared
from the content root.
If i put this in the view start it works
Layout = "../.Modules/OrchardCore.Cms.Web/Views/Shared/_Layout.cshtml";
I think it's quite easy to fix this use case by also serving views when requested from the content root, not only through the app's module, but do you think it's worth to implement this case?
UPDATE i fixed this use case through #2386 (not yet committed). It was already working for other modules (not the application module).
To make a Razor Pages App work as a module, I had to make the following changes:
~/
to ~/AppName/
asp-page
taghelper, from /pagename
to pagename
razorview
to Shared/razorview
if the view was in Pages\Shared, like the _Layout.cshtml (which is it's default location)Using version beta3-68457
Example: https://github.com/psijkof/OrchardCMSDecoupled/commit/f680e7fa63a9c55c30bab8587b991e433a712d0d
@psijkof
When talking about the application as a module, it is the final application which is not a module but behaving as a module when defining things at the app level, controller / views, shapes, views, pages.
An OC app don't call directly things as .AddMvc()
, AddRazorPages()
... This is done through helpers in the isolated context of each tenant (at least one named Default
). So, normally app level mvc things can't work, this is why we made the app behave as a "module" whose virtual location is .Modules/ApplicationName
.
After a quick look to your VanillaRazorPagesApp
, is it an application? You use Sdk.Web
, netcoreapp2.1
, AspNetCore.App
, but you reference Module.Targets
. Oh yes, so you did an app and then you want to use it as a module in another application. Great idea, maybe to test it independently,
So this is not the same sense as described. Never tried, not sure it works with our msbuild scripts. Anyway, for the following tests, i assume that your app is in fact a regular module / theme.
Static files
At the module level, in TheBlogTheme/Pages/Bar1.cshtml
, this works.
At the application level, in /Pages/Bar.cshtml
, (i put the same image under the app wwwroot
). Here we just replaced the module name by the application name (the application's module name).
We call UseStaticFiles()
at the tenant level to serve all modules static files (including the app's module), that's why the above works. Then, assuming that your application also call app.UseStaticFiles()
in its startup, its static files can also be served like this.
/Pages/Shared
asp-page anchor tag helper attribute
Here it's not a routing attribute, it is the name of the page related to its path. So, in a normal application where the page root directory is /Pages
, /Foo
point to /Pages/Foo
. Then, in a page directly under /Pages
, using the relative Foo
without leading slash also point to /Pages/Foo
.
In our case at the module level, in TheBlogTheme/Pages/Bar1.cshtml
, this works.
FooPage FooPage // or other relative paths.
But the following doesn't work because there are wrong absolute paths.
But, because we define default route templates, you can just use href
attributes.
Then, by using AddModularFolderRoute()
or AddModularPageRoute()
as in the OC.Demo
module, you will be able to use e.g.
In our case at the application level, in /Pages/Bar.cshtml
, for the same reasons this works.
FooPage FooPage // or other relative paths.
But not e.g.
FooPage // relative path.
But, because of our default route templates, you can just use href
attributes.
// Because it is the application's module, we don't create this default template. //FooPage
// In place of the above, for the app's module we create this one. FooPage
@sebastienros just for infos
For testing i renamed .Modules
to Areas
, it opens many possibilities. For pages under Areas
razor remove Areas
and Pages
segments from pages names / paths without custom conventions. We could re-use the default page root directory /Pages
so that razor don't want to watch them from the root /
. I think we could also get rid or simplify some view location expanders and so on.
I read a little the RCL doc, their stategy is that e.g if a library defines pages under their Areas
or Pages
, they are respectively "imported" to the app Areas
or Pages
, where the app can override them. In my test this is what is defined in {module}/Pages
which is "imported" in the app /Areas/{module}/Pages
.
Maybe worth to discuss on this, but there would be many things to check.
@jtkech Thanks for the explanation. I started with the App using Cms.Targets. But having a Razor Pages module, seemed more like I would actually use it. Starting with a default 'Web App' (dotnet default template) I showed what I had to change to make it work as a Module for OC. Perhaps all I should have referenced was Module.targets to avoid any confusion and still be able to use IOrchardHelper?
Anyway, great work. I know it's perhaps not feasible, but the less I should have to change to make a 'web app' work as a module, the better it would be, or at least as long it's clear why and what I need to change.
Thanks for the great work, hope soon we see the changes you made merged.
Seems to work great in a razor pages module scenario. We're building the modern business theme with razor pages in repo
If we however request for a non existent page, we get a
Exception: Shape type 'Layout' not found
Why is it trying to find the Layout in a ´404´ situation? What should we supply to create a decent (preferably styled according to the modern business template) error page?
Thanks for the insights!
Follow up: We added a Layout.cshtml in the themes project, in location Pages\Shared. When a razor page from a module looks for 'Layout', that file is found and used. However, if a request comes for a non existent page, OC looks for a Layout shape in Views folder in the themes project. Would it be a good idea to display the 404 content in that file, or would that break the generic Standard CMS usage of OC (and therefore it wouldn't be a good idea)?
So, i you have the cms with the theming engine and then a selected teme, normally you just have to use a regular layout which render the body, and then a not found page in the shared folder, as done in TheBlogTheme
. So try to create and use this view YourTheme/Views/Shared/NotFound.cshtml
@jtkech If I place the Layout.cshtml
back in Views
of the Theme project, I get a not found by the localization of the layout page for the razor pages
/Areas/ModernBusiness.Theme/Pages/ModernBusiness.Pages/Layout.cshtml
/Areas/ModernBusiness.Theme/Pages/Shared/Layout.cshtml
/Areas/ModernBusiness.Theme/Views/ModernBusiness.Pages/Shared/Layout.cshtml
/Areas/ModernBusiness.Theme/Views/Shared/Layout.cshtml
That's why I put it in Views\Shared
.
But then, when I request a non existing page, OC complains it can't find the Layout shape (page)... It is looking in the theme project, Views
folder.
Also, putting the Layout.cshtml as is in Views
, with RenderSection commands, it faults with the error:
An unhandled exception occurred while processing the request.
InvalidOperationException: RenderSection invocation in '/Areas/ModernBusiness.Theme/Views/Layout.cshtml' is invalid. RenderSection can only be called from a layout page.
Microsoft.AspNetCore.Mvc.Razor.RazorPage.EnsureMethodCanBeInvoked(string methodName)
Any idea what I should do here?
UPDATE:
It's as if Layout in Views
in the Themes project is not a real Layout view. Even if I make it as simple as
@RenderBody
It faults with: InvalidOperationException: RenderBody invocation in '/Areas/ModernBusiness.Theme/Views/Layout.cshtml' is invalid. RenderBody can only be called from a layout page.
UPDATE 2:
If I supply a Layout.liquid with just a simple {% render_body %}
, and a Views/Shared/NotFound.cshtml
, things work like it should, I guess?
Yes, with a RazorPages.Page
you specify the layout, but when not found it acts as a "normal" themed Razor.RazorPage
, that's why the layout is searched in the theme Views
folder.
Then, i think you need to call our @await RenderBodyAsync()
, not @RenderBody()
.
Repro: Create this
And in
_ViewStart.cshtml
_Layout.cshtml
is created inViews\Shared
Without the custom route, the layout is used.