Redirect routes on the fly (e.g. via tag)

I have been wondering if there’s anyway to create redirect links on the fly.
Basically my thought was, having an custom tag such as while writing content

(redirect: some-cloaking url: http://real-url-where-to-be-redirected-to.ext text: something to display)

which creates the html tag for it (while that part isn’t much of a problem)

right now i am doing rather the very manual way which works but is not really straightforward…

c::set('routes', array(
  array(
    'pattern' => 'links/(:any)',
    'action' => function ($url) {
      $link = site()->links()->toStructure();
      $out = $link->filterBy('out','==',$url);

      if(count($out) >= 1){
        go($out->nth(0)->link());
      } else {
        go('error');
      }
    }
  )
));

while i then after manually creating a structure use the normal (link: route-url text: show something)

so without a hassle having to skip the manual link entry would be good.

similar to those url shorteners where having http://url.com/links/url-gives-information-what-to-expect

e.g. /links/cheap-watches

anyone has an idea where to get started on this?

edit:
why would this be beneficial? i intend using this on protecting said links within an pdf file as i don’t want anyone pirate for their own work that said ebook file which links out directly but as “kind of” assurance this will still need to go though kirby after being exported to pdf

1 Like

to follow up on that thought. i have been able to at least make it somewhat a bit more usable by having multiple pages being able to use the same methods to get it done…

searching for the parent page which has informations about the url’s and inserting a link via kirbytag

<?php

kirbytext::$tags['out'] = array(
	'attr' => array(
		'text'
	),
  'html' => function($tag) {
		if($tag->page()->template() == 'book.cover'){
			$page = $tag->page()->url();
		}
		if($tag->page()->parent()->template() == 'book.cover'){
			$page = $tag->page()->parent()->url();
		}
		if($tag->page()->parent()->parent()->template() == 'book.cover'){
			$page = $tag->page()->parent()->parent()->url();
		}
		$out = $tag->attr('html');
		$text = $tag->attr('text');
		return '<a href="'.$page.'/'.$tag->attr('out').'">'.$text.'</a>';
  }
);

Just like before, but using site->structure() each independant page:

  array(
    'pattern' => 'shop/(:any)/(:any)',
    'action' => function ($product,$url) {
      $link = page('shop/'.$product)->links()->toStructure();
      $out = $link->filterBy('name','==',$url);
      if(count($out) >= 1){
        go($out->nth(0)->link());
      } else {
        go('error');
      }
      return false;
    }
  )

is there a good way to go though all parent levels to search for in this case the right template?

EDIT:

When i think about it… Wouldn’t it be possible to directly update the structure with links if the entry is not available of just yet??

kirbytext::$tags['out'] = array(
	'attr' => array(
		'text'
	),
  'html' => function($tag) {
		if($tag->page()->template() == 'book.cover'){
			$page = $tag->page()->url();
                // PSEUDO CODE
                    
                    $link = $page->links()->toStructure();
                    $out = $link->filterBy('name', '==',$tag->attr('out'));
                    if(count($out) == 0){
                       // UPDATE Structure accordingly directly here??
                    }
                   }
		}
		// ............ More
		$out = $tag->attr('html');
		$text = $tag->attr('text');
		return '<a href="'.$page.'/'.$tag->attr('out').'">'.$text.'</a>';
  }
);

EDIT 2:

<?php

kirbytext::$tags['out'] = array(
	'attr' => array(
		'text', 'link'
	),
  'html' => function($tag) {
               // Looking for a nicer way to check each parent site 
		if($tag->page()->template() == 'book.cover'){
			$page = $tag->page();
		}

               // This works just fine, just need to apply to parent pages in a more beautiful way
		if($tag->page()->parent()->template() == 'book.cover'){
			$page = $tag->page()->parent();
			$links = $page->links()->toStructure();
	    $out = $links->filterBy('name', '==',$tag->attr('out'));
	      if(count($out) == 0){
	         // UPDATE Structure accordingly directly here??
          $exist = $page->content()->get('links')->yaml();
					$exist[] = ['name' => $tag->attr('out'), 'link' => $tag->attr('link')];
					$page->update(['links' => yaml::encode($exist)]);
	      }
		}
               // Looking for a nicer way to check each parent site 
		if($tag->page()->parent()->parent()->template() == 'book.cover'){
			$page = $tag->page()->parent()->parent();
		}
		$out = $tag->attr('html');
		$text = $tag->attr('text');
		return '<a href="'.$page->url().'/'.$tag->attr('out').'">'.$text.'</a>';
  }
);

So this is the solution to create it on the fly which works fine, just need to cleanup going through all parent pages to find the template file. so my question applies, is there any good way to check each parent without repeating the same code over and over again? :slight_smile:

You could get the correct parent page like this:

$tag->page()->parents()->findBy('template', 'book.cover');

This returns a single page, i.e. the closest parent with that template.

but only the immediate page?

because there are several silblings who also feature the same template.

Weren’t you asking for parents, not siblings?

yes, just wanted to make sure that it’ll just get the correct parent one :wink:

As I said, the code I proposed finds the next parent with that template, which can be be the immediate parent or the grandparent or the greatgrandparent, depending on which one has that template. But it stops at the first it finds.

BTW, if you use findBy()instead of filterBy() you don’t have to count:

$out = $link->findBy('name','==',$url);
      if($out){
        go($out->link());
      } else {

I have played around with your code and noticed it didn’t quite work out (with multi language settings?) so i adjusted some to get it working…

c::set('routes', array(
  array(
    'pattern' => '(:any)/shop/(:any)/(:any)',
    'action' => function($language,$product,$url) {
          // first 'any' is language code
        $go = site()->visit('shop/'.$product,$language);
         // as it said in cheatsheat, on multi-lang sites
         // need to 'visit' proper page + languageversion
        $link = $go->links()->toStructure();
         // no big change
        $out = $link->findBy('name', $url);
        // Both here with 'findBy' i had to set value() 
        // to get it accepted / working
      if($out->value()){
        go($out->link()->value());
      } else {
      // Do something
      }
    }
  )
));