FMCorz/moodle-block_xp

Coding error detected, it must be fixed by a programmer: Controller for route not found.

Closed this issue · 29 comments

When I click on the report link in a Levelup block, I see the error. I clearly remember it works with no problem before.
Moodle 3.3.1+ (Build: 20170804) with default Boost theme.
Level up! 3.1.0

Yes, I'm using Nginx. How to disable slasharguments in the site's administration?

I suspect that other things in Moodle may not work due to your config.

Have you followed? https://docs.moodle.org/33/en/Nginx#Nginx

Here is my config which works:

	location ~ [^/]\.php(/|$) {
		include snippets/fastcgi-php.conf;
		fastcgi_pass unix:/run/php/php7.0-fpm.sock;
 		fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
	}

To disable slasharguments, search for it in the admin.

Have you made any progress?

I replace "location ~ .php$ {" with your "location ~ [^/].php(/|$) {", problem still exists.

It's not so much the location, rather the fastcgi parameters.

Have you found a solution?

I have the same problem (using Moodle 3.4) and fixing the fastcgi parameters did not help. Here is my part of the respective nginx config file:
location ~ [^/].php(/|$) {
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
include fastcgi_params;
}

Any help would be welcome!

Have you tested the configuration mentioned above? The order of the directives might matter.

Yes, it doesn't fix the problem either.

Disabling shlasharguments helped but that can only be a short-term solution as the comment on slasharguments says that it will be required in future.

scara commented

Hi @chockemeyer,
beware that "." in a regexp is different from the literal . i.e. do not use:

location ~ [^/].php(/|$) {
fastcgi_split_path_info ^(.+.php)(/.+)$;

but add the missing \ before .php to let . be a real dot char in the pattern matching:

location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info  ^(.+\.php)(/.+)$;

HTH,
Matteo

Thanks Matteo but meanwhile I have the version posted by FMCorz on Sep 21 which includes the backslash.

@chockemeyer Do you also have issues with downloading files when slasharguments is turned on? The logic for serving files is almost exactly the same as the login for finding the route. A quick test would be to write content embedding a file hosted on the system (e.g. Private files), and see if that file is displayed properly.

Alternatively, could you place a file called test.php at blocks/xp/test.php with the following content:

<?php
require(__dir__ . '/../../config.php');
echo '<pre>';
var_dump($_SERVER['PATH_INFO']);
var_dump($_SERVER['SCRIPT_NAME']);
var_dump($_SERVER['REQUEST_URI']);
var_dump($_SERVER['SERVER_SOFTWARE']);

Then accessing WWWROOT/blocks/xp/test.php/a/b/c/d, you should get something similar to this:

string(8) "/a/b/c/d"
string(27) "/levelup/blocks/xp/test.php"
string(35) "/levelup/blocks/xp/test.php/a/b/c/d"
string(12) "nginx/1.10.3"

You probably will have something else than levelup for the 2nd and 3rd entries, or nothing at all. Could you please share your result? Remember the re-enable slasharguments prior to trying it.

I don't have problems with downloading files, at least embedded PDF files are shown correctly and I also could download backup files.

The results for your test.phpare

string(0) ""
string(19) "/blocks/xp/test.php"
string(27) "/blocks/xp/test.php/a/b/c/d"
string(12) "nginx/1.12.2"

independent of whether slasharguments is turned on or off.

Not sure what happens, I'm confused as to why files are downloaded. Could you share the link to a file? I'm curious about the URL that Moodle constructed, I won't be downloading the file.

For reference here is my full config:

$ cat sites-enabled/somesite 
server {
    listen 80;
    listen [::]:80;

    root /something;

    index index.php index.html index.htm index.nginx-debian.html;

    server_name example.com;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny all;
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ [^/]\.php(/|$) {
        include snippets/fastcgi-php.conf;

        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
        fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

}
$ cat snippets/fastcgi-php.conf 
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;

# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;

# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;

fastcgi_index index.php;
include fastcgi.conf;
scara commented

Hi @chockemeyer,
what about cgi.fix_pathinfo=1 in your php.ini?

HTH,
Matteo

Link to a file included in a page: https://moodle.tquant.eu/pluginfile.php?file=/197/mod_resource/content/4/MartinLages_SuggestionsforApps%20.pdf
Link for a Private File: https://moodle.tquant.eu/pluginfile.php?file=/5/user/private/logo_TquanT.jpg&forcedownload=1

nginx config file:

server {
	listen 443 ssl;

	root /var/www/moodlessl/moodle;
	index index.php index.html index.htm;

	server_name moodle.tquant.eu;


        rewrite ^/(.*.php)(/)(.*)$ /$1?file=/$3 last;


	location / {
		try_files $uri $uri/ /index.php?q=$uri&$args;
	}

	location /dataroot/ {
	    internal;
	    alias /var/www/moodlessl/moodledata/; # ensure the path ends with /
	}

	location ~ [^/]\.php(/|$) {
		include snippets/fastcgi-php.conf;
		fastcgi_pass unix:/run/php/php7.0-fpm.sock;
 		fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
	}

}

snippets/fastcgi-php.conf:

# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;

# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;

# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;

fastcgi_index index.php;
include fastcgi.conf;

@scara
cgi.fix_pathinfo=1 is set.

scara commented

Mmmhhh... could you share what is your OS and the PHP version?
Has everything - PHP, nginx - been installed via standard packages?
Spare time permitted I'd like to recreate it via docker to investigate your config.

In the past there were some strange bugs e.g. MDL-51554 and I not actually sure if you're hitting something strange here under nginx/1.12.2 or there's something simply missing at first sight.

HTH,
Matteo

scara commented

FYI @chockemeyer

Link to a file included in a page: https://moodle.tquant.eu/pluginfile.php?file=/197/mod_resource/content/4/MartinLages_SuggestionsforApps%20.pdf
Link for a Private File: https://moodle.tquant.eu/pluginfile.php?file=/5/user/private/logo_TquanT.jpg&forcedownload=1

Everything work in Moodle since your are under slasharguments off: that's the reason why for the HTTP GET param file in your URLs, which is the way Moodle uses to find the "slasharguments" when not properly supported.
Otherwise, they should look as below:

  • https://hostnamepluginfile.php/197/mod_resource/content/4/MartinLages_SuggestionsforApps .pdf
  • https://hostname/pluginfile.php/5/user/private/logo_TquanT.jpg?forcedownload=1

HTH,
Matteo

I have a xubuntu 14.04 LTS with PHP 7.0. nginx and php are installed from the following sources:

deb http://nginx.org/packages/ubuntu/ trusty nginx
deb-src http://nginx.org/packages/ubuntu/ trusty nginx
deb http://ppa.launchpad.net/ondrej/php/ubuntu trusty main

And yes, turning slasharguments on, the file URLs look as you described it.

Pretty sure this is the incriminating line:

rewrite ^/(.*.php)(/)(.*)$ /$1?file=/$3 last;

Level up! does not expect the route to be passed through the file argument, rather the _r argument. As the rest of you config file looks right. Could you try to comment out the rewrite line, enable slasharguments, restart nginx and access the test file as commented above?

If this does not work, the workaround is to add a rewrite rule for Level up!. I don't recommend this approach, but here is an untested example:

rewrite ^/(blocks/xp/.*.php)(/)(.*)$ /$1?_r=/$3 last;
rewrite ^/(.*.php)(/)(.*)$ /$1?file=/$3 last;

The test.phpnow returns

string(8) "/a/b/c/d"
string(19) "/blocks/xp/test.php"
string(27) "/blocks/xp/test.php/a/b/c/d"
string(12) "nginx/1.12.2"

And, much more important, Level Up seems to work now with slasharguments turned on.

Thank you very much!

@FMCorz
Your "rewrite ^/(blocks/xp/..php)(/)(.)$ /$1?_r=/$3 last;" saved me! Problem disappears. Cool~

scara commented

Hi @fishfree,
beware that you've implemented an unsupported workaround that could not work in the future.

It should be better to investigate the web server configuration for a proper slash argument support 😉 .

HTH,
Matteo

Hi All,
I am trying levelup plugin on Moodle 3.4.2 installed on windows, IIS web server and the database is MSSQL.
I added the block to a course and when pressing any link including ladder and settings I got the below error

Coding error detected, it must be fixed by a programmer: Controller for route not found.
More information about this error
×Debug info:
Error code: codingerror
×Stack trace:
line 95 of \blocks\xp\classes\local\routing\router.php: coding_exception thrown
line 79 of \blocks\xp\classes\local\routing\router.php: call to block_xp\local\routing\router->get_controller_from_request()
line 38 of \blocks\xp\index.php: call to block_xp\local\routing\router->dispatch()

I added the test page and I get the below output
string(1) "/"
string(19) "/blocks/xp/test.php"
string(34) "/blocks/xp/test.php/?file=/a/b/c/d"
string(17) "Microsoft-IIS/8.5"

any suggestion how can I fix the issue

How to apply this rewrite "rewrite ^/(blocks/xp/..php)(/)(.)$ /$1?_r=/$3 last; to IIS

It is now working fine for me now in IIS 8.5
the pattern should be ^(blocks/xp/[^\?]+?.php)(/.+)$
and the rewrite URL should be {R:1}?_r={R:2}
screenshot below show the correct rule for the workaround in IIS 8.5
https://1drv.ms/u/s!Amdk-vbboLpgh0taTJqtwiOcaXFE