-
Bug
-
Resolution: Not a bug
-
Minor
-
None
-
4.3.4, 4.3.5, 4.4, 4.5
-
None
-
MOODLE_403_STABLE, MOODLE_404_STABLE, MOODLE_405_STABLE
TL;DR
The URL to a Javascript file may start with the $CFG->wwwroot, but the actual file is not generally present at a local path relative to the $CFG->dirroot. Just because the Moodle site is https://example.com and its root directory on the system is /srv/www/example, a Javascript file under the URL https://example.com/path/to/file.js does not necessarily exist under /srv/www/example/path/to/file.js. Yet Moodle currently assumes just that and this leads to errors.
Current implementation details
When the page_requirements_manager->js_fix_url method receives a moodle_url instance as its $url argument, it first checks $url->is_local_url(). That method returns true, if the URL starts with the $CFG->wwwroot. For example, if the site's $CFG->wwwroot is https://example.com and the Javascript URL in question is https://example.com/path/to/file.js that would be considered "local".
So assuming the URL is found to be "local", the page_requirements_manager->js_fix_url method cuts off the $CFG->wwwroot part and recursively calls itself with the remaining path (in the example above /path/to/file.js would be passed as the argument).
Seeing a URL that starts with a slash and ends with .js, the method proceeds to construct a new URL pointing to the /lib/javascript.php script with the path to the Javascript file as a slash argument.
That script by definition assumes the Javascript files it is asked to serve are located on the same file system (relative to the site's $CFG->dirroot). To extend our example, say the dirroot is set to /srv/www/example, the script will try to locate our Javascript file under /srv/www/example/path/to/file.js. If it does not find it, the file is not served and if that was the only file to serve, a 404 is returned here.
If debugging mode is on, this causes an even nastier error right back in the page_requirements_manager->js_fix_url, which checks for the existence of that file and throws a coding_exception, if it does not find it.
Problem
It is obvious that this is wrong. URL paths are generally unrelated to local system paths for any given website. The assumptions made in that implementation are therefore incorrect.
Among other things this causes a bunch of tasks to fail, whenever they depend on some Javascript file to be loaded that is not stored locally on the same system or not in the same root directory.
In our specific case our filter_mathjaxloader | httpsurl points to our own instance of the MathJax file that we happen to host under the same domain. But we also have a reverse proxy involved that routes requests to that MathJax URL differently than requests to "regular" Moodle URLs. The MathJax file just so happens to be located on the same host/file system as the Moodle instance, but in a completely different directory. (But it could just as easily be served from an entirely different host.)
We have numerous task failures caused by the algorithm described above whenever the MathJax URL is required.
Side note: I personally think that the moodle_url->is_local_url method is poorly named. Its documentation correctly explains what it does (i.e. checking if the URL is relative to the $CFG->wwwroot), but I don't think "local" is the correct way to call it because it leads to exactly that confusion of URLs with the same base with resources located under the same root directory. A more appropriate name might be is_relative_to_wwwroot. But this is not essential to the problem at hand.
Proposed change
page_requirements_manager->js_fix_url must not attempt to strip the $CFG->wwwroot of the URL it receives. The caller must ensure to pass an actual relative URL (i.e. just a path starting with a slash), if it knows that the file is located on the same file system.
I have not exhaustively checked every call site of page_requirements_manager->js_fix_url, so I cannot say for sure that this will not break some of those. This needs to be verified. Maybe someone else has a better idea of how to solve this.
Reproducibility
I think the theory laid out above should be sufficiently simple to see the problem and a step-by-step reproducible setup is not necessary here. If you must see the same error we encounter yourself, you'll need to set up a reverse proxy and host your MathJax script in a different location than Moodle.