Setting Sitecore.Context.Language gives me Items in the wrong language one time - asp.net

I have this page (item) in my Sitecore website that is viewed from a Facebook page tab. It's a rather simple page, but I have an issue with Sitecore giving me the wrong language on first load, then subsequent loads are ok.
This item runs through this controller:
//
// GET: /Portfolio/
public override ActionResult Index()
{
var appId = "*****";
var appSecret = "*****";
// Defaults to en
var requestLanguage = "en";
// Get language from FB
if (Request.Form["signed_request"] != null)
{
if (Request.Url.Host.ToLower().Contains("local")) {
appId = "*****";
appSecret = "*****";
}
var fbUser = new Facebook.FacebookClient
{
AppId = appId,
AppSecret = appSecret
};
var parsedSignedRequest = JObject.Parse(fbUser.ParseSignedRequest(Request.Form["signed_request"]).ToString());
if (parsedSignedRequest != null)
{
requestLanguage = parsedSignedRequest["user"]["locale"].ToString().StartsWith("fr") ? "fr-CA" : "en";
} // Else: Request can't be parsed, something is wrong
} // Else: Probably not in FB
// ?l=***** can bypass language setting
if (!string.IsNullOrWhiteSpace(Request.QueryString["l"]))
{
requestLanguage = Request.QueryString["l"];
}
Context.Language = Language.Parse(requestLanguage);
// Views will need this
ViewBag.requestLanguage = requestLanguage;
ViewBag.appId = appId;
return base.Index();
}
When I debug this, it works perfectly. I'm setting Sitecore's Context.Language to that of what the Facebook user uses (I have french and english content).
Now onto views, I have this master layout that basically just (other than boring html markup) places the placeholder:
#Html.Sitecore().Placeholder("fb-body")
Finally, my view rendering looks like this:
#using Sitecore.Globalization
#using Sitecore.Data.Items
#model RenderingModel
#{
// I checked this and the context language here is always correctly set, even on first load (controller did that)
// Sitecore.Context.Language = Language.Parse(ViewBag.requestLanguage);
var all = "All";
var back = "Back";
var projetTitle = "the project";
var servicesTitle = "services";
// Since my language is correctly set, this works fine
if (Language.Current.Name.ToLowerInvariant().Contains("fr"))
{
all = "Tous les projets";
back = "Retour";
projetTitle = "le projet";
servicesTitle = "services";
}
var portfolio = Model.Item.Parent.Children.FirstOrDefault(x => x.TemplateName == "Portfolio");
var wrongLanguage = portfolio.Language;
var wrongLanguage2 = Model.Item.Language;
}
Here when I pull my portfolio node, it's in the wrong language. If I look in Model.Item.Language, I also get the wrong language.
What am I missing here, is there something else I need to tell Sitecore so that he understands my language? This also sort of looks like a caching issue... Where do I look to solve this?
Thanks!

I am not sure what base controller you have set up, but it seems you make use of the PageContext.Current.PageView either by returning it yourself or inheriting from the SitecoreController. I bumped into some issues trying to reproduce the problem so I ended up with this:
public class TestController : Controller
{
public ActionResult Index()
{
Sitecore.Context.SetLanguage(Language.Parse("nl-NL"), true);
var view = (PageContext.Current.PageView) as RenderingView;
var renderer = view.Rendering.Renderer as ViewRenderer;
renderer.ViewPath = "/Views/Test/Index.cshtml";
return View(view);
}
}
And in my view I render:
#model Sitecore.Mvc.Presentation.RenderingModel
Context language is: #Sitecore.Context.Language<br/>
Model language is: #Model.Item.Language
First time result:
Context language is: nl-NL
Model language is: en
Second time result:
Context language is: nl-NL
Model language is: nl-NL
The problem is that the language resolver first sets the language to the default (being 'en') and when it gets into your controller action it has already fetched the Item with 'en'. So your language adjustment comes in too late. The second time it is loaded in correct language because the adjusted language was persisted in a cookie.
A proper place to set the language would be the LanguageResolver. You can override/extend the LanguageResolver and replace it in the httpRequestBegin pipeline. That way you will also prevent localized content being cached with the default language in the cachekey.

Related

Switching feature in Handlebars Templates

So, we have created many templates using handlebars. Out of the many, we have one handlebar where we would like to make some changes that should only go live after a certain date. To do so, we wanted to create sort of a toggle switch, something like:
{{if switch on}}
display new content
{{else}}
display old content
Below is the generic template parser where I am trying to create a switch that I can inject in the if part of my template. Any suggestions?
/**
* Creates HTML output of the specified context for the given templateId and templateVersion combination
* we implicitly assume certain json fields (template specific) to be present in the content
*/
#Timed("handlebarsService.parseTemplateToHtml")
fun parseTemplateToHtml(htmlTemplateLocation: String, model: Map<String, Any>, locale: Locale): String {
val modelWithLanguage = model.toMutableMap()
modelWithLanguage[languageTag] = locale.language
//modelWithLanguage[switch] = "off"
val context = Context.newBuilder(modelWithLanguage)
.resolver(MapValueResolver.INSTANCE)
.build()
val template = try {
handlebars.compile(htmlTemplateLocation)
} catch (e: IOException) {
throw PdfGenerationException(e, "Internal error while compiling template")
}
return try {
template.apply(context)
} catch (e: IOException) {
throw PdfGenerationException(e, "Internal error while applying template")
}
}
}
private const val languageTag = "languageTag"
//private const val switch ="off"

I want to allow forward slash into SENAME

I want to allow forward slash into SENAME of my nop project. How can i do this using customisation?
for example,
I want product url like "/product/htc-one-m8-android-l-50-lollipop" instead of "/htc-one-m8-android-l-50-lollipop"
I want category url like "/category/desktops" instead of "/desktops"
I am using nopcommerce 4.3 version.
sample code
endpointRouteBuilder.MapDynamicControllerRoute<SlugRouteTransformer>("SeName}");
I am not getting call into this TransformAsync method. i want to get call here when i add "/product/sename" into url
public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
}
You can register routes for Product/ and Category/ path in GenericUrlRouteProvider like:
endpointRouteBuilder.MapDynamicControllerRoute<SlugRouteTransformer>("Product/{SeName}");
endpointRouteBuilder.MapDynamicControllerRoute<SlugRouteTransformer>("Category/{SeName}");
If you want your existing links to be displayed correctly your also want to update default Product and Category routes and your register routes method should look like:
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
{
var pattern = "{SeName}";
var productPattern = "Product/{SeName}";
var catgoryPattern = "Category/{SeName}";
if (DataSettingsManager.DatabaseIsInstalled)
{
var localizationSettings = endpointRouteBuilder.ServiceProvider.GetRequiredService<LocalizationSettings>();
if (localizationSettings.SeoFriendlyUrlsForLanguagesEnabled)
{
var langservice = endpointRouteBuilder.ServiceProvider.GetRequiredService<ILanguageService>();
var languages = langservice.GetAllLanguages().ToList();
pattern = "{language:lang=" + languages.FirstOrDefault().UniqueSeoCode + "}/{SeName}";
productPattern = "{language:lang=" + languages.FirstOrDefault().UniqueSeoCode + "}/{SeName}";
catgoryPattern = "{language:lang=" + languages.FirstOrDefault().UniqueSeoCode + "}/{SeName}";
}
}
endpointRouteBuilder.MapDynamicControllerRoute<SlugRouteTransformer>(pattern);
endpointRouteBuilder.MapDynamicControllerRoute<SlugRouteTransformer>(productPattern);
endpointRouteBuilder.MapDynamicControllerRoute<SlugRouteTransformer>(catgoryPattern);
//and default one
endpointRouteBuilder.MapControllerRoute(
name: "Default",
pattern: "{controller=Home}/{action=Index}/{id?}");
//generic URLs
endpointRouteBuilder.MapControllerRoute(
name: "GenericUrl",
pattern: "{GenericSeName}",
new { controller = "Common", action = "GenericUrl" });
//define this routes to use in UI views (in case if you want to customize some of them later)
endpointRouteBuilder.MapControllerRoute("Product", productPattern,
new { controller = "Product", action = "ProductDetails" });
endpointRouteBuilder.MapControllerRoute("Category", catgoryPattern,
new { controller = "Catalog", action = "Category" });
endpointRouteBuilder.MapControllerRoute("Manufacturer", pattern,
new { controller = "Catalog", action = "Manufacturer" });
endpointRouteBuilder.MapControllerRoute("Vendor", pattern,
new { controller = "Catalog", action = "Vendor" });
endpointRouteBuilder.MapControllerRoute("NewsItem", pattern,
new { controller = "News", action = "NewsItem" });
endpointRouteBuilder.MapControllerRoute("BlogPost", pattern,
new { controller = "Blog", action = "BlogPost" });
endpointRouteBuilder.MapControllerRoute("Topic", pattern,
new { controller = "Topic", action = "TopicDetails" });
//product tags
endpointRouteBuilder.MapControllerRoute("ProductsByTag", pattern,
new { controller = "Catalog", action = "ProductsByTag" });
}
Update:
In order to allow / in product name, there are multiple changes you need to make.
Allow / in sename, to do that UrlRecordService has GetSeName method and in okChars variable you want to add / as one of the valid characters.
Fix your call related to dynamic routes. You have { missing in your code. It should be:
endpointRouteBuilder.MapDynamicControllerRoute<SlugRouteTransformer>("{SeName}");
Update SlugRouteTransformer's TransformAsync method to unescape URL before searching for matching Url records using:
var urlRecord = _urlRecordService.GetBySlug(Uri.UnescapeDataString(slug));
I believe that is all and after that you should be able to allow / in your entity name. Be aware that this might break existing pages and links might not work perfectly all the time. Also, if you look closely, your product/category url will have %2F in the URL, if you want to change that you will have to unesacpe all the links before rendering, something like:
#Model.Name BUT IT WILL BREAK A LOT OF THINGS!

xamarin shell: dynamically generate pages for routes

I was trying out the newly released xamarin shell. Basically, I am trying out how to map the route to a dynamically generated page. I noticed the RegisterRoute has an overload that requires an object of type RouteFactory. I created a demo class for this:
class NavMan : RouteFactory
{
private readonly string title;
public NavMan(string title) : base()
{
this.title = title;
}
public override Element GetOrCreate()
{
return new ContentPage
{
Title = "tit: " + title,
Content = new Label { Text = title }
};
}
}
Now, in the App.cs I create a register a demo route:
Routing.RegisterRoute("batman", new NavMan("I am batman"));
I have tried several variations of setting up the Shell object. I mostly get blunt null pointers and need to guess what to change.
As of now, my App class's constructor has the following code:
var sea = new Shell();
var theItem = new FlyoutItem
{
IsEnabled = true,
Route = "batman"
};
sea.Items.Add(theItem);
sea.CurrentItem = theItem;
MainPage = sea;
This gives me a blunt null pointer too. All I am trying for now is to display the page of route "batman". Even a flyout or tab isn't mandatory.
Update
While not the aim, I at least got the app opening with the following:
var sea = new Shell();
Routing.RegisterRoute("batman", new NavMan("I am batman"));
var theItem = new ShellContent {
Title = "hello 20",
Route = "batman2",
Content = new ContentPage {
Content = new Button {
Text = "Something shown",
Command = new Command(async () => await Shell.Current.GoToAsync("//batman"))
}
}
};
sea.Items.Add(theItem);
sea.CurrentItem = theItem;
MainPage = sea;
On clicking the button, it now shows me the following exception and never calls the GetOrCreate function.
System.Exception: <Timeout exceeded getting exception details>
Update 3
Basically, I am looking for a way to bind a route to ShellContent in a way that it simply displays I mention the route property and it displays a page in the route. It doesn't make sense that I need to mention a route AND mention a content template for the page. The route is already mapped to a page.

How to get a diff of pending changes to a model in ASP.NET MVC 2

I am working on an ASP.Net MVC app and I want to show a confirmation page after the user edits some data. What I would like to show is a list of the pending changes that the user made to the model.
For example,
Are you sure you want to make the following changes:
FieldName:
Previous Value: XXX
New Value: YYY
I know I can read my stored value from the database and compare it with the POSTed object but I want this to work generally. What would be some good ways to approach this?
To clarify, I am looking for a general way to get a "diff" of the pending changes. I already know how to get the previous and pending changes. Kind of like how TryUpdateModel() can attempt to update any Model with posted values. I'd like a magical GetPendingModelChanges() method that can return a list of something like new PendingChange { Original = "XXX", NewValue = "YYY"} objects.
You might be doing this already but I wouldn't send my model to the view, create a viewmodel. In this case I would map the model data to the viewmodel twice, my viewmodel might contain OrderInput and OrderInputOrig. Then stick OrderInputOrig in hidden fields. On post back you can compare the values and then redirect, if something changed, to a display view with the original and the changes for confirmation.
Maybe something like this:
[HttpPost]
public ActionResult Edit(CustomerInput cutomerInput)
{
var changes = PublicInstancePropertiesEqual(cutomerInput.OriginalCustomer, cutomerInput.Customer);
if (changes != null)
{
cutomerInput.WhatChangeds = changes;
return View("ConfirmChanges", cutomerInput);
}
return View();
}
public ActionResult ConfirmChanges(CustomerInput customerInput)
{
return View(customerInput);
}
from: Comparing object properties in c#
public static Dictionary<string, WhatChanged> PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
Dictionary<string, WhatChanged> changes = null;
if (self != null && to != null)
{
var type = typeof(T);
var ignoreList = new List<string>(ignore);
foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
{
if (!ignoreList.Contains(pi.Name))
{
var selfValue = type.GetProperty(pi.Name).GetValue(self, null);
var toValue = type.GetProperty(pi.Name).GetValue(to, null);
if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
{
if (changes == null)
changes = new Dictionary<string, WhatChanged>();
changes.Add(pi.Name, new WhatChanged
{
OldValue = selfValue,
NewValue=toValue
});
}
}
}
return changes;
}
return null;
}
Coming in very late here, but I created a library to do this on MVC models and providing "readable" diffs for humans using MVC ModelMetadata:
https://github.com/paultyng/ObjectDiff
It gives me output when I save a Model similar to:
Status: 'Live', was 'Inactive'
Phone: '123-456-7898', was '555-555-5555'
Etc.
use the TempData Dictionary.
TempData["previousValue"];
TempData["newValue"];

Flex prevent URL encoding of params with HTTPRequest

I'm trying to port an existing AJAX app to Flex, and having trouble with the encoding of parameters sent to the backend service.
When trying to perform the action of deleting a contact, the existing app performs a POST, sending the the following: (captured with firebug)
contactRequest.contacts[0].contactId=2c33ddc6012a100096326b40a501ec72
So, I create the following code:
var service:HTTPService;
function initalizeService():void
{
service = new HTTPService();
service.url = "http://someservice";
service.method = 'POST';
}
public function sendReq():void
{
var params:Object = new Object();
params['contactRequest.contacts[0].contactId'] = '2c33ddc6012a100097876b40a501ec72';
service.send(params);
}
In firebug, I see this sent out as follows:
Content-type: application/x-www-form-urlencoded
Content-length: 77
contactRequest%2Econtacts%5B0%5D%2EcontactId=2c33ddc6012a100097876b40a501ec72
Flex is URL encoding the params before sending them, and we're getting an error returned from the server.
How do I disable this encoding, and get the params sent as-is, without the URL encoding?
I feel like the contentType property should be the key - but neither of the defined values work.
Also, I've considered writing a SerializationFilter, but this seems like overkill - is there a simpler way?
Writing a SerializtionFilter seemed to do the trick:
public class MyFilter extends SerializationFilter
{
public function MyFilter()
{
super();
}
override public function serializeBody(operation:AbstractOperation, obj:Object):Object
{
var s:String = "";
var classinfo:Object = ObjectUtil.getClassInfo(obj);
for each (var p:* in classinfo.properties)
{
var val:* = obj[p];
if (val != null)
{
if (s.length > 0)
s += "&";
s += StringUtil.substitute("{0}={1}",p,val);
}
}
return s;
}
}
I'd love to know any alternative solutions that don't involve doing this though!

Resources