It would appear there is a use-case where the function locale_language_url_rewrite_url() produces an improper URL.
When working with domain specific (LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN) URLs for language negotiation which are nested in subdirectories, so for instance, if we have drupal rendering behind something like this:
- http://english.drupal.example.com/demo
- http://français.drupal.example.com/demo
we run into a problem if the locale modules setting for USER INTERFACE TEXT LANGUAGE DETECTION method is enabled for URL.
Due to a hardcoding of the base url within the function locale_language_url_rewrite_url(), these lines...
$host = 'http://' . str_replace(array('http://', 'https://'), '', $options['language']->domain);
$host = parse_url($host, PHP_URL_HOST);
// Apply the appropriate protocol to the URL.
$options['base_url'] = $url_scheme . $host;
...which dictate what the new base_url shall be, are overwriting the URL path. Should the site be using the global setting $base_url, the value is ignored, the paths are forced to render incorrectly and the rest of the site will generate HREF from the base HOST url and send the user in the wrong direction when navigating links.
The following edit to locale.inc corrects this and ensures the global value is used:
// Let's check the $base_url and add it to the path if it exists.
global $base_url;
if (isset($base_url)) {
$base_path = parse_url($base_url, PHP_URL_PATH);
if ($base_path) {
$options['base_url'] .= $base_path;
}
}
I've included a patch file in case this can make it upstream.
Hopefully you find this useful and compelling to help solve a fringe use-case.
Comments
Comment #1
wilco commentedUpon some reflection, I think I found an even better solution to the patch I wrote above.
Instead of going to the $base_url value, which may not be set, rather, check the $options['language']->domain value and see if the path is set there.
This is a tighter implementation and stays within the definition of the language configuration.
Comment #2
wilco commentedThis is an update of this patch for version 7.21 of Drupal Core
Comment #3
wilco commentedI'm setting this critical as it kills our government website with every update of core. Please, can I get someone to review or talk to me so I can explain the use case?
Comment #4
plachThe critical priority is only for issues breaking a whole site in very common configurations, this is admittedly an edge case. From a cursory look the proposed solution looks a bit weird to me: am I missing something or does it assume that language domains are configured as full URLs with a non empty base path? If this is the case the configuration is not correct, as the language domain is assumed to hold only the domain URL part.
That said, I've verified that the bug exists and is easily reproducible by just configuring a
base_urland setting domain language negotiation up. I think something like the following would be a more correct solution:Anyway, this will need to be fixed in D8 and then backported.
Comment #5
plachAlso, this will need test coverage.
Comment #6
plachClarifying the issue title.
Comment #7
jeroen commentedThis also breaks our customer's website.
I would opt to rewrite the Issue title again. Because the problem can be described as follows:
Domain language negotiation broken with Drupal installed in a subfolder.
It breaks even without explicitly setting a $base_url in settings.php, the check in the patch to see if (isset($base_url)) is unnecessary because there's always a base_url, whether you set it in settings.php or not.
So, indeed, currently, when you have:
http://dutch-example.com/subfolder
http://french-example.com/subfolder
with language domains configured as french-example.com and dutch-example.com it outputs URLs as http://dutch-example.com/ and forgets about "subfolder"
plach's fix is minimal and almost works, it's just that the arguments have to be like this: rtrim(base_path(), '/');
I'll add patches for D8 and D7 tonight.
Comment #8
jeroen commentedHere's the patch for Drupal 7.
Comment #9
jeroen commentedHere's the patch for Drupal 8.
Comment #10
plachThanks, we need some test coverage before committing this.
Comment #12
jeroen commentedHere it is again for Drupal 8 with the fix...
I had introduced a bug that adds the subfolder between the domain and the port... http://my.example.com/subfolder:88 doesn't work.
Also adjusted tests so they don't fail when run from a Drupal in a subfolder.
Comment #13
jeroen commentedHmm, maybe the build-system didn't pick up my 2nd patch because it has the same name as the first failing one...
Just a rename then...
Comment #14
plachSet the status to needs review if you want the bot to pick the patch up.
Comment #15
plachOk, bot green. Can you provide a test-only patch (as in #13 but without the fix) to show that the bug is actual test-covered?
Comment #16
jeroen commentedWell,
I don't quite know how to do that... My understanding is that when you run the Drupal tests, Drupal creates a new database to run the tests against, right?
So it basically runs its tests from the location where it's installed, just on a fresh database. My test-case should set up Drupal in a subfolder of Apache's DocumentRoot and as far as I know I don't think it'll be copying itself into a new directory.
The only way I found I could verify whether my patch works is by actually installing Drupal in a subfolder and running the tests. If you know how to create a test-setup so that Drupal acts as if it's installed in a subfolder, please enlighten me... I don't know.
Comment #17
plachWell, testbots work in a slightly different way: they perform a checkout in
sites/default/files/checkoutbut I guess there is no way to configure a base path. You could try and see if there is test coverage for the plain base path functionality: if there is take inspiration from it, otherwise I think it's ok to go on with the current patch.Comment #18
dcam commentedhttp://drupal.org/node/1427826 contains instructions for updating the issue summary with the summary template.
Comment #19
yesct commentedtagging
Comment #20
vuzzbox commentedManually testing...
Comment #21
jeroen commented@vuzzbox, what's your conclusion?
Comment #22
vuzzbox commentedConclusion: Applying the D8 patch resolves the problem. D7 not tested.
Steps I took to reproduce the error:
Applied this patch:
domain-language-negotiation-fix-1826252-8-1.patchURLs now properly formatted with sub-folder paths and I can navigate successfully across both english.drupal8 and francais.drupal8 sites.
Comment #23
gábor hojtsyThe patch looks good on a code review too. I don't believe this would be a major issue though, its kind of a twisted configuration that you'd use subfolders for sites but then still have control over domains to switch langage. You could just as well configure your site more cleanly to use domains in the first place instead of folders. The idea is that you'd likely use a subfolder if your host does not let you fiddle with domains. Anyway, the patch looks good, and should get in to cover this edge case.
Comment #24
alexpottNeeds a reroll
Comment #25
benjf commentedre-rolled, with minor conflicts resolved
Comment #27
benjf commentedanother attempt - looks like I tried to re-introduce an entire function, oops!
Comment #28
gábor hojtsyOnly test changes are included in this patch, not the actual fix from core/modules/language/language.negotiation.inc?!
Comment #29
benjf commentedI'm unsure how to proceed, any advice would be great! It looks like the 'language_url_rewrite_url' function is no longer in 8.x HEAD, which is where this fix occurs (that entire function is in my patch on #25). Does this mean it's no longer relevant at all? Or maybe this functionality has been moved?
thanks!
Comment #30
gábor hojtsyIt moved in all probability :) You can grep Drupal for a distinctive snippet of that function to see where it is now.
There is your answer :)
Comment #31
benjf commentedthanks for the advice - hopefully this one works.
Comment #32
alexpottComment #33
gábor hojtsyIt looks to be the place where it went, indeed!
Comment #34
gábor hojtsyGreato!
Comment #35
alexpottCommitted 0363a0d and pushed to 8.x. Thanks!
Comment #36
gábor hojtsyHas needs backport to D7 tag :)
Comment #37
jeroen commentedSorry, I've been away on holiday.
Glad to see the patch made it in! It might be a corner-case but it's a bit short-sighted to dictate how customers should set up their environments.
In this case the customer (a big one) has a website and a subsection of the website (a Drupal installation) lives in a subfolder.
Anyway, shall I do the same patch for Drupal 7 then? Or is someone already on it?
I guess I'll have to create a new issue for it on the D7 issue branch?
Comment #38
gábor hojtsyOur current backport process is we use the same issue for the backport. So as soon as you have a patch, feel free to change status to needs review :)
Comment #38.0
gábor hojtsytypos and edits.
Comment #39
prashantgoel commentedI have backported the patch for d7 and will be working on this issue.
I will be shortly submitting the updated patch with test cases.
Comment #40
prashantgoel commentedI have updated the patch as the previous patch contained white spaces and the debug code.
Explanation:
Code for locale_language_url_rewrite_url function is present in the file includes/locale.inc for d7.
I have changed the #8 a bit because d8 patch which is commited seemed to be a slight different. So as to maintain the consistency I added a new line appending the base url.
Thanks
Comment #42
prashantgoel commentedUpdated the code with the test cases.
Comment #44
prashantgoel commentedUpdated test case.
Comment #46
yesct commentedthis might need a slash between .com and rtrim ?
oh and these ternary operators hurt my brain.
[edit: added bits from irc conversation]
Looks like rtrim would be needed in the second part wher ethere is no clean urls.
maybe making a $base_url var would clean that up? like...
$base_url = $langcode . '.example.com/' . rtrim(base_path(), '/');
$link = (!empty($GLOBALS['conf']['clean_url'])) ? $base_url . '/admin' : $base_url . '/?q=admin';
Comment #47
yesct commentedalthough, looking back at the d8 patch, there is not a / before the rtrim.
I'm confused.
[edit: more thoughts from irc]
maybe base_path always starts with a / (so we dont get something like http://example.fr:90whatever/
also, that "fix" in the test in the d8 patch, ... seems like it needs the rtrim(base_path()) in the first half of that if also.
should it have been:
?
Comment #52
5n00py commentedAt the moment tests pass.
Also manual testing is ok.
Can we commit it to 7.x ?
Comment #54
5n00py commentedOk, lets fix tests
Comment #55
5n00py commentedLocally all test is ok for me. Lets check it one more time.
Comment #56
5n00py commentedRe-roll.
Comment #58
5n00py commentedOk, last path pass tests.
Also I confirm that patch works well in my environment and my multilingual setup.
I have domain language detection and drupal placed in subdir.
Comment #59
poker10 commentedAny reason why the patch #44 / #56 is missing two changes in tests from the D8 patch? Only one change in tests is included in the D7 patch. If there is not reason for this, these should be added as well (the code from D8 patch is still used in D9).
Comment #60
poker10 commentedMoving this back to Needs review because of #59. Thanks!