2

I am attempting to implement an oembed provider using the Silverstripe framework but have come across an issue.

I have a controller routed from the url /omebed.json and it works fine if I call something like /omebed.json?mediaurl=mymovie.mp4.
However the Oembed standard states it should be /omebed.json?url=mymovie.mp4

But Silverstripe internally checks the $_GET['url'] variable and will attempt to route to that page/controller. So SilverStripe is trying to route to /mymovie.mp4 skipping my controller and hitting the ErrorPage_Controller creating a 404.

I'm thinking im going to have to extend the ErrorPage_Controller and rejig it if the url is oembed.json, but this seems a little hackish.

Any suggestions?

Cheers

CJSewell
  • 183
  • 2
  • 8
  • What controller are you extending? `PageController`? `ContentController`? Those grab the `$_GET['url']` param in their `init()` method. Why not just extend `Controller` directly? – wmk Feb 04 '15 at 10:22
  • Hi, I am extending `Controller` directly and also have
        ---  
        Name: VideoEmbedRoutes  
        After: framework/routes#coreroutes  
        ---  
        Director:  
          rules:  
            'oembed.json//$Action': 'VideoEmbedController'  
    
    In my yml file. The issue is `/framework/main.php` is picing up the `$_GET['url']` param and handing it to `Director::direct()`
    – CJSewell Feb 04 '15 at 19:34

2 Answers2

3

Extending on @Stephen's answer, here is a way to get around that issue without duplicating main.php and without modifying it directly.

What I did was create a _ss_environment.php file which is added early on in the loading process of Silverstripe.

_ss_environment.php

global $url;
$url = $_GET['raw_url'];
if (isset($_GET['url']))
{
    unset($_GET['url']);
}

// IIS includes get variables in url
$i = strpos($url, '?');
if($i !== false)
{
    $url = substr($url, 0, $i);
}

.htaccess

RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule .* framework/main.php?raw_url=%1 [QSA]

So here is what is happening:

  • The .htaccess is now using raw_url instead of url
  • _ss_environment.php is being called early in the loading process, setting the global $url variable that main.php normally sets. This is set with raw_url rather than url.
  • To prevent main.php to just override it again when it sees your url query string parameter, it is unset (Silverstripe seems to reset this later as far as my test is concerned).
  • Lastly is a little block of code that main.php would normally run if $_GET['url'] is set, copied as-is for apparent support in IIS. (If you don't use IIS, you likely won't need it.)

This has a few benefits:

  • No update to main.php allows upgrading Silverstripe slightly easier in the future
  • Runs the minimal amount of code needed to "trick" Silverstripe into thinking it is running normally.

The one obvious drawback to any solution for changing away form the url query string parameter is if anything looks at the parameter directly. With how Silverstripe works, it is more likely that code uses the $url global variable or the Director class rather than looking at the query string for the current URL.

I tested this on a 3.1 site by doing the changes I mentioned and:

  • Creating a controller called TestController
  • In the init function of the controller, I am running the following:

    var_dump($_GET['url']);
    var_dump($this->getRequest()->getVars());
    
  • Visited /TestController?url=abc123, saw the value of both dumps have "abc123" as the value for the URL parameter.
  • Navigated to a few other custom pages on the site to make sure they were still working (no issues that I saw)

Unfortunately, I haven't been able to find documentation for the order of inclusion in regards to _config.php and _ss_environment.php. However, after browsing through the code, I have worked out it is this:

I could probably go on however I think this gives a pretty good picture of what is going on. I don't really see a way around not using the _ss_environment.php file. Nothing else gets included early enough that you can hook into without modifying core code.

Turnerj
  • 4,258
  • 5
  • 35
  • 52
  • I think so far this is the cleanest option. I was hoping to turn this in to a module, however it can't just be installed with composer, since you have to create/modify `_ss_environment.php` and `.htaccess`. This could be documented in the modules readme, but would be cool to get around that. Thanks – CJSewell Feb 16 '15 at 22:01
  • Is there documentation about the order of inclusion? Something like `mysite/_config.php` then `_ss_enviroment.php` then `*\_config.php` ? Basically, could the `_ss_enviroment.php` changes go in to `mymodules\_config.php`? – CJSewell Feb 16 '15 at 22:03
  • Haven't found any documentation but I have had a quick browse of the code and it is `_ss_environment.php` first then `*/_config.php`. I will update my answer to explain what is happening for that. – Turnerj Feb 17 '15 at 02:43
  • 1
    Awesome, thanks heaps for that +10000 points for linking back to the relevant PHP files on github – CJSewell Feb 17 '15 at 07:12
  • No problem. I like to be quite detailed with my answers for things like this. Your question was pretty interesting, I couldn't help but answer it :) – Turnerj Feb 17 '15 at 08:06
1

I had a quick play with this the other day. And looking at what main.php does it might be best to hack away at it rather than ErrorPage_controller.

For startes SS's default .htaccess file does this:

<IfModule mod_rewrite.c>
    SetEnv HTTP_MOD_REWRITE On
    RewriteEngine On
    # RewriteBase /silverstripe

    RewriteCond %{REQUEST_URI} ^(.*)$
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule .* framework/main.php?url=%1&%{QUERY_STRING} [L]
</IfModule>

Note the ?url changing that to something else and then changing main.php's usage as well may/should help or will cause a heap of extra errors and sadness.

To avoid hacking the core/framework, you could change the .htaccess to target a copy of main.php in mysite (with appropriate include changes).

Stephen
  • 146
  • 7
  • Good point, maybe something like `RewriteRule .* mysite/main.php?url=%1&%{QUERY_STRING} [L]` Where `mysite/main.php` checks if the URL parameter is one of mine, if it is, handle it, other wise just include `framework/main.php` Will check it out and let you know, cheers. – CJSewell Feb 16 '15 at 03:30
  • I'd recommend changing `RewriteRule .* mysite/main.php?url=%1&%{QUERY_STRING} [L]` to something like `RewriteRule .* mysite/main.php?apacheurl=%1&%{QUERY_STRING} [L]` Then you don't need to test the URL. I can't guarantee that won't cause funky stuff to happen, but is should give you the least mangled URL to work with. – Stephen Feb 16 '15 at 05:57