Sulu CMS - Change the default locale provider - localization

I need to open the language version of the site according to the browser language and IP address.
I'm trying to use my Locale Provider as suggested in the documentation.I copy the RequestDefaultLocaleProvider.php to src folder, rename and registered it as a service in services.yaml and insert his ID to sulu_website.yaml.
When I open the site method "getDefaultLocale" in my provider is not called and I can't open the desired version of the site. What am i doing wrong?
services.yaml
app.locale.my_locale_provider:
class: App\Locale\MyLocaleProvider
arguments:
- '#Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface'
- '#Symfony\Component\HttpFoundation\RequestStack'
sulu_website.yaml
sulu_website:
twig:
attributes:
urls: false
path: false
default_locale:
provider_service_id: app.locale.my_locale_provider
MyLocaleProvider.php
namespace App\Locale;
use Sulu\Bundle\WebsiteBundle\Locale\DefaultLocaleProviderInterface;
class MyLocaleProvider implements DefaultLocaleProviderInterface
{
public function __construct()
{
dump("from constructor") ;
}
public function getDefaultLocale()
{
dump("from getDefaultLocale") ;
return 'de';
}
}
I also tried to use the recommended approach Symfony (subscribe to the onKernelRequest event and set the language I need in $request->setLocale() method but this also did not work)

The default locale provider is only taken into account when you are using the following url schema in your webspace:
<url>{host}/{localization}</url>
So if you have e.g. sulu.rocks/en and sulu.rocks/de and somebody is calling sulu.rocks/ the default locale provider is called and redirect then the user provided language.
If you are running a locale on the rootLevel of your webspace, the default locale provider will not be called as it is alrea

The problem is solved, my mistake was that I expected the getDefaultLocale() method to be executed in my ISP, but a redirect to sulu.rocks/{language} was released and getDefaultLocale () was not executed there.

Related

How can I know if my app is running under Kestrel or HTTP.sys?

What's the best way to know if my app is running under Kestrel or HTTP.sys. All that I have found so far is to check for "Kestrel" in one of the HttpConext property class names.
Within an MVC controller I can do something like this:
Boolean IsKestrel = HttpContext.Features.GetType().ToString().Contains("Kestrel");
i.e. check this:
Features = {Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection<Microsoft.AspNetCore.Hosting.HostingApplication.Context>}
When using HTTP.sys I only get:
Features = {Microsoft.AspNetCore.Http.Features.FeatureCollection}
(Is "Http" here enough to know that this is HTTP.sys?)
There must be a better way. Is there an obvious property somewhere that contains the name of the host being used?
A broader question might be, how do I know what the builder pattern built?
Update
Found something better, but still looking for a Property that has the server name or type.
In an MVC controller:
var isKestrel = HttpContext.Request.Headers.GetType().ToString().Contains(".Kestrel.");
var isHTTPsys = HttpContext.Request.Headers.GetType().ToString().Contains(".HttpSys.");
At the operating system level, netsh http show servicestate will list all active URLs listening via HTTP.SYS.
From code you can locate an instance of Microsoft.AspNetCore.Hosting.Server.IServer and check what its implementation is, in netcore 6:
Kestrel => Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl
IIS ==> Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer
HTTP.SYS => Microsoft.AspNetCore.Server.HttpSys.MessagePump
This relies on implementation details (so can break), also other extensions can change these e.g. CoreWcf creates CoreWCF.Configuration.WrappingIServer that wraps one of the above implementations.
you can use System.Diagnostics.Process.GetCurrentProcess().ProcessName
I am not sure whether you want to check this information using the code only or you are just looking for a way to know on which web server your app is running.
In my search result, I found that we could set the ports for a specific web server. When the application will run on that specific web server then it will use that pre-configured port. I am assuming your app also has a similar configuration. You could set the different ports for Kestrel, Http.sys, or IIS. By checking the port number you could say that on which web server your site is running.
You could try to go to the launchSettings.json file in your project where you could configure ports for IIS and Kestral.
Helpful References:
Kestrel Web Server in ASP.NET Core
Understand HTTP.sys Web Server In ASP.NET Core
Hello this is a good question, you question is asking how to find out from inside the code and not from a console.
OOB I did not find anything. So, I had to get very creative to figure this out, sorry for the typo's its brand new stuff...
Option 1:
Since the Kestrel section & endpoints are inside the appsettings.json I used that to find out if its hosted by Kestrel!
//Please create a static class to hold the config.
public static class MyStartupIsItKestrelConfiguration
{
public static IConfiguration Configuration;
public bool static IsKestrel()
{
//check your section kestrel??
var kestrel = configuration.GetSection("Kestrel");
// now check kestrel section or any other section
// see picture for kestrel endpoint in app setting sbelow
return true;
}
}
Now you can access it anywhere and see if you used Kestrel
//Now add it/save it in your startup and access later
public Startup(IConfiguration configuration)
{
Configuration = configuration;
MyStartupIsItKestrelConfiguration.Configuration = configuration;
}
Once you have this
//you can use it in ** YOUR CONTROLLER
MyStartupIsItKestrelConfiguration.IsKestrel();
Option 2:
Please check this public Microsoft.AspNetCore.Http.Features.IFeatureCollection Features { get; }
You can get the features public TFeature? Get<TFeature> (); as a Key Value Pair - and then check the feature for e.g. KESTREL DOES NOT ALLOW PORT SHARING
they split the features namespace in .net core 6 there are breaking changes
You should use the features collection

Apigility Error after installation and creation of first service of demo

i have just installed Apigility and following the tutorials i have an error. When i try with Postman to call my service i get an error like this
Zend\View\Renderer\PhpRenderer::render: Unable to render template "status/v1/rpc/ping/ping/ping"; resolver could not resolve to a file
My call on postman is like this.
http://localhost/demo/api/public/ping
How to solve this problem?
Apigility as part of Zend Framework is now part of the open source Laminas project and is called Laminas API Tools.
Make sure that Zend OPcache is disabled in your PHP configuration before trying to create your API service.
Quick steps to verify:
create a phpconfig.php file that displays php configuration for your development server. Do not put this in production. See for details https://www.php.net/manual/en/function.phpinfo.php
open this file on your server http://localhost:8080/phpconfig.php and look for two things a) ZendOPcache - if it is enabled, then look at b) loaded php.ini something like /etc/php7/cli/php.ini
add opcache.enable=0 to the [opcache] section. Even if it is commented out, it is still loaded, you saw it above right?
restart your PHP server / application to verify that Zend OPcache is off and that's it.
For anyone have this problem, in the example of Apigility change the example code from this:
namespace Status\V1\Rpc\Ping;
use Zend\Mvc\Controller\AbstractActionController;
use ZF\ContentNegotiation\ViewModel;
class PingController extends AbstractActionController
{
public function pingAction()
{
return new ViewModel([
'ack' => time()
]);
}
}
to this
namespace Status\V1\Rpc\Ping;
use Zend\Mvc\Controller\AbstractActionController;
class PingController extends AbstractActionController
{
public function pingAction()
{
return ['ack' => time()];
}
}
Doing this the example is fine.

How to set the scheme for Authorize attribute from a .txt file?

My controller is protected with Authorize attribute. What I want to do is to tell the Authorize attribute to use Windows schema or no schema at all. How would I accomplish that?
[Authorize(AuthenticationSchemes = AuthSchemes)]
Case: AuthSchemes = "Windows"
Case: AuthSchemes = ""
I want to change the schema during run-time, so the administrator will be able to change authentication settings basically.
After long time of figuring out I have managed to get this working, but in a not so elegant way.
I decided not to use the Authorize attribute, instead I've decided to make a custom authentication process. So basically I migrated authentication middleware to ASP.NET CORE 2.0 with custom authentication process, following this resource Migrating authentication middleware to ASP.NET CORE 2.0.
With this set up, I have made a simple class, which will be used when reading from .json file:
public class AuthenticationSettings
{
public string AuthenticatonScheme { get; set; }
public bool Enabled { get; set; }
}
I have also made a .json file which contains information of weather our application should use Windows authentication or not. Reading of the file happens in Startup class in ConfigureServices() method. If windows is set to true we create custom policy (with our scheme which is now set) which is then applied globally:
services.AddAuthorization(o => // we create policy with our custom sheme
{
o.AddPolicy("WinPolicy", b =>
{
b.RequireAuthenticatedUser();
b.AuthenticationSchemes = new List<string> { auth_settings.AuthenticatonScheme }; // here we set the windows scheme
});
});
services.AddMvc(o =>
{
o.Conventions.Add(new AddAuthorizeFiltersControllerConvention()); // we apply the policy
});

breeze 1.3.6 cannot get metadata

The Devcurry hottowelsignalr example which utilizes breeze works fine until i upgraded breeze via nuget to 1.3.6.
F12 in chrome shows that it is trying to retrieve metadata from http://localhost/api/breeze/Metadata whereas previously it would use
http://localhost/OnlineCollaborationWithSignalR/api/breeze/Metadata. Which is the correct location of the call. It appears that the path of the application is missing from the root. i.e. (OnlineCollaborationWithSignalR)
Update
noticed the release notes for 1.3.1. And subsequently changed the routing to
public static class BreezeWebApiConfig {
public static void RegisterBreezePreStart() {
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{controller}/{action}"
);
}
}
and the app/viewmodels/home.js to
// service name is route to the Web API controller
var serviceName = 'breeze/Breeze';
from
// service name is route to the Web API controller
var serviceName = 'api/Breeze';
It still fails with the same error as noted above.
Can you confirm that the API responds accordingly?
Based on your routeTemplate, it should be.
http://localhost/breeze/Breeze/Metadata
UPDATE:
Yes, if your configuration is such. It should be
http://localhost/OnlineCollaborationWithSignalR/breeze/Breeze/Metadata
I am not familiar with the specific project, but I downloaded and updated it. I then made the changes that you specified, and I am getting a response from the API. Although I am getting an error (most likely unrelated), it is in fact routed properly.
For the record, the error I get is:
"The provider for invariant name 'System.Data.SqlClient' is specified multiple times in the application configuration. The invariant name must be unique for each configured provider."

How to route without a templateUrl?

Ok. I have a url setup to log a user out. On the server, there is no html. The session on the server simply gets destroyed, and then the user is redirected to an address.
This works fine with plain html, but with Angular i am having issues. I've been routing all main routes using $routeProvider.when('/foo', {templateUrl: '/foo.html', controller: 'Ctrl'}) and that works fine for normal templated routes.. however, if there is no template it will not work.
So, how do i support the route /logout in the same fashion as above, when there is no html template?
A workaround is to use template instead of templateUrl. From the Angular docs:
template – {string=} – html template as a string that should be used
by ngView or ngInclude directives. this property takes precedence over
templateUrl.
This can be used as follows:
$routeProvider.when("/foo", {template: " ", controller: "Ctrl"});
Note: You must use " " instead of an empty string "" because Angular uses an if (template) check before firing the controller, and an empty string evaluates to false.
-- EDIT --
A better way to do it is to use the resolve map. See the Angular Docs:
resolve - {Object.=} - An optional map of
dependencies which should be injected into the controller.
This can be used like this:
$routeProvider.when('/foo', {resolve: {redirect: 'RedirectService'}});
Note: I've changed it from "Ctrl" to "RedirectService", because what you're describing in the question isn't really a "controller" in the Angular sense. It doesn't set up scope for a view. Instead, it's more like a service, which ends up redirecting.
I am writing the solution based on the already accepted answer and the github issue mentioned in it's comments.
The approach I am using is a resolve parameter in the $routeProvider. In my case I was trying to create a nice solution to logout in my application, when user goes to /logout.
Example code of $routeProvider:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
...
when('/logout', {
resolve: {
logout: ['logoutService', function (logoutService) {
logoutService();
}]
},
}).
...
}]);
In the resolve part you specify a service (factory) by name and later on you have to call it. Still it is the nicest solution around.
To make the example complete I present my logoutService:
angular.module('xxx').factory('logoutService', function ($location, Auth) {
return function () {
Auth.setUser(undefined);
$location.path('/');
}
});
Works great!

Resources