Retain url parameter with routes - Kirby v2

Hi guys I have the following code in my route and it works well. But when I want to retain the url-parameter it seems the route strips it away. How can I get the parameter and forward it to the login page?

Ex. url:
domain.com/br/login?location=tech%2Fremove-any-url-from-google-search

// Logout route
  [
    'pattern' => 'en/logout',
    'action' => function () {
      if ($user = site()->user()) $user->logout();
      go('en/login');
    }
  ],
  [
    'pattern' => 'br/logout',
    'action' => function () {
      if ($user = site()->user()) $user->logout();
      go('br/login');
    }
  ],

You have to fetch it from the request and then pass to the controller.

https://k2.getkirby.com/docs/cheatsheet/request/params

https://k2.getkirby.com/docs/developer-guide/advanced/controllers#arguments-from-the-router

Not sure I do it perfectly. But it works without any problems on another website I have made. Now that said it is not multi-lingual or have many complex routes as my website has. :thinking: So I think it is the routes that somehow messes it up, but I am not sure about that @texnixe.

So to give some clarity here is my process under
In the template with the content:

<? if (!$site->user() && $page->protectedtoggle()->bool()) : ?>

    <?php go('login?location=' . urlencode(kirby()->request()->path())); ?>

<? endif // Ending the if user is logged in ?>

The controller:

<?php

return function ($site, $pages, $page, $args) {
  // go to this url if login was successful

   if(isset($_POST['location']) != '') {
    $redirect = $_POST['location'];
   } else {
      $redirect = $site->url();
   }

   // redirect immediately if user is already logged in
   if ($site->user()) go($redirect);

  if (r::is('post')) {
    if ($user = $site->user(get('username')) and $user->login(get('password'))) {
      go($redirect);
    } else {
      $error = true;
    }

  } else {
    $error = false;
  }

  return array('error' => $error);
};

In the login page to use this for redirection after login:

<input type="hidden" name="location" value="<?php if(isset($_GET['location'])) {
    echo urldecode($_GET['location']);} ?>" />

PS: When I testing it here. I have upon that I hit the login page a flash of the correct url before it hits the /login. For some reason it is not in the URL when I get to the /login page. :slight_smile: And the same code work with problems on the other page.

Ideas @texnixe ?

Will you need to understand the full route @texnixe, is the part over ok?

We are dealing with a POST request, not a GET request, so using the $_GET() variable doesn’t make sense.

1 Like

But updating this made no difference…

<input type="hidden" name="location" value="<?php if(isset($_POST['location'])) {
    echo urldecode($_POST['location']);} ?>" />

Right, it doesn’t actually make sense at all, because you want to send a given location, not something the user types in. So you should set the current location as value, not what is stored in any variables.

the better solution would be to store the location in the session, anyway.

So how do I do that. Because this is modelled on something I found here on the forum long time ago. How will you store the value as you say in the session?

The s class has all you need: http://k2.getkirby.com/docs/toolkit/api#s

It’s basically whenever you want to work with sessions, you have to start the session (s::start().

The you can set (s::set() and retrieve s::get() values from the session by key.

You might want to read a tutorial about sessions to get a better understanding how this works.

If it is seems to complicated, you can stick to your solution, it will work after all (once it works).

I have a basic understanding of sessions. I have just never make it this way before. I did not even know about it. :face_with_hand_over_mouth:

Now that is said it seem much cleaner working with sessions. If I get it right I can just add/start a session on the page and then retrieve in the login controller? But just quickly looked at the forum and did not find exactly what I was looking for. Any place you can point me? It should not be very hard for me to do, I hope :wink: .

Is there one easy quick fix to my code over?

I think I somehow didn’t really understand your code, trying to get it right now.

  1. On the protected page, you send the user to the login page, attaching the original URL as a query parameter ( = GET request).

  2. Ok, now I understand why you use $_GET in the hidden form element. That get’s the location from the URL, so that should actually be OK then.

  3. But in your controller, the first thing you do it to check for the $_POST variable, even if you don’t even have a post request.

That somehow doesn’t all really make sense.


Come to think of it, it can all be much easier, no hidden field, just the location parameter

In redirect template:

<? if (!$site->user() && $page->protectedtoggle()->bool()) : ?>

    <?php go('login?location=' . urlencode(kirby()->request()->path())); ?>

<? endif // Ending the if user is logged in ?>

In your controller:

<?php

return function($site, $pages, $page) {

  // don't show the login screen to already logged in users
  $redirect = get('location') ?? $site->url();
  if($site->user()) go($redirect);

  // handle the form submission
  if(r::is('post') and get('login')) {

    // fetch the user by username and run the
    // login method with the password
    if($user = $site->user(get('username')) and $user->login(get('password'))) {
      // redirect to the homepage
      // if the login was successful
      go($redirect);
    } else {
      // make sure the alert is being
      // displayed in the template
      $error = true;
    }

  } else {
    // nothing has been submitted
    // nothing has gone wrong
    $error = false;
  }

  return array('error' => $error);

};

That’s it.

I am not sure if this fix the root issue. But I have removed the hidden field and updated the controller. And I get this error under when accessing the /login page after updating the controller.

My main issue is that don’t get the location parameter with me on to the Login page in the URL.

Is that really the only changes you made? Or did you put a session start somewhere?

I am pretty sure I have not done this. :blush: That said, after I was out and got back and restarted the local server and the error seems to gone away. But I am left with two issues:

  1. I type in correct username and password but the page reloads and nothing happens. There is no error message or redirection.

  2. The root cause of the initial issue is still there. The ?location=pageurl is still stripped somewhere on the way. So when I get to the /login page there are no ?location=pageurl
    I have one website where this is working without any issues. So I guess it is the routes, but I am not sure about that. This website is also multi-language.

The website that works with the original code:
http://work.baptistella.dk/tech/remove-any-url-from-google-search

The website I have for myself with all the crazy routes you helped me with @texnixe:
https://www.baptistella.xyz/en/cleancar (I have made a button instead of the strait redirect to the login page, but that should be the same since the link is the same).

I really have no idea of where to start to find this issue and not mess up my routes, cause the routes works perfectly… :smile: And thank you for that @texnixe.

Yes, there is definitely a redirect. You would have to make sure that you filter out the login page from your routes.

So, just a thought the old code logged in although the redirect-to-wanted-page did not work, so is it the way the controller work since it now don´t want to login, because the login should redirect to the /home if there is not parameter, right? Now that said under you find the full route for my site. I have no idea of how to get the location parameters in there. :sweat_smile:

Here is my complete route setup:

// Routes by Sonja! :) Thank you!
c::set('routes', [
  // AMP pages for blog/articles
  [
  'pattern' => '(:any)/articles/(:any)/amp',
     'action'  => function($lang, $article) {

      $page = page('articles/' . $article);
      site()->visit($page, $lang);
      $kirby = kirby();

      $data = [
        'kirby' => $kirby,
        'site' => $kirby->site(),
        'page' => $kirby->site()->index()->find('articles/'.$article)
      ];
      return new Response(tpl::load(kirby()->roots()->templates() . DS . 'amp.php', $data), 'html', 200);
     }
  ],
  // Logout route
  [
    'pattern' => 'en/logout',
    'action' => function () {
      if ($user = site()->user()) $user->logout();
      go('en/login');
    }
  ],
  [
    'pattern' => 'br/logout',
    'action' => function () {
      if ($user = site()->user()) $user->logout();
      go('br/login');
    }
  ],
  // Omit routes EN
  [
    'pattern' => 'en/(:any)',
    'action'  => function($uid) {

      $site = site();
      $lang = kirby()->route->lang;
      $page = $site->visit($uid, $lang);

      if($page === $site->errorPage()) $page = $site->visit('articles/' . $uid, $lang);
      if($page === $site->errorPage()) $page = $site->visit('lander/' . $uid, $lang);
      if($page === $site->errorPage()) $page = $site->visit('legacy/' . $uid, $lang);
      if($page === $site->errorPage()) $page = $site->visit('link/' . $uid, $lang);
      return $page;

    },
    'lang'    => 'en'

  ],
  // Omit routes BR
  [
    'pattern' => 'br/(:any)',
    'action'  => function($uid) {

      $site = site();
      $lang = kirby()->route->lang;
      $page = $site->visit($uid, $lang);

      if($page === $site->errorPage()) $page = $site->visit('articles/' . $uid, $lang);
      if($page === $site->errorPage()) $page = $site->visit('lander/' . $uid, $lang);
      if($page === $site->errorPage()) $page = $site->visit('legacy/' . $uid, $lang);
      if($page === $site->errorPage()) $page = $site->visit('link/' . $uid, $lang);

      return $page;


    },
    'lang'    => 'br'

  ],
  // Redirect the visitor to the omitted URL.
  [
    'pattern' =>  ['(:any)/legacy/(:any)', '(:any)/articles/(:any)',  '(:any)/link/(:any)', '(:any)/lander/(:any)'],
    'action'  => function($lang,$uid) {

     go($lang.'/'.$uid);

    }
  ],

  // Improved route for missing language code - The fallback
  [
    'pattern' =>  '(:any)',
    'action'  => function($pattern) {
      $langCodes = array_column(c::get('languages'), 'code');
      if(in_array($pattern, $langCodes)) {
         return site()->visit('/', $pattern);
      } elseif ($page = site()->visit('', 'br')->parent()->index()->findBy('uid', $pattern)) {
         return  go('br'.'/'.$pattern);
      }
      elseif ($page = site()->visit('', 'en')->parent()->index()->findBy('urlkey', $pattern)) {
         return  go('en'.'/'.$pattern);
      } else {
         go('error');
      }
    }
  ],

]);

// Corrected version for not braking hopefully.
if(function_exists('panel')) {
  panel()->routes([
    [
       'pattern' => '(logout)', // the trick here is the parens around the route pattern
       'method'  => 'GET|POST',
       'filter'  => array('auth'),
       'action'  => function() {
         if($user = panel()->user()) {
           $user->logout();
         }
         go('/');
      },
    ]
  ]);
}

PS: This link uses the old code and controller. But I show you @texnixe so you see the main issue… :slight_smile:

The problem is that the langcode/(:any) routes also reroute the login page (because the query string is ignored), removing the query string. I think you should add a condition to those routes, and return the URL to the login page as is.

Ok. :wink: How do I do that. Because I have no idea of how to do that. :confused: Also will that fix the login issue I have with the controller?