Symfony3 add locale in deeplink - symfony

I create a new site in symfony3 following the getting started section in the official symfony documentation in https://symfony.com/doc/current/setup.html
Everything is working ok.. if I put mydomain.com as the URL, the framework add /en or the correct local.
My question is if there is a way that if the user do a deeplink to mydomain.com/blog the framework found that the local is not present so it can add and transform the url to mydomain.com/en/blog
I'm not adding the code as it is the default one. Let me know if you need it.

There are multiple ways to do this. Probably the easiest is to have an EventSubscriber or -Listener that catches request without a locale and then handles adding that information. Since you based your project on the demo application you might want to look at their solution: https://github.com/symfony/demo/blob/master/src/EventSubscriber/RedirectToPreferredLocaleSubscriber.php
The steps to perform in your event handler are roughly these:
Listen to kernel.request event
Return early based on some criteria, e.g. homepage, a cookie with the language is set, or something else
Detect the language either by getting the default locale or determining from your available locales and the browser header which language fits best (see: https://github.com/willdurand/Negotiation#language-negotiation)
Redirect, add the locale as attribute to request, write the currently set language to a cookie, or whatever else you need to do to change the route

Thanks to #dbrumann I get to this solution... For sure it can be improve to use less code but it just did the trick.
I updated the onKernelRequest method in RedirectToPreferredLocaleSubscriber class
public function onKernelRequest(GetResponseEvent $event): void
{
$request = $event->getRequest();
$path = explode('/',$request->getPathInfo());
$hasLocale = false;
foreach ($this->locales as $key => $l) {
if($l == $path[1]){
$hasLocale = true;
}
}
if(!$hasLocale){
// Ignore sub-requests and all URLs but the homepage
if (!$event->isMasterRequest() || '/' !== $request->getPathInfo()) {
$preferredLanguage = $request->getPreferredLanguage($this->locales);
if ($preferredLanguage !== $this->defaultLocale) {
$url = "";
foreach ($path as $key => $p) {
if($key > 0){
$url .= "/" . $p;
}
}
//print_r('/' . $preferredLanguage . $url);exit;
$response = new RedirectResponse('/' . $preferredLanguage . $url);
$event->setResponse($response);
}
}
else{
// Ignore requests from referrers with the same HTTP host in order to prevent
// changing language for users who possibly already selected it for this application.
if (0 === mb_stripos($request->headers->get('referer'), $request->getSchemeAndHttpHost())) {
return;
}
$preferredLanguage = $request->getPreferredLanguage($this->locales);
if ($preferredLanguage !== $this->defaultLocale) {
$response = new RedirectResponse($this->urlGenerator->generate('homepage', ['_locale' => $preferredLanguage]));
$event->setResponse($response);
}
}
}
}

Related

Fortumo reciept verificaton

Can anyone help to configure this its doing my nut in now.
This will be useful: https://developers.fortumo.com/cross-platform-mobile-payments/
I have the secret key and the widget set up i just need it add the stuff to my database e.g coins + 1 in a query but the code inserted into the successful payment bit wont run. So time to start again fresh.
Any help will be appreciated.
<?php
// check that the request comes from Fortumo server
if(!in_array($_SERVER['REMOTE_ADDR'],
array('1.2.3.4', '2.3.4.5'))) {
header("HTTP/1.0 403 Forbidden");
die("Error: Unknown IP");
}
// check the signature
$secret = ''; // insert your secret between ''
if(empty($secret) || !check_signature($_GET, $secret)) {
header("HTTP/1.0 404 Not Found");
die("Error: Invalid signature");
}
$sender = $_GET['sender'];//phone num.
$amount = $_GET['amount'];//credit
$cuid = $_GET['cuid'];//resource i.e. user
$payment_id = $_GET['payment_id'];//unique id
$test = $_GET['test']; // this parameter is present only when the payment is a test payment, it's value is either 'ok' or 'fail'
//hint: find or create payment by payment_id
//additional parameters: operator, price, user_share, country
if(preg_match("/completed/i", $_GET['status'])) {
// mark payment as successful
}
// print out the reply
if($test){
echo('TEST OK');
}
else {
echo('OK');
}
function check_signature($params_array, $secret) {
ksort($params_array);
$str = '';
foreach ($params_array as $k=>$v) {
if($k != 'sig') {
$str .= "$k=$v";
}
}
$str .= $secret;
$signature = md5($str);
return ($params_array['sig'] == $signature);
}
?>
The first lines of the code example validates that the request comes from Fortumo IP address, if not then it the script dies.
// check that the request comes from Fortumo server
if(!in_array($_SERVER['REMOTE_ADDR'],
array('1.2.3.4', '2.3.4.5'))) {
header("HTTP/1.0 403 Forbidden");
die("Error: Unknown IP");
}
Write to support#fortumo.com to get the latest IP addresses to validate (and replace the example values 1.2.3.4 with them).
Meanwhile you can comment out the ip check and continue building your script.

Symfony2 PHPWord response

I am trying to generate a docx document on Symfony2, using the PHPWord bundle.
In my controller, I succeed in returning a docx file, but it is empty, I think it comes from my faulty response format.
public function indexAction($id)
{
$PHPWord = new PHPWord();
$section = $PHPWord->addSection();
$section->addText(htmlspecialchars(
'"Learn from yesterday, live for today, hope for tomorrow. '
. 'The important thing is not to stop questioning." '
. '(Albert Einstein)'
));
// Saving the document
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($PHPWord, 'Word2007');
return new Response($objWriter->save('helloWorld.docx'), 200, array('Content-Type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'));
}
Try this class
<?php
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
use Symfony\Component\HttpFoundation\Response;
class WordResponse extends Response
{
/**
* WordResponse constructor.
* #param string $name The name of the word file
* #param PhpWord $word
*/
public function __construct($name, &$word)
{
parent::__construct();
// Set default zip library.
if( !class_exists('ZipArchive')){
Settings::setZipClass(Settings::PCLZIP);
}
$writer = IOFactory::createWriter($word, 'Word2007');
//Set headers.
$this->headers->set("Content-Disposition", 'attachment; filename="' . $name . '"');
$this->headers->set("Content-Type", 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
$this->headers->set("Content-Transfer-Encoding", 'binary');
$this->headers->set("Cache-Control", 'must-revalidate, post-check=0, pre-check=0');
$this->headers->set("Expires", '0');
$this->sendHeaders();
$writer->save('php://output');
}
}
Then in your controller do:
return new WordResponse($phpWord, "filename.docx");
Thanks a lot for your answer.
I achieve using the 2nd method, which is in my opinion the best.
I just have to return a response, otherwise the file was generated, but stuck in the web directory.
Using this response, everything was fine and a download prompt appeared, with the "full" file.
Here's my code :
$PHPWord = new PHPWord();
$section = $PHPWord->addSection();
$section->addText(htmlspecialchars(
'"Learn from yesterday, live for today, hope for tomorrow. '
. 'The important thing is not to stop questioning." '
. '(Albert Einstein)'
));
// Saving the document
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($PHPWord, 'Word2007');
$filename="MyAwesomeFile.docx";
$objWriter->save($filename, 'Word2007', true);
$path = $this->get('kernel')->getRootDir(). "/../web/" . $filename;
$content = file_get_contents($path);
$response = new Response();
$response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
$response->headers->set('Content-Disposition', 'attachment;filename="'.$filename);
$response->setContent($content);
return $response;
PHPWord->save() returns a true value so that would be why your file is not being downloaded. With your return new Response() you are setting the content of your response to true (the result of your save call) which is why your response is empty.
You have 2 (and probably more that I haven't thought of) options to generate and download this file..
1. Save your file to a temp folder and server from there
$filename = sprintf(
'%s%sDoc-Storage%s%s.%s',
sys_get_temp_dir(),
DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR,
uniqid(),
'docx'
);
$objWriter->save($filename);
$response = new BinaryFileResponse($filename);
For more info on the BinaryFileResponse see the docs.
2. Ignore Symfony and serve directly via the PHPWord action
$objWriter->save($filename, 'Word2007', true);
exit();
The ->save method provides all of the actions to download the generated file internally (see the code) so all you need to do is set the format and the third parameter to true and it will handle all of the headers for you. Granted it won't be returning a Symfony response but you will be exiting out before you get to that exception.

Matching a URL to a route in Symfony

We have files behind authentication, and I want to do different things for post-authentication redirect if the user entered the application using a URL of a file versus a URL of an HTML resource.
I have a URL: https://subdomain.domain.com/resource/45/identifiers/567/here/11abdf51e3d7-some%20file%20name.png/download. I want to get the route name for this URL.
app/console router:debug outputs this: _route_name GET ANY subdomain.domain.{tld} /resource/{id2}/identifiers/{id2}/here/{id3}/download.
Symfony has a Routing component (http://symfony.com/doc/current/book/routing.html), and I'm trying to call match() on an instance of Symfony\Bundle\FrameworkBundle\Routing\Router as provided by Symfony IOC. I have tried with with the domain and without the domain, but they both create a MethodNotAllowed exception because the route cannot be found. How can I match this URL to a route?
Maybe a bit late but as I was facing the same problem, what I come to is something like
$request = Request::create($targetPath, Request::METHOD_GET, [], [], [], $_SERVER);
try {
$matches = $router->matchRequest($request);
} catch (\Exception $e) {
// throw a 400
}
The key part is to use $_SERVER superglobal array in order to have all things setted straight away.
According to this, Symfony uses current request's HTTP method while matching. I guess your controller serves POST request, while your download links are GET.
The route name is available in the _route_name attribute of the Request object: $request->attributes->get('_route_name').
You can do something like this ton get the route name:
public/protected/private function getRefererRoute(Request $request = null)
{
if ($request == null)
$request = $this->getRequest();
//look for the referer route
$referer = $request->headers->get('referer');
$path = substr($referer, strpos($referer, $request->getBaseUrl()));
$path = str_replace($request->getBaseUrl(), '', $lastPath);
$matcher = $this->get('router')->getMatcher();
$parameters = $matcher->match($path);
$route = $parameters['_route'];
return $route;
}
EDIT:
I forgot to explain what I was doing. So basicly you are getting the page url ($referer) then taking out your website's base url with str_replace and then trying to match the remaining part of the path with a know route pattern using route matcher.
EDIT2:
Obviously you need to have this inside you controller if you want to be able to use $this->get(...)

facebook sdk (php) can't get access token

EDITS: See this picture: http://trackauthoritymusic.com/wwwroot/images/fb-issue-bug.jpg.
For snapshots of the Network tab and all HTTPS headers from my page through FB's redirect.
The windows in the image above show the var_dump's in the code below:
For an access token I only get default the combined appId|secret.
When I var_dump $_REQUESTS at the first point of contact from Facebook, I get nothing so know codeigniter is not stripping the values, but i'm definitely not getting an "signed_request" post from Facebook!
I'm 85.1% sure my Facebook app settings are fine. I've made dozens of tweaks and resets while testing to no success.
And when I switch the settings to client-side approach with the access token in the browser hash, i DO get a valid token, but am desperately trying to avoid all that javascript on my page and will need the php integrated anyway.
All of this only happens once you've approved the app, and I can manually look up their membership in Insights, but know they can't access the app without seeing their token.
I had put this bug aside until now, since my ORIGINAL POST below April 24:....
It's been 3 days with trial-n-error and research:
My Environment: LAMP using facebook sdk 3 & CodeIgniter 2
Login Code:
$CI->load->library('facebook', array("appId"=>APP_ID, "secret"=>APP_SECRET));
$this->visitor['access_token'] = $CI->facebook->getAccessToken();
$fb_id = $CI->facebook->getUser();
var_dump($CI->facebook); // see picture above
var_dump($fb_id); // == 0
if ($fb_id && $fb_id > 0) {
$temp = $CI->users->getUserByFb($fb_id);
if (!$temp) {
$this->insertFBUser($fb_id);
$this->visitor['redirect'] = "?prompt=newfb";
} else {
$this->visitor = array_merge($this->visitor, $temp);
if (isset($this->visitor['user_allowed']) && $this->visitor['user_allowed'] == 0) {
$CI->users->updateUser(array("user_allowed" => 1), $this->visitor['user_id']);
}
}
} else {
array_push($this->errors, $CI->input->get_post("error_msg", false));
array_push($this->errors, $CI->input->get_post("error_code", false));
array_push($this->errors, $CI->input->get_post("error_reason", false));
array_push($this->errors, $CI->input->get_post("error", false));
array_push($this->errors, $CI->input->get_post("error_description", false));
if ($CI->input->get_post("autoclose", false) == true) {
array_push($this->errors, "javascript stackoverflow is encoding weird, but basically changes the hashtag of the pop-window, so the parent page automatically closes it");
}
var_dump($this->errors);
die("nada");
}
Research & Debugging:
This post describes my problem as well, but the solution did not work: stackoverflow.com/questions/8587098/suddenly-getuser-became-to-return-0-php-3-1-1-sdk with or without the trailing comma in the DROP_QUERY_PARAMS array on this page.
Facebook is sending me NO error messages in the url, post, or session and scraping my page fine
EVERYTHING worked fine a few days ago and i've changed very little around this code.
The login now fails whether i use http or https
The popup link opens at:
www.facebook.com/dialog/oauth?client_id=222912307731474&redirect_uri=https%3A%2F%2Ftrackauthoritymusic.com%2Fmanage%2Fusers%2Flogin%3Fautoclose%3Dtrue&state=4522cb9da5bf5107d690a22eee6c5a2e&scope=email&display=popup while redirecting successfully to my desired login url with both state and code parameters apparently valid: trackauthoritymusic.com/manage/users/login?autoclose=true&state=4522cb9da5bf5107d690a22eee6c5a2e&code=AQBfSkI4y_VxhCuF3coVvNmjetdGZjugyFv0UsLlKt5sR5MEGdY8KqpDXZKvqHTGaSHhzY4pHXuR_zmilkwmoQ5y6M9jh15GPI6DXz5E2fSBizAVlrlebriNGcNZb4DRaDFK8cxPJoa9xB2ERuimtuizmlZERNa8hwJxLXtztqkWWhkLFCaGjQvAyyf5jJRkuoztmvfKDIZz3W9lslM6fk_m
but at this point, the sdk cannot get any access token or facebook session data.
PLEASE HELP!
I fixed this by using codeigniter's input library within the Facebook SDK to get the code/token and all $_GET/$_POST/$_REQUEST globals.
See the git diff on the facebook sdk. I'm still not sure what i did to break thisthough. OAuth/Login WAS working consistently before a certain point. I'm sure this wasn't just some race condition on codeigniter occasionally clearing the globals
## -490,10 +490,11 ##
*/
public function getSignedRequest() {
if (!$this->signedRequest) {
- if (!empty($_REQUEST['signed_request'])) {
- $this->signedRequest = $this->parseSignedRequest(
- $_REQUEST['signed_request']);
- } elseif (!empty($_COOKIE[$this->getSignedRequestCookieName()])) {
+ $CI = & get_instance();
+ $signed_request = $CI->input->get_post("signed_request");
+ if (!empty($signed_request)) {
+ $this->signedRequest = $this->parseSignedRequest($signed_request);
+ } else if (!empty($_COOKIE[$this->getSignedRequestCookieName()])) {
$this->signedRequest = $this->parseSignedRequest(
$_COOKIE[$this->getSignedRequestCookieName()]);
}
## -691,15 +692,18 ##
protected function getCode() {
- if (isset($_REQUEST['code'])) {
- if ($this->state !== null &&
- isset($_REQUEST['state']) &&
- $this->state === $_REQUEST['state']) {
-
+ $CI = & get_instance();
+ $code = $CI->input->get_post("code");
+ if (!empty($code)) {
+ $state = $CI->input->get_post("state");
+ if ($this->state !== null && $state && $this->state === $state) {
// CSRF state has done its job, so clear it
$this->state = null;
$this->clearPersistentData('state');
- return $_REQUEST['code'];
+ return $code;
} else {
self::errorLog('CSRF state token does not match one provided.');
return false;
$params['access_token'] = $this->getAccessToken();
}

Wordpress redirect issue, headers already sent

I am wondering, based on the code bellow, where I would want to put my wp_redirect function because where it currently is does nothing but spazzes out and sais:
Warning: Cannot modify header information - headers already sent by (output started at /***/***/WordPress/WordPressDev/wp-includes/script-loader.php:664) in /***/***/WordPress/WordPressDev/wp-includes/pluggable.php on line 881
Which I get because the page has already loaded. but I am un sure where to call this function.
I have replace my web site and any "personal data" with stars and example.com. How ever this code does work, it just wont redirect me.
thoughts?
function get_latest_version_zip(){
global $wp_filesystem;
if(current_user_can('update_themes')){
$aisis_file_system_structure = WP_Filesystem();
$aisis_cred_url = 'admin.php?page=aisis-core-update';
if($aisis_file_system_structure == false){
request_filesystem_credentials($aisis_cred_url);
$this->credential_check = true;
}
$aisis_temp_file_download = download_url( 'http://example.com/aisis/aisis_update/Aisis2.zip' );
if(is_wp_error($aisis_temp_file_download)){
$error = $aisis_temp_file_download->get_error_code();
if($error == 'http_no_url') {
add_action( 'admin_notices', 'aisis_framework_download_update_erors' );
}
}
$aisis_unzip_to = $wp_filesystem->wp_content_dir() . "/themes/" . get_option('template');
$this->delete_contents_check(); //Check if we need to delete the aisis core folder.
$aisis_do_unzip = unzip_file($aisis_temp_file_download, $aisis_unzip_to);
unlink($aisis_temp_file_download); //delete temp jazz
if(is_wp_error($aisis_do_unzip)){
$error = $aisis_do_unzip->get_error_code();
if($error == 'incompatible_archive') {
$this->aisis_incompatible_archive_errors();
}
if($error == 'empty_archive') {
$this->aisis_empty_archive_errors();
}
if($error == 'mkdir_failed') {
$this->aisis_mkdir_failed_errors();
}
if($error == 'copy_failed') {
$this->aisis_copy_failed_errors();
}
return;
}
//throwing errors
wp_redirect(admin_url('admin.php?page=aisis-core-options'));
exit;
}
}
in my functions.php file I placed the following code:
function callback($buffer){
return $buffer;
}
function add_ob_start(){
ob_start("callback");
}
function flush_ob_end(){
ob_end_flush();
}
add_action('wp_head', 'add_ob_start');
add_action('wp_footer', 'flush_ob_end');
with this I still get the error, I think I misunderstanding something....
Just replace the following line
add_action('wp_head', 'add_ob_start');
with
add_action('init', 'add_ob_start');
Output buffering should start before anything sent/echoed to the browser and wp_head hook occurs a bit later than init hook and till then headers already sent and also Keep/place it at the top of your functions.php before anything echoed/sent to the browser.
The problem is that somewhere in wordpress the header() function has been called and some output was already sent to the client while output buffering is off.
Headers have to be sent before any output, otherwise you get the error you described.
wp_redirect(admin_url('admin.php?page=aisis-core-options'));
The above line sets a header like this: header('Location: admin.php......');
Turning on output buffering via php.ini, at the index.php of wordpress or simply before anything is echo'ed to the client should take care of the error.
Details/Documentation can be found here: http://php.net/manual/en/book.outcontrol.php
simplest way i can think of is make your wordpress index.php look like this:
ob_start();
// content of your index.php here
ob_flush();
Another possibility would be adding a priotity:
add_action('wp_head', 'add_ob_start', 1);
The third param is $priority.
Also, if you're hooking in more than one function, it gives you complete control of the execution chain.

Resources