Routing problem with module

Topics: Troubleshooting
Jul 19, 2011 at 2:01 AM

I know that getting help regarding third party code is difficult, but I thought I'd ask. I am able to successfully send e-mails using MvcMailer, however, the body of the message is empty.

MvcMailer sets up views, so if I create a Mailer, under the views folder of my module it will put "/Views/Mailer".  Under "/Mailer" it will have a "_Layout.cshtml" file and a "Mailer.cshtml" file which represents the body of the e-mail sent.

At first I thought that moving the "/Mailer" folder either to the "/Views" folder of my THEME or to the "/Views" folder of Orchard.Web might work (I thought it was just not finding the paths).

I tried playing around with route.cs in my module, but I don't know whether or not I can just leave CONTROLLER and ACTION empty ("") and put the folder "/Mailer" in there only. In any event, I created a route for "Mailer.cshtml" but that hasn't worked either.  I am wondering if this is even the right way to go (I did not set up actions in a controller, but am thinking to do so - just going to have to redirect?).

I'm a bit lost, so I appreciate any guidance.

-REM

Jul 22, 2011 at 2:10 PM

I am still having trouble.  I have attempted various things to try to get this to work.

Again, the e-mails are successfully being sent (through SMTP and to a local directory dump).  Data being entered is getting passed to the email (the "mailMessage.To.Add(model.UserEmail);").  It's just that the template and view are not showing up in the body of the e-mail.

In the implementation there is "MasterPage = "_Layout.cshtml"", and I suspect that because its a module it is not finding the template and hence not rendering the body portion of the e-mail.

I tried looking in other modules to see how they render templates/views, but I confess to not knowing the intricacies of programming (hence why I am trying to integrate MvcMailer in my module, since I don't know how to properly implement templates and views using Orchard's mail module; that plus I set up a few mailers in my old MVC3 project already using the MvcMailer scaffolding).

If anyone is interested, I would definitely consider posting my results and a how-to of getting this to work. 

Thanks.

Coordinator
Jul 26, 2011 at 12:17 AM

Did you try asking the people who build MvcMailer?

Jul 26, 2011 at 2:15 AM
Edited Jul 26, 2011 at 2:46 PM

Yes. Actually, I started looking through the MvcMailer source to see how things were done, but still no luck.  I've also tried looking through the hierarchy using VS2010.  I also tried debugging.

Debugging using the source was the suggestion I received from the builder of MvcMailer.

I am using MultiTenancy, so I am going to have to use the landlord site because the module is for a tenant. But I will first try to debug and report my results.

I confess I am no programmer, so I am just sort of lost. Ordinarily if I run into this type of problem I try and figure out an alternative way, but a good portion of the site is based on sending out mails based on user input. The other major portion of the site that I have a better handle on is CRUD type operations. My other alternative would be to go back to my old Mvc3 project, but I was hoping to use Orchard for everything I am doing rather than splitting my time between two projects.

Thanks.

Coordinator
Jul 26, 2011 at 6:18 PM

Well, it doesn't seem like there is any MvcMailer expertise to be found on this forum, unfortunately. StackOverflow maybe?

Jul 26, 2011 at 6:50 PM

I tried over at Stack a while back.  I'm going to try and reformulate my question to see if I get a better response.

I just thought it was in the way that Orchard handled routing which might be preventing the template/view from displaying in the body of the mail message.

I had no success trying to debug. The body is showing empty ("") in the debugger. The MasterName is showing up as the value that's in the code, but it's not showing a route.

Thanks for the help.

Coordinator
Jul 26, 2011 at 7:04 PM

Maybe another option would be to drop MvcMailer and go for some more austere string formatting. Not as nice, but it would work.

Jul 26, 2011 at 7:44 PM
Edited Jul 26, 2011 at 7:45 PM

I am thinking the same thing and trying to figure out how. I was thinking of using T4 templates, just have to figure out how to get it to work. :)

FYI, I tried some different methods with MvcMailer and got this error message (NOTE: before, the success page would show up and the e-mail would get sent, but again without a body):

(ANOTHER NOTE: It doesn't list locations searched which furthers my confusion). Thank you again for being kind enough to help out and let me take you away from more important things (i.e., Orchard 2.0). :)

 

Server Error in '/' Application.

The view 'QuoteMailMatrimonialUncontested' or its master was not found or no view engine supports the searched locations. The following locations were searched:

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: The view 'QuoteMailMatrimonialUncontested' or its master was not found or no view engine supports the searched locations. The following locations were searched:

Source Error: 

 

Line 146:            public void EndProcessRequest(IAsyncResult result) {
Line 147:                try {
Line 148:                    _httpAsyncHandler.EndProcessRequest(result);
Line 149:                }
Line 150:                finally {


Source File: C:\[DELETED_FOR_POSTING]\My Web Sites\ORCHARD\src\Orchard\Mvc\Routes\ShellRoute.cs    Line: 148 

Stack Trace: 

[InvalidOperationException: The view 'QuoteMailMatrimonialUncontested' or its master was not found or no view engine supports the searched locations. The following locations were searched:]
   Mvc.Mailer.StringResult.ExecuteResult(ControllerContext context, String mailerName) +142
   Mvc.Mailer.MailerBase.EmailBody(String viewName, String masterName) +182
   ClientSlateInquiry.Mailers.QuoteMailer.QuoteMailMatrimonialUncontested() +567
   ClientSlateInquiry.Controllers.MatrimonialController.QuoteMatrimonialUncontested(QuoteMatrimonialUncontestedPart UncontestedQuote) +103
   lambda_method(Closure , ControllerBase , Object[] ) +108
   System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
   System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +208
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
   System.Web.Mvc.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12() +55
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +263
   System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +263
   System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +263
   System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +191
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
   System.Web.Mvc.Controller.ExecuteCore() +116
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
   System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
   System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   Orchard.Mvc.Routes.HttpAsyncHandler.EndProcessRequest(IAsyncResult result) in C:\[DELETED_FOR_POSTING]\My Web Sites\ORCHARD\src\Orchard\Mvc\Routes\ShellRoute.cs:148
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8920029
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184



Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.225

Coordinator
Jul 26, 2011 at 7:51 PM

Isn't T4 a Visual Studio technology? I was thinking even more barebones, something like String.Format. Then again I don't know how complex your e-mails are.

Jul 26, 2011 at 8:47 PM

Yes, they are pretty complex. I am taking user input, for example, to determine a price quote for a service. A=$5, B=$10, C=$15, as an example. There is a base price, say $100, and based on the user choice for one item, I'd add $5, $10 or $15 to the quote. There are multiple add-ons for a service, so I could be adding $100 + $5 + $10 + $15, etc. So even before the e-mail is sent out in the background this is calculated and then the e-mail is generated. 

In the end, I'll probably have to use strings + append everything.  This is multiplied by the amount of services, so it's a daunting prospect and will probably be a nightmare to maintain.

Coordinator
Jul 26, 2011 at 8:49 PM

Yes, that's not good. If it makes you feel any better, Orchard 2.0 has a tokens feature that does some fancy formatting that might help. Also, if you prepare your data from a controller-like entity and then just format the results into the mail, it should be slightly less horrible.

Jul 26, 2011 at 10:40 PM
Edited Jul 26, 2011 at 10:42 PM

I saw that the roadmap/info on tokens this past weekend and was very excited (partly for this purpose and others like it that I have in mind).

One last thought/questions which I hope you are able to guide me on:

One of the problems I encountered early in trying to resolve this problem was in the web.config files under the module.  In a regular MVC project this would ordinarily appear:

<system.web.webPages.razor>
    ...
	<pages pageBaseType="System.Web.Mvc.WebViewPage">
    ...
</system.web.webPages.razor>

But in Orchard (and for obvious reasons) this appears:

<system.web.webPages.razor>
    ...
     <pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
    ...
</system.web.webPages.razor>

 

This is where I still believe my problems lie, because MvcMailer is expecting to use the regular MVC pageBaseType. Originally, I tried to replace the Orchard pageBaseType with the standard MVC3 one, but got a bunch of errors (obviously), starting with Layout.Title and then Script.Include (the page itself being rendered). When I commented that out I started getting errors in my _Layout.cshtml page in the Theme folder.  It was clear that my errors were related to not using Orchard's pageBaseType, so I stopped and put the web.config back in order and tried looking at this problem from other angles.

My strange/unorthodox question is this: Is there a way to either a) force a module to use both the Orchard pageBaseType and the MVC pageBaseType (although not sure if that's possible since <pages> cannot appear twice in a config file) or b) get a module to use the MVC pageBaseType inside of Orchard without it breaking?

I will note that the web.config I speak of is the one under the root of the "/[NameOfModule]" folder.  The web.config in the "/[NameOfModule]/Views" folder has the regular MVC pageBaseType (as opposed to the Orchard pageBaseType).

Again, many thanks for your valuable time.

-Robert

Coordinator
Jul 27, 2011 at 8:50 PM

Well, did you try specifying a base explicitly in the view itself? For all I know, what's in config is only the default.

Jul 28, 2011 at 2:03 AM

I tried that before and it didn't work.  Trying different things, I'll post any results in case it helps someone else.

Aug 15, 2011 at 2:42 PM

I've also tried to use MvcMailer.

The problem is that MvcMailer didnt find View  from current ControllerContext.

Here is simple test method 

 

        public ActionResult Index()
        {
            var view = new ViewResult();
            view.ViewName = "NonExisting";
            view.ExecuteResult(this.ControllerContext);
            return View();
        }

 

In usual Mvc application you will get this error

Server Error in '/' Application.

The view 'NonExisting' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Test/NonExisting.aspx
~/Views/Test/NonExisting.ascx
~/Views/Shared/NonExisting.aspx
~/Views/Shared/NonExisting.ascx
~/Views/Test/NonExisting.cshtml
~/Views/Test/NonExisting.vbhtml
~/Views/Shared/NonExisting.cshtml
~/Views/Shared/NonExisting.vbhtml

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: The view 'NonExisting' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Test/NonExisting.aspx
~/Views/Test/NonExisting.ascx
~/Views/Shared/NonExisting.aspx
~/Views/Shared/NonExisting.ascx
~/Views/Test/NonExisting.cshtml
~/Views/Test/NonExisting.vbhtml
~/Views/Shared/NonExisting.cshtml
~/Views/Shared/NonExisting.vbhtml

 

In Orchard you will get something like this

 

The view 'NonExisting' or its master was not found or no view engine supports the searched locations. The following locations were searched:

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: The view 'PublicMailer/Some' or its master was not found or no view engine supports the searched locations. The following locations were searched:

 

Thats why MvcMailer didnt find view for rendering Email

 

Does anyone can help how to workaround this ?

Oct 6, 2011 at 3:21 AM

I've noticed that MvcMailer works with Orchard 1.3

Oct 6, 2011 at 12:52 PM

@rodpl

When you say notice, do you mean that the errors that have been documented in this discussion are not appearing and that the view is getting passed (i.e., an e-mail is generated and whatever template was set up is being sent)?

Thanks.

Oct 6, 2011 at 7:02 PM

Test method is working correctly. MvcMailer can populate  message body based on Razor template. The only problem is with MasterPage  which doesnt work. This is not problem if you use Partials for header and footer for the view.

Coordinator
Oct 6, 2011 at 7:08 PM

How do you setup mail settings ? in Web.Config ? 

Nov 16, 2011 at 7:12 PM

I've created Views/PublicMailer folder with Some.cshtml template im my module.

I've got also class

 

    public interface IPublicMailer : IDependency
    {
        MailMessage Some();
    }

    public class PublicMailer : MailerBase, IPublicMailer
    {
        public MailMessage Some()
        {
            var mailMessage = new MailMessage { Subject = "ssss" };
            this.PopulateBody(mailMessage, viewName: "Some");
            return mailMessage;
        }
    }

.. somewhere in Module. This class uses MvcMailer only for creating MailMessage

For sending an email Ive got my own class which takes smtp parameters from Orchard settings.

    public class EmailMessagingService : IMessagingService
    {
        private IPublicMailer publicMailer;

        private IOrchardServices orchardServices;
        public EmailMessagingService(IPublicMailer publicMailer, IOrchardServices orchardServices)
        {
            this.orchardServices = orchardServices;
            this.publicMailer = publicMailer;
            this.Logger = NullLogger.Instance;
        }

        public ILogger Logger { get; set; }


        public void SendSome()
        {
            var message = this.publicMailer.Some();
            message.To.Add("Some@mail.to");
            message.Bcc.Add(AdminEmail);
            message.Subject = "blabla";

            Send(message);
        }

        #endregion

        #region Methods

        private void Send(MailMessage message)
        {
            var smtpSettings = orchardServices.WorkContext.CurrentSite.As<SmtpSettingsPart>();

            // can't process emails if the Smtp settings have not yet been set
            if (smtpSettings == null || !smtpSettings.IsValid())
            {
                Logger.Error("Ustawienia SMTP zdefiniowane w portalu s&#261; nieprawid&#322;owe");
                return;
            }

            using (var smtpClient = new SmtpClient(smtpSettings.Host, smtpSettings.Port))
            {
                smtpClient.UseDefaultCredentials = !smtpSettings.RequireCredentials;
                if (!smtpClient.UseDefaultCredentials && !String.IsNullOrWhiteSpace(smtpSettings.UserName))
                {
                    smtpClient.Credentials = new NetworkCredential(smtpSettings.UserName, smtpSettings.Password);
                }

                if (message.To.Count == 0)
                {
                    Logger.Error("Recipient is missing an email address");
                    return;
                }

                smtpClient.EnableSsl = smtpSettings.EnableSsl;
                smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;

                message.From = new MailAddress(smtpSettings.Address);
                message.IsBodyHtml = message.Body != null && message.Body.Contains("<") && message.Body.Contains(">");

                try
                {
                    smtpClient.Send(message);
                    Logger.Debug("Message sent to {0} with subject: {1}", message.To[0].Address, message.Subject);
                }
                catch (Exception e)
                {
                    Logger.Error(e, "An unexpected error while sending a message to {0} with subject: {1}", message.To[0].Address, message.Subject);
                }
            }

        }

 

 

 

 

 

Dec 19, 2011 at 2:23 AM
rodpl wrote:

Test method is working correctly. MvcMailer can populate  message body based on Razor template. The only problem is with MasterPage  which doesnt work. This is not problem if you use Partials for header and footer for the view.

Update:

To use Master Pages in MvcMailer you have to inherit view page from Mvc's WebViewPage not form Orchard's one. Like this:

 

@inherits System.Web.Mvc.WebViewPage<object>
@{
  Layout = "_Layout.cshtml";
}