what do you mean by "not set in his own directory"
We put the views directly in the ~/Views folder because this makes sense to regular Umbraco devs. People that don't know or don't care to know MVC and how it all works, etc... would be confused if their views had to be stored in ~/Views/RenderMvcController
Of course if you are hijacking Umbraco routes, you can store your views in the controller named folder. For example, if you have a controller that Hijacks a route called "MyHomeController" and you returned views from it, you could store your views in:
~/Views/MyHome
just like how normal MVC works. However, there will be no UI tooling support for this in the back office (at least not currently)
I'm not familiar with the approach taken in the "Working in Visual Studio when developing umbraco solutions" post that you've mentioned so I'm not sure if this answers your question or not?
First i want to thank you for replying to all my questions! I really want to do it good because we are firm with alot of big customers and if i want to say that Umbraco is a good system i must understand how everything works.
My idea was that i made 2 projects, 1 with umbraco and 1 with mvc so i can have the source code in source control (that's what is read on forums that it is a good way to do sourcecontrol in umbraco). Then i make a reference to umbraco so that the project build and as an afterbuild proces i copy all the files and dll's to the umbraco dir. If umbraco keeps the code just like microsoft mvc you can use all the visual studio tools that mvc gives you (right mouse click > add view, etc). That was the idea so you can use mvc like microsoft want to use it.But after more trying out i now understand that if you copy the files to the view directory that is not directly a template you can use, you must add it to the database also.. is it a idea to build a directory watcher as a umbraco plugin and add it with sql automatic or do i get alot of problems then?
Now you know what i'm trying to do maybe you know better why i asked the question, i still think it's a good idea to keep everything the same as microsoft so the dev's that uses visual studio can work faster with umbraco and for the direct umbraco users, they don't see the directory structure so that wouldn't be a problem for them..
But my second question is still a problem, most of our sites exists out of 30 or more template, if they are all in the root i can be messy. Someone else got that problem?
Maybe someone can tell how this work with the masterpages in the old way, are there any subfolders you can use?
We have also discussed auto-creating the database entries like you have done (nice work!). However, we cannot use a file system watcher in the core as this requries Full Trust. We could use a timer however, or just create the db entries on app startup however, neither of these are great solutions until we actually get this task completed.
There is still much legacy code in the Umbraco codebase that we need to slowly phase out and this is one of them. We cannot just change these things because it will break everyone's builds.
Then you can store your views for each of your document types in sub folders that match your controllers name just like normal MVC. For example if you have a doc type called "Home" and you hijack a route for it, you would have a controller called "HomeController". You could then simply return base.Index(model); (if you don't want to do anything fancy in your controller) and you could then store your view at
~/Views/Home/
since the controller that is rendering the view is called HomeController.
I do the something like hijacking, but i still use the path's from Umbraco, not the one from MVC. I still want the good SEO path's so i'll keep that. For the problem with path's i was thinking of overriding your code in Umbraco and writing code the uses the path's also but i just looked over the Umbraco code very fast so i need to see if it's possible.
On the other thing, i understand you need to have backward compatiblity but i don't have to! I will try it, if it works i'll maybe build it one day to a module for other users.. do you have maybe any pointers that i keep in mind when building this?
fast a update what i found out: the cms understands a path without any change in the code! I've just changed the path in the database from test to /test/ed and the with the Umbraco i can edit that file in the template part in Umbraco. But the frontend doesn't find that path.. only a empty page.
I see that the file RenderViewEngine parses the pages.. let see if i can change that :)
damm.. don't think this is gonna work, i see that there is a own RenderViewEngineand that i must write my own thing... maybe you know a simpler way Shannon?
var replaceWithUmbracoFolder = _supplementedViewLocations.ForEach(location => templateFolder + location);
if the location in the database is "test/ed" i would be replace to the right position but still a empty page... can't see to find where's the problem.
@Shannon, can you please help me, i now understand why from Umbraco viewpoint controller directories are not needed but as a developer friendly CMS i find it strange that all the views are in one directory. Big sites with 50 or more views are normal for us you can understand we then need the directories. I can understand that this is a problem for more companies that build big sites so it would be a nice addon. If you don't have the time or so can you then give me pointers so i can implement this because without it i don't think we are gonna use Umbraco as our CMS in the future :(
private void OnCreated(object sender, FileSystemEventArgs e) { if (e.Name.IndexOf('\\') > -1) { try { //string l_strFilename = Path.GetFileNameWithoutExtension(e.Name).Replace('\\', '/'); string l_strFilename = e.Name.Substring(0, e.Name.LastIndexOf(".")).Replace('\\', '-'); if (Template.GetTemplateIdFromAlias(l_strFilename) == 0) { Template.MakeNew(l_strFilename, new umbraco.BusinessLogic.User(0)); //clean umbraco file that is generated File.Delete(m_objContext.Server.MapPath(String.Format("~/Views/{0}.cshtml", l_strFilename))); } } catch { } } } }
Umbraco also make a file (i wish that he didn't do that!) so you had an endless loop.. now it's fixed and the Umbraco file is deleted. I this way you cannot edit the file in the CMS anymore but that's not really a problem because you got visual studio if you do url hijacking :)
And i think it is broken again... code for the RenderMvcController has been changed so that you must have the views in the /views/ directory, damm that's not acceptable for us. What i said earlier, we have 20/30 templates per site so no structure any more in our projects. Shannon, can't you give just a warning in the log and not return Content("")? Something like this?
protected ActionResult CurrentTemplate<T>(T model) { var template = ControllerContext.RouteData.Values["action"].ToString(); if (!EnsurePhsyicalViewExists(template)) { LogHelper.Warn<RenderMvcController>("No physical template file was found for template " + template + " in the Views directory"); } return View(template, model); }
Yes, we can put a log message in there but the RenderMvcController's logic has not changed in 6.0. The only thing that was changed is in this revision:
35fc4486c9d4
Which just adds some properties to the controller. Previous changes were in 4.11 in revision:
4703906d9c52
Which just moves the exact same logic from the 'Index' method into a new protected method called: CurrentTemplate<T>
I know this is an old post but having recently started working with Umbraco and found the lack of folders in the view folder a similar issue i wanted to share my solution.
Having tried the route hi-jacking i found little success when the view contained a form posting to a surface controller as the view engine always seemed to return the view stored in the DB which as the template alias didnt correspond to the physical view path caused the response of all SurfaceCotrollers to be blank (maybe I missed something?).
On application start up i am registering a custom default controller with a controller that overrides the Index method with a local implementation of the CurrentTemplate method (taken from the Umbraco source), the only difference is that before using the template path i am replacing any instance of an underscore with a slash which allows a naming convention of template alias's such as 'MyAccount_Bookings' to match the view '/Views/MyAccount/Bookings.cshtml'.
The only issue now is to prevent Umbraco from generating the physical view files...
In MVC you can always render a view by it's full virtual path:
~/Views/WhateverYouWant/Something/Blah.cshtml
With route hijacking, you are using your own controller so views will be found by normal MVC convention if you don't specify the entire virtual path. For example if you have a controller called HomeController, and you return a view called "Hello" then the view engines will look in:
~/Views/{Controller}/{ViewName}.cshtml
it will also look in various other places too and finally will try the default umbraco path of ~/Views/{ViewName}.cshtml
I'm not sure what you mean by your second paragraph:
Having tried the route hi-jacking i found little success when the view contained a form posting to a surface controller as the view engine always seemed to return the view stored in the DB which as the template alias didnt correspond to the physical view path caused the response of all SurfaceCotrollers to be blank (maybe I missed something?).
When you post to a SurfaceController you generally always return either
return CurrentUmbracoPage();
or
return RedirectToCurrentUmbracoPage();
You never return a view from a POST to a SurfaceController because that will literally just return a view - the same way as normal MVC would.
MVC views not in subfolder?
Why are view's build in the Umbraco not set in his own directory? I want to do this http://our.umbraco.org/wiki/codegarden-2009/open-space-minutes/working-in-visual-studio-when-developing-umbraco-solutions with a MVC project and then copy the view directory to Umbraco but there all the files are directly under the views folder..
Hi,
what do you mean by "not set in his own directory"
We put the views directly in the ~/Views folder because this makes sense to regular Umbraco devs. People that don't know or don't care to know MVC and how it all works, etc... would be confused if their views had to be stored in ~/Views/RenderMvcController
Of course if you are hijacking Umbraco routes, you can store your views in the controller named folder. For example, if you have a controller that Hijacks a route called "MyHomeController" and you returned views from it, you could store your views in:
~/Views/MyHome
just like how normal MVC works. However, there will be no UI tooling support for this in the back office (at least not currently)
I'm not familiar with the approach taken in the "Working in Visual Studio when developing umbraco solutions" post that you've mentioned so I'm not sure if this answers your question or not?
Hi Shannon,
First i want to thank you for replying to all my questions! I really want to do it good because we are firm with alot of big customers and if i want to say that Umbraco is a good system i must understand how everything works.
My idea was that i made 2 projects, 1 with umbraco and 1 with mvc so i can have the source code in source control (that's what is read on forums that it is a good way to do sourcecontrol in umbraco). Then i make a reference to umbraco so that the project build and as an afterbuild proces i copy all the files and dll's to the umbraco dir. If umbraco keeps the code just like microsoft mvc you can use all the visual studio tools that mvc gives you (right mouse click > add view, etc). That was the idea so you can use mvc like microsoft want to use it.But after more trying out i now understand that if you copy the files to the view directory that is not directly a template you can use, you must add it to the database also.. is it a idea to build a directory watcher as a umbraco plugin and add it with sql automatic or do i get alot of problems then?
Now you know what i'm trying to do maybe you know better why i asked the question, i still think it's a good idea to keep everything the same as microsoft so the dev's that uses visual studio can work faster with umbraco and for the direct umbraco users, they don't see the directory structure so that wouldn't be a problem for them..
Ed
First question i got fixed, i have now a module with a directory watcher so after copy i do this.. (is a try, could be better)
public virtual void LoadWatch(Object p_objContext) {
FileSystemWatcher l_objWatcher = new FileSystemWatcher();
HttpContext l_objContext = (HttpContext)p_objContext;
l_objWatcher.IncludeSubdirectories = true;
l_objWatcher.Path = l_objContext.Server.MapPath("~/Views/");
l_objWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName;
l_objWatcher.Filter = "*.cshtml";
l_objWatcher.Created += new FileSystemEventHandler(OnCreated);
l_objWatcher.EnableRaisingEvents = true;
}
private void OnCreated(object sender, FileSystemEventArgs e) {
string l_strFilename = e.Name.Substring(0, e.Name.LastIndexOf("."));
if (Template.GetTemplateIdFromAlias(l_strFilename) == 0) {
Template.MakeNew(l_strFilename, new umbraco.BusinessLogic.User(0));
}
}
and this is loaded as a module in Umbraco. But is still need curious why Umbraco builders choose not to keep it the microsoft way..
p.s. this has an endless loop, look next page for a better version
But my second question is still a problem, most of our sites exists out of 30 or more template, if they are all in the root i can be messy. Someone else got that problem?
Maybe someone can tell how this work with the masterpages in the old way, are there any subfolders you can use?
Yes, we have an issue on the tracker to remove the db dependencies for templates, but this will mean large breaking changes:
http://issues.umbraco.org/issue/U4-684
https://groups.google.com/d/topic/umbraco-dev/Yso2jtzn1PU/discussion
We have also discussed auto-creating the database entries like you have done (nice work!). However, we cannot use a file system watcher in the core as this requries Full Trust. We could use a timer however, or just create the db entries on app startup however, neither of these are great solutions until we actually get this task completed.
There is still much legacy code in the Umbraco codebase that we need to slowly phase out and this is one of them. We cannot just change these things because it will break everyone's builds.
One way you could have views in subfolders is to hijack a route for each of your doc types:
http://our.umbraco.org/documentation/master/Reference/Mvc/custom-controllers
Then you can store your views for each of your document types in sub folders that match your controllers name just like normal MVC. For example if you have a doc type called "Home" and you hijack a route for it, you would have a controller called "HomeController". You could then simply return base.Index(model); (if you don't want to do anything fancy in your controller) and you could then store your view at
~/Views/Home/
since the controller that is rendering the view is called HomeController.
Hi Shannon,
I do the something like hijacking, but i still use the path's from Umbraco, not the one from MVC. I still want the good SEO path's so i'll keep that. For the problem with path's i was thinking of overriding your code in Umbraco and writing code the uses the path's also but i just looked over the Umbraco code very fast so i need to see if it's possible.
On the other thing, i understand you need to have backward compatiblity but i don't have to! I will try it, if it works i'll maybe build it one day to a module for other users.. do you have maybe any pointers that i keep in mind when building this?
Ed
p.s. thx for your anwser again Shannon!
fast a update what i found out: the cms understands a path without any change in the code! I've just changed the path in the database from test to /test/ed and the with the Umbraco i can edit that file in the template part in Umbraco. But the frontend doesn't find that path.. only a empty page.
I see that the file RenderViewEngine parses the pages.. let see if i can change that :)
damm.. don't think this is gonna work, i see that there is a own RenderViewEngine and that i must write my own thing... maybe you know a simpler way Shannon?
if i see the RenderViewEngine i see this
private readonly IEnumerable<string> _supplementedViewLocations = new[] { "/{0}.cshtml" };
and
var replaceWithUmbracoFolder = _supplementedViewLocations.ForEach(location => templateFolder + location);
if the location in the database is "test/ed" i would be replace to the right position but still a empty page... can't see to find where's the problem.
@Shannon, can you please help me, i now understand why from Umbraco viewpoint controller directories are not needed but as a developer friendly CMS i find it strange that all the views are in one directory. Big sites with 50 or more views are normal for us you can understand we then need the directories. I can understand that this is a problem for more companies that build big sites so it would be a nice addon. If you don't have the time or so can you then give me pointers so i can implement this because without it i don't think we are gonna use Umbraco as our CMS in the future :(
...it was there all the time! our.umbraco.org/.../custom-controllers i just didn't get it right away...
I've made a change to the viewwatcher for ppl who dive into this..
public class ViewWatcher : IHttpModule {
static object m_objLockObject = new object();
static bool Initialized = false;
public virtual void Init(HttpApplication p_objApplication) {
if (!Initialized) {
lock (m_objLockObject) {
if (!Initialized) {
ParameterizedThreadStart l_objPTS = new ParameterizedThreadStart(LoadWatch);
Thread l_objThread = new Thread(l_objPTS);
l_objThread.Start(p_objApplication.Context);
Initialized = true;
}
}
}
}
public void Dispose() {
}
public virtual void LoadWatch(Object p_objContext) {
FileSystemWatcher l_objWatcher = new FileSystemWatcher();
m_objContext = (HttpContext)p_objContext;
l_objWatcher.IncludeSubdirectories = true;
l_objWatcher.Path = m_objContext.Server.MapPath("~/Views/");
l_objWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName;
l_objWatcher.Filter = "*.cshtml";
l_objWatcher.Created += new FileSystemEventHandler(OnCreated);
l_objWatcher.EnableRaisingEvents = true;
}
private HttpContext m_objContext;
private void OnCreated(object sender, FileSystemEventArgs e) {
if (e.Name.IndexOf('\\') > -1) {
try {
//string l_strFilename = Path.GetFileNameWithoutExtension(e.Name).Replace('\\', '/');
string l_strFilename = e.Name.Substring(0, e.Name.LastIndexOf(".")).Replace('\\', '-');
if (Template.GetTemplateIdFromAlias(l_strFilename) == 0) {
Template.MakeNew(l_strFilename, new umbraco.BusinessLogic.User(0));
//clean umbraco file that is generated
File.Delete(m_objContext.Server.MapPath(String.Format("~/Views/{0}.cshtml", l_strFilename)));
}
} catch {
}
}
}
}
Umbraco also make a file (i wish that he didn't do that!) so you had an endless loop.. now it's fixed and the Umbraco file is deleted. I this way you cannot edit the file in the CMS anymore but that's not really a problem because you got visual studio if you do url hijacking :)
And i think it is broken again... code for the RenderMvcController has been changed so that you must have the views in the /views/ directory, damm that's not acceptable for us. What i said earlier, we have 20/30 templates per site so no structure any more in our projects. Shannon, can't you give just a warning in the log and not return Content("")? Something like this?
protected ActionResult CurrentTemplate<T>(T model)
{
var template = ControllerContext.RouteData.Values["action"].ToString();
if (!EnsurePhsyicalViewExists(template))
{
LogHelper.Warn<RenderMvcController>("No physical template file was found for template " + template + " in the Views directory");
}
return View(template, model);
}
Yes, we can put a log message in there but the RenderMvcController's logic has not changed in 6.0. The only thing that was changed is in this revision:
35fc4486c9d4
Which just adds some properties to the controller. Previous changes were in 4.11 in revision:
4703906d9c52
Which just moves the exact same logic from the 'Index' method into a new protected method called: CurrentTemplate<T>
I know this is an old post but having recently started working with Umbraco and found the lack of folders in the view folder a similar issue i wanted to share my solution.
Having tried the route hi-jacking i found little success when the view contained a form posting to a surface controller as the view engine always seemed to return the view stored in the DB which as the template alias didnt correspond to the physical view path caused the response of all SurfaceCotrollers to be blank (maybe I missed something?).
On application start up i am registering a custom default controller with a controller that overrides the Index method with a local implementation of the CurrentTemplate method (taken from the Umbraco source), the only difference is that before using the template path i am replacing any instance of an underscore with a slash which allows a naming convention of template alias's such as 'MyAccount_Bookings' to match the view '/Views/MyAccount/Bookings.cshtml'.
The only issue now is to prevent Umbraco from generating the physical view files...
Anthony
In MVC you can always render a view by it's full virtual path:
~/Views/WhateverYouWant/Something/Blah.cshtml
With route hijacking, you are using your own controller so views will be found by normal MVC convention if you don't specify the entire virtual path. For example if you have a controller called HomeController, and you return a view called "Hello" then the view engines will look in:
~/Views/{Controller}/{ViewName}.cshtml
it will also look in various other places too and finally will try the default umbraco path of ~/Views/{ViewName}.cshtml
I'm not sure what you mean by your second paragraph:
When you post to a SurfaceController you generally always return either
or
You never return a view from a POST to a SurfaceController because that will literally just return a view - the same way as normal MVC would.
is working on a reply...