SilverStripe 3.1 routing and Link function - hyperlink

I'm updating a SilverStripe website from 2.4 to 3.1.
I have many Links functions used in controllers and views.
The problem is that when I set routes.yml like this
Director:
rules:
'gottesdienste//$Action/$ID/$OtherID' : LiturgiesPage_Controller
'veranstaltungen//$Action/$ID/$OtherID' : ArrangementsPage_Controller
Links in my site change from
gottesdienste/archive/2012
to
LiturgiesPage_Controller/archive/2012
How do I fix this?

You need to implement a custom Link() method that uses the base string you want.
public function Link($action = null) {
return self::join_links('gottesdienste', $action);
}

Related

Override UrlResolver

I've been working through the Angular 2 tutorial (in TypeScript) when I got stuck on this part. They now want to separate templates into separate files. That's fine and dandy, but I've got a quirky setup: I'm serving up the files with ASP.NET MVC, and it's refusing to serve up the file from the Views folder. Fair enough: I anticipate needing to serve up Razor (.cshtml) files, so I'm happy to try and hack this out instead of just whitelisting .html.
I've worked with Angular 1 before, and in this situation I used a decorator to modify the $templateRequest service to modify the template URLs into something MVC will accept, and then I set up MVC to serve up the corresponding files. Quite clever work if I do say so myself. So I just need to replicate this in Angular 2, right? That should be easy.
Wrong. So wrong. After some guesswork Googling I found UrlResolver which, after some client-side debugging I confirmed, is the class I want to extend. The documentation even says:
This class can be overridden by the application developer to create custom behavior.
Yes! This is exactly what I want to do. Unfortunately no examples of how to override it have been supplied. I've found this DemoUrlResolver and this MyUrlResolver, but I can't figure out how or if either of them works. I've tried the multiple approaches to supplying my custom provider (see this answer) including the bootstrap and providers (on the module and the app component) approaches all to no avail.
How do I override UrlResolver?
I assume it doesn't matter, but at the moment my extension does nothing but defer to the base class:
class MvcUrlResolver extends UrlResolver {
resolve(baseUrl: string, url: string): string {
return super.resolve(baseUrl, url);
}
}
Interesting question. Since it is part of compiler it makes sense that it would not be instantiated along with other application components, and after some research and analyzing angular's code I found the solution. You need to provide it directly in the call to platformBrowserDynamic(). In this case it will be merged into default compiler options and will be used by injector that instantiates compiler.
import { COMPILER_OPTIONS } from '#angular/core';
class MvcUrlResolver extends UrlResolver {
resolve(baseUrl: string, url: string): string {
let result = super.resolve(baseUrl, url);
console.log('resolving urls: baseUrl = ' + baseUrl + '; url = ' + url + '; result = ' + result);
return result;
}
}
platformBrowserDynamic([{
provide: COMPILER_OPTIONS,
useValue: {providers: [{provide: UrlResolver, useClass: MvcUrlResolver}]},
multi: true
}]).bootstrapModule(AppModule);

Role-based navigation display in MVC4 Bootstrap Sample

How are you supposed to conditionally display menu items based on roles in the Bootstrap Sample project? I was thinking of doing the following
Implement INavigatonRouteFilter - really just implementing the shouldRemove(Route navigationRoutes) method - by getting the default controller/action for the route and seeing if the user is authorized
Call NavigationRoutes.Filters.Add(myAuthorizationFilter) after configuring the NavigationRoutes in App_Start
There are two problems I see with this approach:
I don't actually know how to do the first step unless I add in a bunch of conditional statements to check for Controller's name explicitly
This seems like it could make NavigationRoutes.Filters very hard to deal with once there are a lot of filters or a desire for more modularity later on
I don't know that I've explained the problem clearly enough, but basically I want to use what is provided in the Bootstrap sample to implement authorization-based navigation menu display if at all possible. Using INavigationRouteFilter just seemed like the most natural way to do so.
For those looking for an answer or at least a quick fix.
Here's what I've come up with after 5 minutes and I most certainly haven't though about any side effects this may have.
routes.MapNavigationRoute<HomeController>("Index", c => c.Index())
.FilterRoute(() => !WebSecurity.IsAuthenticated);
You can either do all your filtering in your call to FilterRoute() or you can add more extension methods to save you some characters.
I'm thinking of .RequireRole("Adiministrators"); that calls WebSecurity.RequireRoles() in turn (or HttpContext.Current.User.IsInRole()) etc.
public static NavigationRouteBuilder FilterRoute(this NavigationRouteBuilder builder, Func<bool> func)
{
var currentRoute = builder._parent;
NavigationRoutes.Filters.Add(new BootstrapAuthorizationFilter(builder, x =>
{
if (x == currentRoute)
return func();
else
return false;
}));
return builder;
}
and BootstrapAuthorizationFilter is just a class implementing INavigationRouteFilter that calls func() in its ShouldRemove() method
public class BootstrapAuthorizationFilter : INavigationRouteFilter
{
private NavigationRouteBuilder builder;
private Func<NamedRoute, bool> func;
public BootstrapAuthorizationFilter(NavigationRouteBuilder builder, Func<NamedRoute, bool> func)
{
this.builder = builder;
this.func = func;
}
public bool ShouldRemove(Route navigationRoutes)
{
if (navigationRoutes is NamedRoute)
return func(navigationRoutes as NamedRoute);
return false;
}
}
Clearly nothing fancy and I'm not sure if I'd use it in production.
But I think is simple enough and works (for the cases I tested).
Having said that, I hope the new routing functionality is going to be released soon :)

How can i call a Method in App.xaml.cs from Mainpage.xaml.cs in wp7

I have a quick question here. Can any one please help me to sort out this problem.
I'm new to windows Phone. I'm developing an Application where i can change my Font styles for the entire application . I have three different Resource file to set three different types of font styles. The resource file are set to application in App.xaml.cs file. Now i need to support to change the styles in Application run time from Application Changestyles page. So i need to call the method in app.xaml.cs from changestyles.xaml.cs page.
private void LoadResourceDictionary()
{
var dictionaries = new ResourceDictionary();
string source = String.Format("/Testapp;component/Large.xaml");
var themeStyles = new ResourceDictionary { Source = new Uri(source, UriKind.Relative) };
dictionaries.MergedDictionaries.Add(themeStyles);
App.Current.Resources = dictionaries;
ResourceDictionary appResources = App.Current.Resources;
}
I need to call this method to set the another resource to my application in run time.
Is it possible to resolve this issue?
Make this method public static and you can call it from everywhere: App.LoadResourceDictionary();
Here is a generic how to based on the code I needed for an app I am writing. I know the circumstances are slightly different, but it may help someone else who is after a similar solution:
In MainPage.xaml you create a method as follows:
public static void InMainPage()
{
System.Diagnostics.Debug.WriteLine("Hi I am a method in MainPage.xaml.cs");
}
Now in App.xaml.cs you can call it in any method as such:
MainPage.InMainPage();
AND IT WORKS FOR YOU CIRCUMSTANCE IN THE REVERSE DIRECTION
In App.xaml.cs you create a method as follows:
public static void InAppXaml()
{
System.Diagnostics.Debug.WriteLine("Hi I am a method in App.xaml.cs");
}
Now in Mainpage.xaml.cs you can call it in any method as such:
App.InAppXaml();
Tested and works well. Hope it helps!

asp.net MVC: localization

I have my target language in Session["lang"], which is either "en" or "it". I have added this to the Site.master:
<script runat="server">
void Page_Load(object sender, EventArgs e) {
string lang = Session["lang"].ToString();
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture(lang);
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture(lang);
}
</script>
Then I'd like to invoke a resource string like this:
<asp:Label ID="Label1" runat="server" Text="<%$ Resources:Global, test %>"></asp:Label>
I have two files in the App_GlobalResources, named Global.resx and Global.en.resx.
The problems is that no matter what is in the lang variable, I always get the results from the main Global.resx, and I never get the english version from Global.en.resx
I am doing this wrong entirely??
I tried putting the System.Threading... part in the Application_PreRequestHandlerExecute method in Global.asax.cs but the result was the same.
Thanks
PS: I am asking about a way to make this work in a simple way. If I was to use the complicate way, I'd go with this: http://helios.ca/2009/05/27/aspnet-mvc-and-localization/
i had the same dilema(how to implement localization) in my asp.net mvc app.
I followed the instructions posted here and it works like a charm.
So i created a folder named Localization under Content and then i create Resources resx files for each language i want to translate. Keep in mind that there is a convention for the resx file names. ie
Resources.resx is the default fall back for everything.
Resources.en-GB.resx is for english GB
Resources.en-US.resx is for english US
etc.
Just make sure you follow the instructions posted in the link to embed and make the Resources available in all places in your app (views, controllers etc)
Edit:
I want to add that i ommited this line from web.config since i wanted to manually set the local from my app.
<globalization uiCulture="auto" culture="auto"/>
Instead i have created the following class:
public class SmartController : Controller
{
public SmartController()
{
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
}
}
All controllers inherit from this class.
Since this is an administrative set of the locale i have to set it from my apps settings. You could read it from Cookies and set it, or otherwise. This is imo the simplest solution for localization that i have encountered so far.
Once implemented you can refer to any string you add by the following simple line of code, no extra code needed.
<%= Resources.Strings.TranslatedTerm %>
I bet this one is a duplicate.
Anyway - all you need is here (assuming that you are using webforms viewengine (might work with others too, haven't investigated)).
Oh well... here goes my 'summary':
Helpers are just a part. You need to do some modifications with your default view engine too . On createview/createpartialview it should return localizationwebformview which adds a path key to viewdata which is used by htmlhelper to find resourceexpressionsfields and pass them to localizationhelpers class which retrieves desired value.
Little bonus=>
This might be handy if you don't want to recreate resource folders for view subfolders
(in case you modify viewengine.view/partialviewlocationformats):
private static string ReformatVirtualPath(string virtualPath)
{
//This allows NOT to duplicate App_localResources directory
// ~/Views/Shared/Partial/Some/BulltihS/_View.ascx
// turns into =>
// ~/Views/Shared/_View.ascx
var start = #"(~(/?\w*/?){2})";
var end = #"(\w*.as(c|p)x)";
start = Regex.Match(virtualPath, start).Value;
end = Regex.Match(virtualPath, end).Value;
return start + end;
}
usage:
internal static ResourceExpressionFields GetResourceFields
(string expression, string virtualPath)
{
virtualPath = ReformatVirtualPath(virtualPath);
var context = new ExpressionBuilderContext(virtualPath);
var builder = new ResourceExpressionBuilder();
return (ResourceExpressionFields)
builder.ParseExpression(expression, typeof(string), context);
}
EDIT:
but it might be a good idea to avoid App_GlobalResources and App_LocalResources as K. Scott Allen suggests (check Konstantinos answer).

URL-encode parameters in ActionLink?

I have the following route registered;
routes.MapRoute(
"LocationsByArea",
"Locations/{system}/{storage}/{area}",
new { controller = "StorageLocation", action = "Index" },
null
);
...and the following code in my view;
<%= Html.ActionLink("Platser", "Index", "StorageLocation", new { system = Model.System, storage = Model.Storage, area = item.Name }, null)%>
My problem is when the "area = item.Name" contains a colon, e.g. "Area 4:1". If I click the rendered link I get HTTP-error 400, Bad reqest. I guess I have to encode my area parameter in some way, but I cant figure out how. Any help is apreciated.
Thanks!
The built-in encoding/decoding does not work, so I suggest you roll your own, like this:
namespace MyProject.Helpers
{
public static class JobNameHelper
{
public static string JobNameEncode(string jobname)
{
return jobname.Replace(":", "---colon---");
}
public static string JobNameDecode(string jobname)
{
return jobname.Replace("---colon---", ":");
}
}
}
Can you not just use
Server.UrlEnconde(item.Name)
Or am I missing something?
In your routing you may have to use Server.UrlDecde as well although I think It should decode for you on request.
Try using the Routing Debugger to see what the url router is getting passed, then you can see where the decoding needs to happen
ASP.NET 3.5 SP1 and earlier have a number of restrictions on which URLs are valid. In ASP.NET 4 most of these issues have been fixes (or are at least customizable via web.config). I think that the colon character, even when encoded, might not be allowed in ASP.NET 3.5 SP1 and earlier due to security concerns. Allowing colons can be a security problem when performing file checks since they are a little-known syntax for NTFS Alternate Data Streams.
I recommend trying to choose a character other than a colon for these purposes. Maybe a comma, semi-colon, or equal sign might work instead?

Resources