EPiServer 7 MVC IDisplayModes - episerver-7

Trying to setup a Mobile Channel for use in Edit Mode in EPiServer 7.
Been following this link
http://world.episerver.com/Documentation/Items/Developers-Guide/EPiServer-CMS/7/Content/Display-Channels/
Created an Initialization module
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class DisplayModesInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
if (context.HostType == HostType.WebApplication)
{
System.Web.WebPages.DisplayModeProvider.Instance.Modes.RemoveAt(0);
context.Locate.DisplayChannelService()
.RegisterDisplayMode(new DefaultDisplayMode(RenderingTags.Mobile)
{
ContextCondition = (r) => r.Request.Browser.IsMobileDevice
});
}
}
public void Preload(string[] parameters) { }
public void Uninitialize(EPiServer.Framework.Initialization.InitializationEngine context) { }
}
As you can see I tried removing the existing "Mobile" display mode that exists to be replaced with the one created through the EPiServer DisplayChannelService().
Just browsing to the homepage works ok but when I force the userAgent to be a mobile browser it does hit the correct view... i.e. Index.mobile.cshtml
However it appears to still be looking for the _Layout.cshtml instead of _Layout.mobile.cshtml and even at that it fails to find it.
The file "~/Views/Shared/_Layout.cshtml" could not be rendered, because it does not exist or is not a valid page.
Anyone successfully create a mobile IDisplayMode for MVC through the EPiServer DisplayChannelService ?
Also if I explicitly set the layout in the mobile view
#{
Layout = "~/Views/Shared/_Layout.mobile.cshtml";
}
If fails to find that also ?
The file "~/Views/Shared/_Layout.mobile.cshtml" could not be rendered, because it does not exist or is not a valid page.
both the _Layout and _Layout.mobile DO exist in that location ?

Managed to get it working.
Discovered that _ViewStart.cshtml had the following set:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
DisplayModeProvider.Instance.RequireConsistentDisplayMode = true;
}
So I removed the DisplayModeProvider.Instance.RequireConsistentDisplayMode = true; and it now works.
Not sure why this was causing the problem as there are both mobile and desktop views for the homepage and also mobile and desktop layouts ?

Related

Move one folder back in server MVC

I need to display an image in another project from an MVC project. While accessing the image from MVC, i need to go a folder back ( or no need of the View folder name ) in the path. Following is the controller code for the view Views\Booking
public virtual ActionResult ResendEticket(BookingViewModel resendTicketModel)
{
if (ModelState.IsValid)
{
if(!string.IsNullOrEmpty(resendTicketModel.Email))
{
BookingService resendEticket = new BookingService();
string result = resendEticket.ResendEticket(Convert.ToInt32(resendTicketModel.BookingReference), resendTicketModel.Email);
if(!string.IsNullOrEmpty(result))
{
ModelState.AddModelError(string.Empty, result);
}
}
}
ModelState.AddModelError(string.Empty, "Unable to do resend ticket due to system error");
return RedirectToAction("Index", "Booking");
}
The path i am getting in the receiving side currently is
C:\ Projects\Booking\Invoices\Templates\images\logo_eticket.png
What i need is C:\ Projects\Invoices\Templates\images\logo_eticket.png
In the receiving side , i am using following code
TaxInvoiceFilePath = HttpContext.Current.Server.MapPath(mtxr.TaxInvoiceFilePath);
I may not be able to change anything on above line, as this is shared with other applications.
How can i move HttpContext.Current.Server.MapPath one step back so that all will be fine

What happens when you return View when should be PartialView?

In a View I am calling an action that returns a View
View:
Html.RenderAction("Read", "Stats", new { Module = statsModel.Module, Name = statsModel.Name });
Controller:
public ActionResult Read(Module module, string name, bool showStatsItems = true)
{
eRPortalEntities db = new eRPortalEntities();
StatsPanelService service = new StatsPanelService(db, UserID);
StatsPanelViewModel spv = service.Read(module, name);
spv.ShowStatsItems = showStatsItems;
return View("StatsPanel", spv);
}
This unfortunately causes some of my Bootstrap functionality to break. Such as dropdowns and modals not toggling.
If instead I have the controller return a PartialView, everything works as expected
return PartialView("StatsPanel", spv);
I'm not looking for a specific reason why my bootstrap stopped working but more of an explanation of... Why would this cause issues in general?
Unless you explicitly specify the Layout to be null, When you do return View("StatsPanel"), Razor view engine will render the view content inside the Layout (_Layout.cshtml) similar to how you render a page normallly. That means, it will include all those scripts & Css in the head section again. That could be the reason it is messing up your markup.
Using PartialView() method seems appropriate in your use case. If you still want to use the View() method, you can explicitly define layout as null in your StatsPanel.cshtml view like this
#{ Layout = null; }

Orchard - Querying the Part On A Layout File

I’ve gotten stuck on an “Object reference not set to an instance of an object” error message.
We’re trying to use a PagePart field that is attached to the Page type to dynamically link a CSS file in the HEAD of a layout file. See below code.
<!-- DYNAMIC CSS-->
var contentItem = Model.ContentItem;
var pagePart = (PagePart)contentItem.PagePart;
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName))
{
<link ref="#Url.Content("/Themes/MyTheme/Styles/festival-programs/" + pagePart.FestivalProgramName + ".css")" rel="stylesheet" type="text/css">
}
This is in a file called:
Layout.cshtml
Something is wrong about this (obviously) since pagePart is “null” when I Attach to Debugger and look. I get that the Layout file doesn’t know that it’s associated with a “Page” Content Type but this layout is only used with Pages. Anyway, this is very similar to code that works elsewhere in our Orchard site. Any help or advice is hugely appreciated!
Thanks, T
In the Layout, the Model is the Layout object. It has nothing to do whatsoever with whatever content is going to get rendered into the Content zone.
I think what you are trying to do should be done by overriding the Page template (Content-Page.Detail.cshtml). (Note the Detail part, you probably don't want to import every css when displaying multiple pages in summary)
In there you can do:
#{
var contentItem = Model.ContentItem; // The Page content item
var pagePart = contentItem.Page; // Note that casting to PagePart won't work, because it does not exist
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName.Value))
{
// "Orchard's" way to include styles
Style.Include("festival-programs/" + pagePart.FestivalProgramName.Value + ".css");
}
}
EDIT:
What you probably should do (I assume not every page is a festival page) is create a new Content Type: FestivalPage. Then attach the following parts to this content type (same as the Page content type):
Common
Publish later
Title
Autoroute
Body (Orchard 1.8.1 and lower)
Layout (Orchard 1.9.x)
Tags
Localization
Menu
And your field:
FestivalProgramName
Then create an alternate Content-FestivalPage.Detail.cshtml with the following content:
#using Orchard.Utility.Extensions;
#{
if (Model.Title != null) {
Layout.Title = Model.Title;
}
Model.Classes.Add("content-item");
var contentTypeClassName = ((string)Model.ContentItem.ContentType).HtmlClassify();
Model.Classes.Add(contentTypeClassName);
var tag = Tag(Model, "article");
var contentItem = Model.ContentItem; // The FestivalPage content item
var pagePart = contentItem.FestivalPage; // The FestivalPage part with the FestivalProgramName field
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName.Value))
{
// "Orchard's" way to include styles
Style.Include("festival-programs/" + pagePart.FestivalProgramName.Value + ".css");
}
}
// -- Default Orchard content --
#tag.StartElement
<header>
#Display(Model.Header)
#if (Model.Meta != null) {
<div class="metadata">
#Display(Model.Meta)
</div>
}
</header>
#Display(Model.Content)
#if(Model.Footer != null) {
<footer>
#Display(Model.Footer)
</footer>
}
#tag.EndElement
// ----
This way you won't get in the way with the normal pages of the application.
Thank you so much for responding. Your solution works but not in my case since I use the Layout Selector module and can't override at the Page.Detail level since that would imply one layout - or at least from my perspective it seems that way. I found another option though that does the trick.
We already take advantage of the Handlers class to insert META stuff into the HEAD of the page and thanks to your feedback and this thread Adding an element to page <head> in Orchard CMS, it dawned on me to use the same Handler to insert the CSS link.
PagePartHandler.cs.
using System;
using MyModuleName.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Title.Models;
using Orchard.Data;
using Orchard.UI.Resources;
using Orchard.Utility.Extensions;
namespace MyModuleName.Handlers {
public class PagePartHandler : ContentHandler {
private readonly IResourceManager _resourceManager;
public PagePartHandler(
IRepository<PagePartRecord> repository,
IResourceManager resourceManager) {
_resourceManager = resourceManager;
Filters.Add(StorageFilter.For(repository));
OnGetDisplayShape<PagePart>(RegisterFestivalProgramStyle);
}
private void RegisterFestivalProgramStyle(BuildDisplayContext context, PagePart part) {
if (context.DisplayType != "Detail")
return;
if (String.IsNullOrWhiteSpace(part.FestivalProgramName))
return;
_resourceManager.RegisterLink(new LinkEntry
{
Rel = "stylesheet",
Type = "text/css",
Href = "/Themes/Bootstrap/Styles/festival-programs/" + part.FestivalProgramName + ".css"
});
}
}
}
This uses the tradition link style, not ResourceManifest.cs, but WORKS!

DevExpress CallbackPanel PerformCallback hangs when content includes PageControl or TabControl

I would like to refresh the tabs in my TabControl on demand, when a separate client action occurs on the page. I have placed my TabControl extension (tried PageControl also) within a CallbackPanel, and the EndCallBack event never fires. If ShowLoadingPanel is set to true, you see that the call is hanging because the loading panel never disappears. Both OnBeginCallback and the actual Controller callback action are executed. I assume there are some sort of conflicting callbacks occuring between the panel and tabs but I cannot figure out how to resolve it. If I replace the TabControl with basic html or other simpler DevExpress controls, everything works fine.
TabControl Partial (CallbackTestPageControl.cshtml):
#Html.DevExpress().TabControl(settings => {
settings.Name = "testTabControl";
settings.Width = Unit.Percentage(100);
settings.Tabs.Add("tab 1");
settings.Tabs.Add("tab 2");
settings.Tabs.Add("tab 3");
}).GetHtml()
Panel Partial (CallbackTestPanel.cshtml):
#Html.DevExpress().CallbackPanel(settings =>
{
settings.Name = "cbpTabStrip";
settings.CallbackRouteValues = new { Controller = "Home", Action = "CallbackTestPanel" };
settings.ClientSideEvents.BeginCallback = "OnBeginCallback";
settings.ClientSideEvents.EndCallback = "OnEndCallback";
settings.SetContent(() => Html.RenderPartial("CallbackTestPageControl"));
}).GetHtml()
View (CallbackTest.cshtml):
<script type="text/javascript">
var testId = null;
function ButtonClicked(s, e) {
alert('click');
testId = 1;
if (!cbpTabStrip.InCallback())
cbpTabStrip.PerformCallback();
}
function OnBeginCallback(s, e) {
alert('begin');
e.customArgs["Id"] = testId;
testId = null;
}
function OnEndCallback(s, e) {
alert('end');
if (testId != null)
cbpTabStrip.PerformCallback();
}
</script>
#Html.DevExpress().Button(settings => {
settings.Name = "CallbackButton";
settings.Text = "Callback";
settings.ClientSideEvents.Click = "ButtonClicked";
}).GetHtml()
#Html.Partial("CallbackTestPanel")
Controller (HomeController.cs):
public ActionResult CallbackTest()
{
return View();
}
public ActionResult CallbackTestPanel()
{
int id = !String.IsNullOrEmpty(Request.Params["Id"]) ? int.Parse(Request.Params["Id"]) : 0;
return PartialView("CallbackTestPanel");
}
ADDITIONAL INFO: Also, I have tried updating the DevExpress configuration in the web.config based on other suggestions online. Specifically - updating the enableResourceMerging attribute on the compression element to false rather than true. This seemed to allow the callback to end intermittantly. I really don't want to disable resource merging anyway, so I'm actually glad this didn't provide a reliable solution. So, this is what I currently have:
<devExpress>
<themes enableThemesAssembly="true" styleSheetTheme="" theme="Office2010Silver" />
<compression enableHtmlCompression="true"
enableCallbackCompression="true"
enableResourceCompression="true"
enableResourceMerging="true" />
<settings rightToLeft="false" />
<errors callbackErrorRedirectUrl="" />
</devExpress>
I'm sorry if I wasted anyone's time on this. In the end, the problem was that I had all my non-DevExpress scripts at the bottom of my layout body. I needed to move jQuery into the head prior to the DevExpress scripts. Strangely enough, everything else had been working fine prior to this issue. Thanks to anyone who tried to reproduce.
I know this is old but my issue is I had caching enabled that form i.e.
Once I removed that everything worked. Hope this helps someone else in the future.

Switching between a custom mobile display mode and desktop mode in ASP.NET MVC4

I would like to create a switch to full site link and switch to mobile link. I don't want to force it to go to either of them by using a session variable. I'd like to know if it's possible to do it automatically by using ViewSwitcher Controller.
Here is what I am using for my custom mobile display mode
public class MobileDisplayMode : DefaultDisplayMode
{
public static readonly List<string> MobileList = new List<string>
{
"Android",
"Mobile",
"Opera Mobi",
"Samsung",
"HTC",
"Nokia",
"Ericsson",
"SonyEricsson",
"iPhone"
,"ipod"
, "symbian"
,"android"
,"windows ce"
,"blackberry"
,"palm"
,"opera mini"
};
public MobileDisplayMode()
: base("Mobile")
{
ContextCondition = (context => IsMobile(context, context.GetOverriddenUserAgent()));
}
private bool IsMobile(HttpContextBase context, string useragentString)
{
return context.Request.Browser.IsMobileDevice || MobileList.Any(val => useragentString.IndexOf(val, StringComparison.InvariantCultureIgnoreCase) >= 0);
}
}
Here is my view switcher code - don't worry about IsMobile Property, it's handled the same way as mobile display mode
if (IsMobile == mobile)
{
HttpContext.ClearOverriddenBrowser();
}
else
{
HttpContext.SetOverriddenBrowser(mobile ? BrowserOverride.Mobile : BrowserOverride.Desktop);
}
Add Jquery.Mobile.Mvc package to the project. A mobile layout will be created. Now every mobile view displays a link to the desktop version. Put the code below in desktop view, so the user can returns to mobile.
#Html.ActionLink("Mobile view", "SwitchView", "ViewSwitcher", new { mobile = true, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })

Resources