Updated: Comment #147

Problem/Motivation

Suggested commit message:
git commit -m 'Issue #1796596 by stefan.r, Eric_A, Steven Jones, Dave Reid, damien_vancouver, azinck, DamienMcKenna, heddn: Fix and prevent circular redirects' --author="stefan.r <stefan.r@551886.no-reply.drupal.org>"

Enabling pathauto and redirect module and setting redirect to " Automatically create redirects when URL aliases are changed." (default behaviour), change a node's url from (original) to (something else). Then, change the node's url back to (original). Result: Infinite loop warning appears on node access.

Proposed resolution

Initially an approach of deleting old aliases if a new alias about to be saved matches it was taken. After reviewing the patch in #146, the module maintainer (Dave Reid) advised against this. Comments #193, #195, #199, #204, #286 provide further background on the reasoning disabling a redirect was chosen instead. An example use case includes: "All it takes is a content editor unwittingly setting an alias that collides with some important redirects and the redirects are silently killed, never to be seen again."

The patch in #249 is the latest attempt at implementing this and was marked RTBC as of #266. It includes the following functionality:

  • On path update disables old alias if new alias about to be saved matches it.
  • Prevents any circular redirect from occurring by checking for it in redirect_redirect().
  • Comes with tests to verify circular redirects are prevented.
  • Includes a hook_update_N() to disable any existing redirects.

A new iteration of this patch has been posted in #422, which also addresses the feedback from Dave Reid.

Remaining tasks

None, all of Dave Reid's most recent feedback have been addressed in the most recent patch.

User interface changes

Disabled/enabled status shown on node edit screen and on redirect overview and edit screens.

API changes

Two new hooks are introduced so other modules can react to redirects being enabled/disabled.

Original report by nightskyone

Recent updates to sites Pathauto module and Redirect Module causes an Infinite loops

CommentFileSizeAuthor
#516 interdiff-512-516.txt564 bytesstefan.r
#516 1796596-516.patch23.35 KBstefan.r
#513 interdiff-489-512.txt5.9 KBstefan.r
#512 interdiff-504-512.txt1.61 KBstefan.r
#512 1796596-512.patch23.35 KBstefan.r
#507 interdiff-503-504.txt1.19 KBstefan.r
#506 interdiff-503-504.txt1.19 KBstefan.r
#506 1796596-504.patch23.23 KBstefan.r
#505 interdiff-502-503.txt3.04 KBstefan.r
#505 1796596-503.patch22.97 KBstefan.r
#502 interdiff-499-502.txt1.31 KBstefan.r
#502 1796596-502.patch23.28 KBstefan.r
#499 interdiff-498-499.txt630 bytesstefan.r
#499 1796596-499.patch23.21 KBstefan.r
#498 interdiff-489-498.txt3.43 KBstefan.r
#498 1796596-498.patch23.2 KBstefan.r
#498 interdiff-496-498.txt967 bytesstefan.r
#496 interdiff-495-496.txt571 bytesstefan.r
#496 1796596-496.patch22.85 KBstefan.r
#495 interdiff-494-495.txt653 bytesstefan.r
#495 1796596-495.patch22.85 KBstefan.r
#494 interdiff-439-494.txt7.64 KBstefan.r
#494 interdiff-489-494.txt2.68 KBstefan.r
#494 1796596-494.patch22.88 KBstefan.r
#493 interdiff-439-489.txt5.73 KBstefan.r
#489 interdiff.txt1.57 KBDave Reid
#489 1796596-prevent-circular-redirects-489.patch21.62 KBDave Reid
#487 1796596-487.patch21.67 KBstefan.r
#487 interdiff-484-487.txt575 bytesstefan.r
#484 interdiff.txt1.84 KBDave Reid
#484 1796596-prevent-circular-redirects-484.patch21.51 KBDave Reid
#482 interdiff.txt3.62 KBDave Reid
#482 1796596-prevent-circular-redirects-test-only.patch3.84 KBDave Reid
#482 1796596-prevent-circular-redirects.patch21.74 KBDave Reid
#444 interdiff-397-439.txt3.36 KBheddn
#439 redirect-n1796596-439.patch23.97 KBstefan.r
#433 redirect-n1796596-433.patch23.98 KBDamienMcKenna
#422 interdiff.txt952 bytesazinck
#422 redirect-schema-cache-fix-1796596-422.patch24.13 KBazinck
#419 interdiff.txt3.34 KBazinck
#418 redirect-schema-cache-fix-1796596-418.patch24.11 KBazinck
#397 redirect-schema-cache-fix-1796596-396.patch24.38 KBstefan.r
#395 redirect-schema-cache-fix-1796596-395.patch24.25 KBstefan.r
#389 interdiff.txt2.62 KBstefan.r
#389 redirect-schema-cache-fix-1796596-389.patch25.12 KBstefan.r
#381 interdiff.txt536 bytesstefan.r
#381 redirect-schema-cache-fix-1796596-381.patch23.91 KBstefan.r
#367 redirect-schema-cache-fix-1796596-367.patch23.88 KBstefan.r
#367 interdiff.txt2.79 KBstefan.r
#364 redirect-schema-cache-fix-1796596-364.patch23.48 KBstefan.r
#354 redirect-schema-cache-fix-1796596-353.patch23.47 KBstefan.r
#354 interdiff-344-353.txt1.82 KBstefan.r
#352 redirect-schema-cache-fix-1796596-352.patch24.27 KBnicholasruunu
#352 interdiff-1796596-344-352.txt44.5 KBnicholasruunu
#349 interdiff-1796596-297-344.txt7.94 KBstefan.r
#345 redirect-n1796596-344.patch22.77 KBstefan.r
#338 interdiff.txt509 bytesstefan.r
#338 redirect-n1796596-338.patch22.48 KBstefan.r
#331 interdiff.txt182 bytesstefan.r
#331 redirect-n1796596-331.patch22.47 KBstefan.r
#329 redirect-n1796596-329.patch22.47 KBstefan.r
#329 interdiff.txt6.3 KBstefan.r
#328 interdiff.txt1.84 KBstefan.r
#327 redirect-n1796596-327.patch22.24 KBDamienMcKenna
#306 ss2.png35.5 KBstefan.r
#306 ss1.png185.3 KBstefan.r
#297 fix_and_prevent-1796596-297.patch22.27 KBstefan.r
#269 redirect--fix_and_prevent-1796596-249.patch20.91 KBvadym.kononenko
#261 redirect-circular_loops-1796596-261.patch4.63 KBmrmikedewolf
#249 interdiff.txt2.78 KBstefan.r
#249 fix_and_prevent-1796596-249.patch22.26 KBstefan.r
#244 fix_and_prevent-1796596-243-only-tests.patch2.54 KBSteven Jones
#242 fix_and_prevent-1796596-242.patch20.67 KBSteven Jones
#242 fix_and_prevent-1796596-242-only-tests.patch2.55 KBSteven Jones
#236 redirect.circular-loops.1796596-236.patch19.22 KBstefan.r
#236 interdiff-1796596-233-236.txt1.3 KBstefan.r
#233 redirect.circular-loops.1796596-230.patch18.81 KBstefan.r
#233 interdiff-1796596-228-230.txt666 bytesstefan.r
#229 interdiff-1796596-227-228.txt6.15 KBstefan.r
#228 redirect.circular-loops.1796596-228.patch18.81 KBstefan.r
#228 redirect.circular-loops.1796596-228.patch18.81 KBstefan.r
#227 redirect.circular-loops.1796596-227.patch14.56 KBSteven Jones
#224 redirect.circular-loops.1796596-223.patch14.94 KBSteven Jones
#219 redirect.circular-loops.1796596-219.patch13.94 KBstefan.r
#217 redirect.circular-loops.1796596-217.patch13.94 KBstefan.r
#215 redirect.circular-loops.1796596-215.patch13.66 KBstefan.r
#211 redirect.circular-loops.1796596-211.patch13.61 KBstefan.r
#207 redirect-fix-and-prevent-circular-redirects-1796596-207.patch3.87 KBGoZ
#172 redirect.circular-loops.1796596-172.patch4.64 KBtannerjfco
#146 redirect.circular-loops.1796596-146.patch4.64 KBSylvainM
#135 redirect.circular-loops.1796596-135.patch4.6 KBSylvainM
#124 interdiff.txt523 bytesstefan.r
#124 redirect.circular-loops.1796596-124.patch3.57 KBstefan.r
#122 interdiff.txt2.01 KBstefan.r
#119 redirect.circular-loops.1796596-119.patch3.56 KBdeviantintegral
#106 interdiff.txt1.68 KBstefan.r
#106 redirect.circular-loops.1796596-106.patch2.03 KBstefan.r
#104 redirect.circular-loops.1796596-104.patch2.04 KBstefan.r
#68 redirect_loop_detection-1796596-68-reroll.patch7.16 KBattila.fekete
#64 redirect_loop_detection.patch4.94 KBtamasd
#48 interdiff.txt674 bytesEric_A
#48 redirect-detect_prevent_circular_redirects_test_only-1796596-18.patch1.38 KBEric_A
#48 redirect-detect_prevent_circular_redirects_patch_and_test-1796596-48.patch2.08 KBEric_A
#18 redirect-detect_prevent_circular_redirects_test_only-1796596-18.patch1.38 KBdamien_vancouver
#18 redirect-detect_prevent_circular_redirects_patch_and_test-1796596-18.patch2.13 KBdamien_vancouver
#17 redirect-detect_prevent_circular_redirects_test_only-1796596-17.patch1.38 KBdamien_vancouver
#17 redirect-detect_prevent_circular_redirects_patch_and_test-1796596-17.patch2.13 KBdamien_vancouver
#8 redirect-circular-1796596-8.patch766 bytesEric_A
#6 interdiff.txt548 bytesEric_A
#6 redirect-circular-1796596-6.patch766 bytesEric_A
#3 redirect-circular-1796596-3.patch740 bytesEric_A
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

pjcdawkins’s picture

Project: Pathauto » Redirect
Version: 7.x-1.2 » 7.x-1.0-rc1
Status: Active » Closed (duplicate)
Dave Reid’s picture

Title: Infinite loops » How to fix and/or prevent circular redirects
Version: 7.x-1.0-rc1 » 7.x-1.x-dev
Category: bug » feature
Priority: Normal » Major
Status: Closed (duplicate) » Active

No, this is a separate issue. Debating removing the manual flood detection for redirect loops is different from "let's prevent circular redirects from happening in the first place".

Relevant comments from #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions

#5
Posted by a.ross on September 19, 2012 at 2:51am
After the update to RC1, we're getting the message about infinite loop on every page. Could this expose another underlying bug? If not, I think it's time for this patch to be committed.

Edit: the error occurred only on pages with redirects, removing and re-adding the redirects fixes the notice.

#6
Posted by philbar on September 19, 2012 at 3:50pm
After the update to RC1, we're getting the message about infinite loop on every page. Could this expose another underlying bug? If not, I think it's time for this patch to be committed.

I am also receiving the infinite loop error. Reverting back to redirect 7.x-1.0-beta4

#7
Posted by dlumberg on September 21, 2012 at 7:17am
Also getting this error on lots of pages after update to rc-1

#9
Posted by hass on September 23, 2012 at 10:49am
There may be another underlying issue that has caused this issue. I guess it's the auto path that is added on updates.

For me it was a taxonomy term (taxonomy/term/52) with current alias tag/foo that also had a redirection from tag/foo to taxonomy/term/52 defined. The redirect module need to make sure that this is not possible and delete redirections aliases that point to the same url like the current alias.

BUG: This means redirection module redirects from tag/foo to tag/foo. A clear FAIL.

Workaround: Manual delete colliding aliases from redirects list at admin/config/search/redirect/list.

#11
Posted by hass on September 23, 2012 at 11:22am
For the next release we also need an update hook to delete all the overriding redirects that will cause endless loops and bring the site down.

#12
Posted by hass on September 23, 2012 at 11:25am
In http://api.drupal.org/api/drupal/includes%21path.inc/function/path_save/7 we can catch all alias changes with http://api.drupal.org/api/drupal/modules%21path%21path.api.php/function/... and delete the alias from the redirect table.

#13
Posted by hass on September 23, 2012 at 11:53am
I would also suggest to remove these logic as the input/duplicate need to be blocked somewhat earlier or at least change to class error:

    // Mark redirects that override existing paths with a warning in the table.
    if (drupal_valid_path($redirect->source)) {
      $row['class'][] = 'warning';
      $row['title'] = t('This redirect overrides an existing internal path.');
    }

#14
Posted by hass on September 23, 2012 at 1:06pm
Found a node that had the current alias also as redirect to itself defined. This node was NOT listed in the redirect list as warning.

#16
Posted by xixor on September 25, 2012 at 4:54pm
I'm getting this error after updating to RC1: "The webpage at webpage.com has resulted in too many redirects. Clearing your cookies for this site or allowing third-party cookies may fix the problem. If not, it is possibly a server configuration issue and not a problem with your computer."

This has happened due to the automatic redirect creation. On some pages, we changed the URL, and then changed it back. Therefore, it has a redirect that is pointing it to itself. Is there any way to check for this and delete/ignore the redirect? We have this on 100+ pages, so it's not workable to remove them manually.

#18
Posted by paulmckibben on September 28, 2012 at 2:53pm
Upgrading to rc1 has caused infinite loops for me as well. Is there a patch for this issue?

#19
Posted by paulmckibben on September 28, 2012 at 3:37pm
The following mysql command seemed to fix the infinite loop issues for me, without breaking any URL aliases.

DELETE r FROM redirect r INNER JOIN url_alias u ON r.source=u.alias AND r.redirect=u.source;

#20
Posted by hass on September 30, 2012 at 3:24pm
Marked #1799430: Too Many Redirects 310 Error when alias and redirect is same as duplicate.

#21
Posted by hass on September 30, 2012 at 3:38pm
Disabled Automatically create redirects when URL aliases are changed for now as several node revision updates cause loops. Editors don't understand this.

#22
Posted by eule on October 2, 2012 at 9:50am
hi,

i get the same problem..i use rc1 ...so if i try to visit my blog post under ./blogs/anyuser i get the msg

Oops, looks like this request tried to create an infinite loop. We do not allow such things here. We are a professional website!

i mean this is a standart drupal path and has nothing to do with any redirection

Eric_A’s picture

Status: Active » Needs review
FileSize
740 bytes

Nothing wrong with a last line of defense.

Something like this.

Eric_A’s picture

Category: feature » bug

I just realized that the posted patch (#3) is missing URL language. I suppose there may be redirect cases where the target URL language is different from the default? Leaving at "Needs review" for now.

Also, I'd like to make a case for considering this a bug rather than a feature request. There's so much going on with pathauto, i18n and the like. Redirect should ultimately prevent circular redirects, no?

hass’s picture

Cannot review and test now, but looks ok. In general I'm still asking me why we allow redirects to be saved if this will cause endless loops. Marking them as warning or error sounds plain wrong to me.

Eric_A’s picture

This one uses current_path() and adds the URL options.

Eric_A’s picture

Hmm, wait, current_path() can't be right here...

Eric_A’s picture

This one handles query parameters and absolute URL's.

lsolesen’s picture

Applying this patch solves the problem for me. I agree with @hass, those redirects should never be saved, if causing endless loops. For me however, it was caused by the upgrade to rc1. There was no redirect loop prior to that for me.

fenstrat’s picture

Status: Needs review » Reviewed & tested by the community

Same situation as @lsolesen. I'd say this is RTBC.

hass’s picture

I do not think that last patch is correct. If my current path is foo and my node has also foo, I can add as many query params as I'd like and the loaded node is still the same. In such a case a url like foo?foo=bar will also trigger an endless loop. I have not tested it, but I think we need to ignore all URL query params.

fenstrat’s picture

Status: Reviewed & tested by the community » Needs work

@hass makes a good point, so that'd be similar to #6.

Eric_A’s picture

A redirect from foo?foo=bar to foo?whatever=somevalue is legit. And by itself this implies just one redirect, no looping. You'd only get in trouble when there's a second redirect in the system from foo?whatever=somevalue to foo?foo=bar, but that type of loop is not in the scope for the patch in #8. It does target redirects like foo?foo=bar to foo?foo=bar and foo/bar to foo/bar.

damien_vancouver’s picture

Status: Needs review » Needs work

While the patch from #8 did prevent the error from occurring for me, we should really be fixing the problem, instead of detecting and working around it. If we did commit #8,it should be in addition to actually fixing the problem and old data.

I am able to cause new instances of the Oops, looks like this request tried to create an infinite loop. We do not allow such things here. We are a professional website! message by editing a node and setting its URL Path to be the same as an existing redirect to that same node.

It's possible to fix the old data using the query from #19 in #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions.

To prevent it happening in future, redirect module must prevent nodes from being saved with an URL path that matches an existing redirect to that node.

I describe the cause of the problem, the fix, and prevention in more detail below:

Cause of the problem

Adding a redirect works properly. If you try and add a redirect to an existing URL alias, the redirect module correctly prevents you, with the message: "You are attempting to redirect the page to itself. This will result in an infinite loop."

However nothing prevents you from editing a node and setting its URL alias to be the same as an existing redirect. Since rc1 of redirect this causes the Oops error message when you go to one of the duplicate URLs.

Steps to reproduce

The following steps will reproduce the problem:

  1. Create and save a test node and set its URL alias to "test" under URL Path Settings.. Note the nid of your new node.
  2. Now add a redirect at Administration » Configuration » Search and metadata » URL redirects, and set the Source to "test2" and the Redirect to node/123 (or whatever your test node's nid is). At this point everything is fine, with /test and /test2 both reaching your /node/123
  3. Next, edit your test node, and under URL Path Settings change its "URL Alias" to be "test2". The node save completes without any errors (bug here!)
  4. Visit the new page at /test2 and you will receive the "Oops, looks like this request tried to create an infinite loop...." message. The redirect table now contains a redirect that's a duplicate of an URL alias, and going there produces the error.

Prevention

This could be prevented by having redirect implementing hook_node_validate(). Or adding a validator function to certain node edit forms via hook_form_alter(). Or quietly deleting the duplicate redirect row at node save time. I'm sure there are a number of other ways as well... Which would be best?

The Fix (for existing bad data and the Oops message)

For fixing existing bad redirects, the query in #19 from #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions works great.

Show records to be deleted:

SELECT r.rid, r.language, r.source, r.redirect  FROM redirect r INNER JOIN url_alias u ON r.source = u.alias AND r.redirect = u.source AND r.language = u.language; 

Delete redirects shown in above query:

DELETE r FROM redirect r INNER JOIN url_alias u ON r.source = u.alias AND r.redirect = u.source AND r.language = u.language;

I think this query belongs in a redirect module DB update rolled out the same time as the fix the problem above. This will fix bad data and prevent the problem from happening in the future.

If we do that, we shouldn't need the patch in #9... but it might be nice to have anyway for situations where people get corrupt data somehow (via a buggy contrib module, failure to run the DB update, or who knows what else).

Thoughts? I could make a patch for this plan, if we can agree on which method is best.

Eric_A’s picture

EDIT: changed "active alias" to "primary alias".

@damien_vancouver, the clean-up query needs work. Would you be willing to open a separate issue? I'd be happy to help on getting that one done. There are at least two things to deal with when running such a database query:
1) Language.
2) The other is the fact that there may be multiple aliases in the database for one system path. The primary one is the one to check against, but it might be the case that (at times) Redirect selects a different one as the primary one than Drupal Core does.

I think it would be wise to keep this issue focused on just one problem space: the last line of defense for one looping case: redirect to self.

damien_vancouver’s picture

@Eric_A : I have opened a separate issue: #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect. Please have a look at comment #1, where I carry on the discussion from #14 and #15 here.

Can you please update the description and/or title of this issue so it better describes the last line of defense thing? Dave Reid described it in #2 as "let's prevent circular redirects from happening in the first place" but really it's now become "let's detect circular redirects are in the process of happening and stop after the first redirect".

Lastly, a +1 from me for the patch in #8, now that I understand that duplicate url_alias entries are allowed for a single source path. That might be bad data a user can't edit their way out of, so having a last line of defence like #8 would be the only way to stop the redirect from happening.

damien_vancouver’s picture

I was looking into test coverage of this problem and I found that the Redirect functional test is actually failing because of this issue, but the test isn't currently detecting the failure.

With 7.0-1.0-rc1, the testPathChangeRedirects() function creates a node with alias "first-alias", then resaves it as "second-alias", then again as "first-alias" (which fails) and finally back to "second-alias" (which also fails).

The failure happens in one of two ways, which appears to be a timing thing. I consistently get one of each kind of these failures when running the Redirect functional tests:

  1. The Redirect detection catches the redirect and displays the "Oops, looks like this request tried to create an infinite loop..." message
  2. The Redirect detection fails to catch the redirect, and instead the test gets an object with an empty $this->content, and with a whole bunch of repeated 302 Redirects in $this->header
  3. This second one looks like this in the debugger - it appears that Simpletest just ends the request after 6 redirects in a row, and then since there are no assertions running it doesn't see that there was a problem:

    headers	Array [83]	
    		0	HTTP/1.1 302 Found\r\n	
    		1	Date: Fri, 19 Oct 2012 19:52:51 GMT\r\n	
    		2	Server: Apache/2.2.16 (Debian)\r\n	
    		3	X-Powered-By: PHP/5.3.3-7+squeeze13\r\n	
    		4	Expires: Sun, 19 Nov 1978 05:00:00 GMT\r\n	
    		5	Last-Modified: Fri, 19 Oct 2012 19:52:51 +0000\r\n	
    		6	Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0\r\n	
    		7	ETag: "1350676371"\r\n	
    		8	Location: http://drupal7/first-alias\r\n	
    		9	Vary: Accept-Encoding\r\n	
    		10	Content-Length: 0\r\n	
    		11	Content-Type: text/html\r\n	
    		12	
    		13	HTTP/1.1 301 Moved Permanently\r\n	
    		14	Date: Fri, 19 Oct 2012 19:52:51 GMT\r\n	
    		15	Server: Apache/2.2.16 (Debian)\r\n	
    		16	X-Powered-By: PHP/5.3.3-7+squeeze13\r\n	
    		17	Expires: Sun, 19 Nov 1978 05:00:00 GMT\r\n	
    		18	Last-Modified: Fri, 19 Oct 2012 19:52:51 +0000\r\n	
    		19	Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0\r\n	
    		20	ETag: "1350676371"\r\n	
    		21	Location: http://drupal7/first-alias\r\n	
    		22	X-Redirect-ID: 1\r\n	
    		23	Vary: Accept-Encoding\r\n	
    		24	Content-Length: 0\r\n	
    		25	Content-Type: text/html\r\n	
    		26	
      ... and so on (it repeats 6 times, giving $this->header 82 entries, before just stopping).
    

    Adding two asserts to the second and third drupalPost in testPathChangeRedirects() catches this problem and fails the test without the fix from #8 applied. The saves are successful only if there is an HTTP 200 in the headers, and no Oops error message appears in the page text.

        // redirect 7.x-1.x-dev redirect.test, line 236:
    	$this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'first-alias'), 'Save');
        $this->assertResponse(200,'Changing node\'s alias back to \'first-alias\' does not break page load with a circular redirect.');
        $this->assertNoText('Oops, looks like this request tried to create an infinite loop. We do not allow such things here. We are a professional website!', 'Changing node\'s alias back to \'first-alias\' does not trigger Circular Redirect detection.');
        //$redirect = redirect_load_by_source('second-alias');
        //$this->assertRedirect($redirect);
    
        $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), 'Save');
        $this->assertResponse(200,'Changing node\'s alias back to \'second-alias\' does not break page load with a circular redirect.');
        $this->assertNoText('Oops, looks like this request tried to create an infinite loop. We do not allow such things here. We are a professional website!', 'Changing node\'s alias back to \'second-alias\' does not trigger Circular Redirect detection.');
        //$redirect = redirect_load_by_source('first-alias');
        //$this->assertRedirect($redirect);
    

    I've attached a patch of the test only with it failing, and a re-rolled #8 with the test in there which passes.

    I agree with Eric's comments in #13 with regard to the foo?foo=bar paths. Should the issue status be set back to Needs Review?

damien_vancouver’s picture

Setting issue status to Needs Review and re-attaching those patches so the Testbot sees them.

ewenss’s picture

Can we please make this module UNAVAILABLE or CARRY A BIG WARNING IN RED at its download page?

Since my infinite loop occurred at my home page (mydomain.com) and I don't have shell access to run MYSQL commands, it looks like I have no other option but to nuke my entire Drupal installation and start from scratch recreating my pages - 100 hours of work, right down the crapper.

Or is there some other way I can fix this?

hass’s picture

Downgrade should help either.

philbar’s picture

Downgrade should help either.

I downgraded the redirect module because of this error. I'm not sure if it's related, but around the same time my javascript aggregation has been broken. The files are being javascript files are being generated and I can view them in FTP. But my site gives 404 errors when anyone tries to access them.

I can't think of any other change that would cause this because I haven't been making many changes on my site. Just updating modules. I've searched for similar issues on Drupal.org and nobody else seems to have the same problem. That is why I believe it is related to downgrading this module, simply because many people haven't needed to downgrade it.

philbar’s picture

The fix in #14 works for me. I believe it should be added to the update script.

Kevin Hankens’s picture

Status: Needs work » Needs review

On a related note, redirect_path_update() appears to be creating a circular redirect when I create a redirect that mimics a url alias.

STR:
1. Create a node with a url alias, e.g. node/123 -> content/my-node
2. Create a redirect from node/123 -> content/my-node-1 (note the slightly different destination path)
3. Edit your node and change the url alias to content/my-node-1 (matches the destination path from step 2)

The redirect module appears to create a new redirect from content/my-node-1 -> node/123. This is done via redirect_path_update().

Is anybody else seeing this behavior?

Kevin Hankens’s picture

I like #18 and it seems to work well for my case. However, I wonder if we should prevent it from ever saving the reciprocal redirect in the first place.

Eric_A’s picture

The redirect module appears to create a new redirect from content/my-node-1 -> node/123.

My impression was that it created a redirect from content/my-node to node/123.
But yes, that amounts to the same thing: a looping issue for as long as the active alias from node/123 happens to be the same as the path the system was redirecting from.
There are multiple ways to get such direct redirects to self. The patches in this issue simply provide a last line of defense: if the target alias right now happens to be same as our original from path then don't redirect. If the code from damien_vancouver and me works for you, then your changing the status to RTBC would be welcomed. Others here have mentioned that it worked, so the time has come.

Eric_A’s picture

Oops, I said it wrong. Kevins scenario is one of those that could happen.

deetergp’s picture

I encountered this redirect loop issue on my site. I initially had some issues applying the patch in #18, it failed "git apply --check", until I realized that the repo was defaulting to the "master" branch rather than the "7.x-1.x" branch. Once I checked out the correct branch, the patch passed check, applied without issue and solved my redirect loop issue.

Though I don't want to presume that my testing definitively counts as "reviewed & tested by the community," it definitely worked for me. Thanks for the patch!

I will create a separate issue for getting the repo to default to the "7.x-1.x" branch.

Eric_A’s picture

Status: Needs review » Needs work

After applying both this patch and the global redirect patch from #905914: Merge global redirect functions into Redirect module it becomes apparent that the patch here (#18) needs a little work. A possible fix that comes to mind is passing in 'alias' => TRUE when normalizing the original URL.

xavier.carriba’s picture

Suscribe

wiifm’s picture

I have --dev + the patch in #18, and I can confirm that at least the error message goes away when repeating the node title change and then change back trick.

The only issue I foresee is that the redundant redirect still continues to persist in the database. I think there should be node_update() implemented to purge any redirects that now point away from the current $node URL.

Thoughts ?

azinck’s picture

The patch in #18 is working for me in fixing circular redirects. I support thinking through #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect to see if we can prevent the problem in the first place. It's a difficult problem to solve.

rooby’s picture

Either this issue or #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect needs to be closed as a duplicate.

I would recommend leaving this one open and updating the first post to have all the info from the other one, because this one has more discussion and patches.

azinck’s picture

@rooby: I agree that there's a lot of overlap between the two issues.

I have a suggestion:
Discussion in this issue seems to have coalesced around "Stop circular redirects from redirecting infinitely". That's the problem the patches here try to fix, at least. Let's re-title this issue appropriately let it carry on.

#1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect has an unnecessarily specific title but is intended to be a broader discussion of how to stop circular redirects from being established in the first place. Personally I don't think that's feasible and have suggested a report-based solution but that's neither here nor there. I suggest re-titling that issue to something clearer. Perhaps "How to resolve conflicts and undesired interactions between url aliases and redirects".

rooby’s picture

Oh, I figured that this one was for fixing the underlying cause.

If it is about handing the infinite redirects that have been allow to be created, then this one should be closed as a duplicate of #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions

[EDIT] Maybe. At any rate, there are too many issues open for what is actually one single underlying issue (infinite redirects should not be created in the first place).

azinck’s picture

I agree that there are a lot (perhaps too many) issues for one basic problem. But I'm not so sure #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions is an exact duplicate either (personally I think the flood detection code discussed there should be stripped out in favor of the patch in this issue).

This infinite loop, url alias collision problem is a fairly complex one without a single clear solution. Even your statement "infinite redirects should not be created in the first place" which seems self-evident on the face of it is perhaps not really so self-evident (see #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect for some discussion of when we might need to preserve infinite redirects).

There probably needs to be a meta-issue established for the overall problem which can link to the various related issues and map an approach forward.

damien_vancouver’s picture

* #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions was the original issue and discussed rolling back the "Oops! This website caused a circular redirect" message.

* (As explained in #2 here), this issue was requested to hold a patch for preventing circular redirects from occurring. This issue has a patch that has been marked RTBC for a while and I've personally had it on a few production sites that were experiencing the issue. It prevents anonymous users seeing that message.

* Then following that issue we started #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect, which was requested as a fork of this issue to deal with preventing the bad data that is the root cause of the problem. That issue's discussion has now become more focused on providing a report of bad links, which is really almost another approach again besides fixing the bad data.

Combining the discussion makes sense, but... this issue's patch fixes the error message, has test coverage of the bug, and has been RTBC for a while.

If it really is ready, it could be committed, and close(fix) it instead. That message showing up to anonymous users is a nasty bug that's very easy to trigger at best, or crippling to an existing site at worst.

Then #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect, the youngest issue of the three, could be marked as duplicate of #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions (the oldest).

EDIT: i see it got marked Needs Work again... but we should still figure out how to get it committed... It seems close.

rooby’s picture

Thanks for the explanation.
I can see why there are some different issues.

The low hanging fruit that would be great is, like you say, making sure the end user doesn't see that message via the patch in this issue.

If there are other ways of triggering that message then another low hanging fruit would be related to #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions: Either removing that whole message code, or making it an error message instead of a status message and also potentially getting rid of the watchdog logging or making that aspect an option to turn on & off.

Then the harder parts of what to do with the underlying problem could be addressed at leisure.

My 2 cents.

Anyway, back on topic.

azinck’s picture

Status: Reviewed & tested by the community » Needs work

Actually, the low-hanging fruit would be getting a patch from THIS issue committed. As far as I can tell, the change in #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions is *mostly* pointless since making that change without also making the change from this issue means you could get truly infinite un-caught redirects and an error from your browser. But once the patch from this issue is committed the one from #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions could follow.

That is, unless I'm missing something.

rooby’s picture

No I don't think you're missing anything, that's the same as what I said, although I tend to not write so clearly sometimes.

azinck’s picture

Oops, sorry rooby, I see now that I didn't read your post closely enough.

Bottom line: this patch needs to go in first. It's by far the most important first step for solving this problem.

damien_vancouver’s picture

OK then, I'll mark #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect as a duplicate of #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions and post its relevant discussion there along with a summary of what we did.

To get this issue (the fix) back on track then, here's where we are at:

Posted by Eric_A on November 19, 2012 at 3:39pm
Status: needs review » needs work
After applying both this patch and the global redirect patch from #905914: Merge global redirect functions into Redirect module it becomes apparent that the patch here (#18) needs a little work. A possible fix that comes to mind is passing in 'alias' => TRUE when normalizing the original URL.

@Eric_A, are you around? Could you provide some guidance on what next or why it's apparent the patch needs work?

rooby’s picture

@damien_vancouver:

You can probably leave them both open.
After this one goes in #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions still needs to either amend or remove that message, and the #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect issue is for trying to fix the cause of the infinite redirects. Or at least some of them. If possible.

Eric_A’s picture

I marked the patch here NW because the implementation does not allow for redirecting a node system path to its alias, i.e. it will not allow for node/1 to be redirected to its alias. It should allow this. (And the other way around, too, I'd say.)

rooby’s picture

I don't see why you would want to redirect node/1 to its alias? I can't think of a use case for that.

Use the globalredirect module if you don't want people hitting the node/1 page.

Although I could be missing something.

jwilson3’s picture

It's really a shame this issue is being held up for nearly 2 months because of a separate issue for globalredirect module integration. I thought it was already agreed that there are a number of ways that infinite loops can happen. Thus it follows that the solution should be progressive, fixing one problem at a time and getting working code into dev, so we can keep pushing forward and finding and nailing down the other issues. This seems like a big enough issue to simply get it in and THEN worry about globalredirect integration. no?

Eric_A’s picture

The one problem we try to fix here is the "redirect to self loop". The patch from #18 does this *and* as an unwanted bonus it kills a subset of perfectly legal redirects. The global redirect issue helped identify this, but it does not hold up this issue in any way. This one just needs work (and apparently a new test assertion is needed). I'll try to find some time this weekend to see if the idea I mentioned earlier is any good.

azinck’s picture

Eric_A: If there are remaining problems with the patch in #18 (aside from issues with Global Redirect) can you elucidate them here? It's not clear to me what those problems might be just from reading through the comments. As it stands, #18 makes sense to me and is working for my use-cases.

Eric_A’s picture

@azink, I've described the problem in #43
Attached patch should do the main job *and* not break a redirect from a system path to its alias. Would you be able to do some testing?

DamienMcKenna’s picture

Here's an update script I wrote to purge redirects that are causing an infinite loop:

/**
 * Fix infinite redirect loops.
 */
function mymodule_update_700() {
  $results = db_query("SELECT
    r.rid, u.alias
    FROM {url_alias} u
      INNER JOIN {redirect} r
        WHERE u.alias = r.source");

  foreach ($results as $record) {
    db_delete('redirect')
      ->condition('rid', $record->rid)
      ->execute();
    drupal_set_message(t("Removed infinite redirect loop for the path @path", array('@path' => $record->alias)));
  }
}

I decided to do it as two separate steps rather than one large query so there can at least be some logging of the pages that were affected.

heddn’s picture

Status: Needs review » Reviewed & tested by the community

Patch in #48 seems to resolve the issue. I tested using system path example.com/node/123 and redirect example.com/foo. Redirects are stopped. If I rename the path alias to foo2, then the redirect logic doesn't get fired, which is also appropriate. Let's get this thing committed.

heddn’s picture

BTW, I like having #49 available but not included in redirect. Not so sure I want it in a hook_update_N that is provided by redirect because I want to be able to pick and choose which redirects should be deleted.

a.ross’s picture

Hm, it also seems that removing redirects like that (#49) won't remove all possible loops. It just removes a redirect to itself.
On the other hand IMO some dedicated diagnostics interface just to find redirect loops is overkill - at least for now. It could always be added later if it turns out to be a popular request or something.

damien_vancouver’s picture

Status: Needs work » Reviewed & tested by the community

+1 for patch from #48 - it works great for me and prevents the Oops message. I've tested it on multiple produdction sites.

Since this issue is already marked RTBC, we need a maintainer for redirect module to do a final test and if it looks good, commit it.

Then 7.x-1.x-dev will at least contain the fix, for novice users who cannot apply a patch (ie. "most people").

re #49 and discussion in #50-52:

- The query should join on the "language" column as well or the join may be ambiguous on multilanguage sites.

- Fixing the bad data doesn't make the underlying problem go away. As soon as users are in there working and changing URL paths, then circular redirects start happening again in the database. It happens almost immediately in the Real World. But we already forked another issue to discuss that: #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect, specifically for that discussion so it would not hold up the fix in this issue. So let's discuss it further or talk about update hooks and fixing / preventing bad data there. I will un-dupe it again, re-title it and paste the comments from #49-52 there for further discussion.

Let's please keep this issue focused on testing the patch in #48 and getting that patch committed ASAP! Does it need anything else? If not hopefully a maintainer will review and commit it soon.

a.ross’s picture

The only thing I would like to add is that the infinite loop error message shouldn't sound that immature... :)

damien_vancouver’s picture

@a.ross:

The only thing I would like to add is that the infinite loop error message shouldn't sound that immature... :)

That's actually discussed in the oldest of the three issues on this topic: #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions.

This issue was forked from that issue originally, as requested by a maintainer.

If this patch gets committed, then #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions could then become a cleanup of the message code, which won't get run any more and is no longer needed.

Meanwhile #1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect could focus on preventing/fixing bad data in the database, and everything would end up surprisingly neat and tidy.

joshf’s picture

Thanks for everyone's work on this.

I'm curious why this happens in redirect_redirect() instead of redirect_goto() where the header is actually set?

Thanks in advance; I'm still trying to figure out the standards for this stuff.

jdleonard’s picture

#48 looks good and is working well for me. +1 RTBC

drupalerocant’s picture

Is the patch in #48 commited to the dev version? I ask because I see that the last development version is from february 6th.

dnotes’s picture

It doesn't look like this has been committed yet. +1 for RTBC since this seems to solve a critical problem for many people. Not sure if there are other considerations for how this is done, but it would be nice to get some solution for this.

Alan D.’s picture

Still needing tests I think, but this worked for us too.

We only noticed after Google AdWord links all generated the loops http://www.example.com/?utm=test

philbar’s picture

Can we get this committed? I've ran into this problem again.

Steps to reproduce:

  1. Add URL Redirect to a node "redirect/example.html"
  2. Change URL Alias to "redirect/example.html"

Expected Result:

  • Alias change would remove the URL Redirect (Or possibly displaying a warning message asking for change).

Actual Result:

  • Infinite Loop
John Pitcairn’s picture

In moderated D7 workflows there is an additional fish-hook because there may be an extra node_save() call to set the "current" revision - see #1945558: Redirects incorrectly created when saving as draft (using pathauto/redirect default settings).

The patch at #48 does remove the resulting warning message (using latest redirect dev) when a redirect-to-self is viewed, but does not prevent the actual creation of the two additional redirects created at the "draft" stage mentioned in that issue. These are redirects from the current revision pathauto alias and a draft revision pathauto alias.

fenstrat’s picture

Yet another +1 for RTBC.

This is a complex set of issues here, but the patch in #48 stops the bleeding with the warning message.

tamasd’s picture

Hi,

I have created a bit more sophisticated solution. This also filters a longer redirect chain e.g.:
- foo -> bar
- bar -> baz
- baz -> foo

And this does not allow saving a redirect loop.

heddn’s picture

The patch #64 doesn't do anything about redirects that are created from the path alias side of things. #48 does account for that by not redirecting if someone creates a path alias that duplicates a redirect. I do like the idea behind this patch, but I think it should also incorporate more of what is in
#48 or redirects could still occur.

emilyf’s picture

subscribe

yannickoo’s picture

Patch from #64 fixed that infinite loops for me, thanks!

attila.fekete’s picture

I rerolled patch #64 to match with 1.0-rc1. (also included #48 in this)

s_leu’s picture

Status: Reviewed & tested by the community » Needs review

I had the problem with infinite loops when in combination with automatic URL aliases. When i changed a node title and saved the node i was redirected twice on the same node and got the infinite loop message.

The patch in #68 fixed the problem for me.

s_leu’s picture

Status: Needs review » Reviewed & tested by the community
plinto’s picture

I have tried #48 and #68 and but niether help with #62, which what I am facing too.

sjugge’s picture

#68 works with a simple test case (no loops can be created) which is already a big win from an editors or end-user POV.

One minor annoyance is that a user enters his or hers content, gets the warning message and then goes of to remove any aliases which may cause the loop. This action will cause them to leave the node edit page and loose their content changes.

vinmassaro’s picture

Tested #68 against 7.x-1.x-dev and it gets rid of the redirect loop message.

We have a few hundred sites and this always happens when users are creating content, and it is confusing for them. Would be great to get some fix for this committed. Thanks!

hass’s picture

Priority: Major » Critical

Dave asked for critical issues in the queue. Setting priority.

Dave Reid’s picture

Priority: Critical » Major

I never *asked* for critical issues hass, so do not put words in my mouth EVER. I agree this is a high priority to fix, but this is something that I still consider major. I have time today set aside to look at it.

hass’s picture

If the module is broken we name this critical.

DamienMcKenna’s picture

@hass: It doesn't happen on every install, therefore it isn't critical.

hass’s picture

500.000 pathauto installations are out there. This is over 50% of all drupal installations compared to 53.000 redirect rc1 installations. No good chances that we have a countable number of sites not affected. This brings us nowhere. This is critical as I need to downgrade.

DamienMcKenna’s picture

@hass: Nobody's stopping you from using the patch, and following smart development practices like Jen Lampton documented back in March you can easily track the patch. Dave said he was going to try looking at it today, please give him the time to do so.

lmeurs’s picture

My temp fix is the following: I used a Pathauto hook which is triggered whenever a new alias is created, updated or read by Pathauto. The hook checks whether the current node has redirects with the same alias as the current node's new alias. If so, it deletes these redirects from the database table.

/**
 * Implements hook_pathauto_alias_alter().
 *
 * Problem  The Redirect module stores redirects with the same alias as the node's current alias, this causes a redirect loop.
 * Fix      Remove redirects with the node's current alias.
 * Info     - Redirect bug report at drupal.org/node/1796596.
 *          - Pathauto API at drupalcontrib.org/api/drupal/contributions!pathauto!pathauto.api.php/7.
 */
function mymodule_pathauto_alias_alter(&$alias, array &$context) {
  // $alias is a reference and since pathauto_alias_uniquify() also references to $alias, we make a local copy of $alias.
  $_alias = $alias;

  // See if our new local alias is unique, optionally add an incremented suffix as in article/my-alias-0.
  pathauto_alias_uniquify($_alias, $context['source'], $context['language']);

  // Check operation, possible values are insert, update, return and bulkupdate.
  if ($context['op'] !== 'return') {
    // If the new alias equals an existing redirect, remove the redirect from the database table.
    $redirects_deleted = db_delete('redirect')
      ->condition('source', $_alias) // Path alias.
      ->condition('redirect', $context['source']) // Internal Drupal path.
      ->execute();

    // For debugging purposes: tell user how many redirects have been removed.
    if ($redirects_deleted) {
      drupal_set_message(t('%redirects_deleted redirect paths deleted.', array('%redirects_deleted' => $redirects_deleted)));
    }
  }
}
jenlampton’s picture

Patch from #68 still applies cleanly.

Elijah Lynn’s picture

I refactored #80 and submitted a new issue to Pathauto. #68 created some issues for me (sorry I forgot what they were) and most importantly still kept a Redirect rule that was the same as the new alias which I don't think is very clean.

The patch for Pathauto is here https://drupal.org/node/2065091#comment-7754273

GoZ’s picture

Keep an eye on https://drupal.org/node/1817976#comment-7753335. It seems to be more simple and patch works fine.
on hook_path_update or hook_path_insert, if a redirect with the new path already exists, it's deleted. That's all. No patch needed for pathauto nor heavy tests.

Elijah Lynn’s picture

Thanks GoZ, the patch in #83 works in my tests and my patch for Pathauto (#82) got closed in favor of the work being done in Redirect anyways.

plinto’s picture

#83 seems to be working, and it works with moderation

acbramley’s picture

#83 works well and stops circular redirects created by changing a node's alias back to one that previously existed should we close this as a duplicate, the patch at https://drupal.org/files/redirect-prevent_circular_redirects_in_hook_pat... is much lighter and a better approach IMO

Alan D.’s picture

Um, that issue assumes that the circular redirects are all due to the generated path aliases doesn't it?

I managed to trigger this via Google AdWord links with #905914: Merge global redirect functions into Redirect module. So from my (poorly researched) perspective:

#1817976: Updating an entity with an URL alias that matches an existing redirect causes bad data and circular redirect = Keep the entity path alias tables clean
This issue = Sanity check / fallback if the s** hits the fan

;)

hass’s picture

Priority: Major » Critical

Another 4 weeks passed away what is far longer than "today".

heddn’s picture

Let's get an update to the issue summary, see: https://drupal.org/issue-summaries

acbramley’s picture

New patch in #68 is definitely an improvement. Updating to that stopped aliases being created that had redirects from them (very confusing)

Dave Reid’s picture

Status: Reviewed & tested by the community » Needs work

Patch needs to be re-rolled against current 7.x-1.x.

swentel’s picture

Status: Needs work » Reviewed & tested by the community

Hrm, this applies cleanly here. Tested it manually: you don't get a redirect or a message of infinite loop.
However, in the patch I see a form error, but wasn't really able to trigger it, so not sure exactly where to go next.

swenteldoos-2:redirect swentel$ git pull
Already up-to-date.
swenteldoos-2:redirect swentel$ git apply redirect_loop_detection-1796596-68-reroll.patch 
swenteldoos-2:redirect swentel$ git status
# On branch 7.x-1.x
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   redirect.module
#	modified:   redirect.test
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	redirect_loop_detection-1796596-68-reroll.patch
no changes added to commit (use "git add" and/or "git commit -a")
swentel’s picture

Update: could get the form error now too (I finally understand the flow), so this is ready to go for me.

Kristen Pol’s picture

I just applied #68 and it worked well for me and prevented getting into a redirect loop. RTBC++ Thanks!

Eric_A’s picture

Status: Reviewed & tested by the community » Needs work

The tests for #48 (and #68) were broken by http://drupalcode.org/project/redirect.git/commit/ad09f6c.

Also, I'm pretty sure that #68 is broken for multilingual sites and for any non-trivial URL (i.e. with URL options). The patch in #68 is sophisticated and thus needs sophisticated tests. Let's please create a separate issue for that and fix the simple (and probably most frequently occuring) scenario first.

Eric_A’s picture

Pasted Issue Summary template. Added related issues to the Issue Summary.

Can't do more right now. We could probably do with summarizing the two patches by describing pros and cons and who reviewed, tested and plussed one those.

swentel’s picture

Status: Needs work » Needs review
swentel’s picture

Also, I'm pretty sure that #68 is broken for multilingual sites and for any non-trivial URL (i.e. with URL options).

Then we need more people testing instead of a gut feeling. Note, I've actually tested on a multilingual site and worked perfectly.

Eric_A’s picture

Status: Needs review » Needs work

The patch in #68 passed after re-testing and that was expected. The point is that the patch in #68 (and this goes for #48 as well) needs an accompanying test only that will fail on the new assertions. (One of them is broken now and will pass instead of fail.)

As for the extra tests that are needed for the sophisticated part of #68. Indeed, it's not my initial analysis or gut feeling that matters. It's up to the patch makers to add automated tests for at least all normal use cases, including cases that start out with the state when there are URLs with the same path but different languages. The point is that it should not detect a loop when there is a target URL with the same path but a different language than the one we're redirecting too. If the logic in #68 really works it might be the perfect replacement for the detection that is being removed in #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions.

tiki16’s picture

Tried to apply the patch "redirect_loop_detection-1796596-68-reroll.patch" and it partially patched (Cannot apply hunk @@ 10 ) using netbeans

Vemma-1’s picture

The only way after trying many patched was to open the redirect.module file and omit line #989

/** drupal_set_message('Oops, looks like this request tried to create an infinite loop. We do not allow such things here. We are a professional website!'); */

generalredneck’s picture

Just an observation. After doing a git biscect after reproducing the symptoms, It sent me to the following commit.

hanoii’s picture

I wonder if anyone tried patch #1936820-7: Auto-redirect doesn't check for old aliases, it seems a pretty simple patch and it should fixed this issue, I pretty sure it's almost a duplicate of the other one and the patch seems nicer.

stefan.r’s picture

Status: Needs work » Needs review
FileSize
2.04 KB

The patch in #103 won't fix aliases that are already broken, so something like #48 would still be needed.

Patch attached that combines the two, with updated assertions in the test that work against the current 7.x-1.x as per #91.

yannickoo’s picture

Status: Needs review » Needs work
+++ b/redirect.module
@@ -1020,6 +1024,11 @@ function redirect_redirect($redirect = NULL) {
+

Why did you add that empty line?

+++ b/redirect.test
@@ -234,10 +234,14 @@ class RedirectFunctionalTest extends RedirectTestHelper {
+    $this->assertResponse(200,'Changing node\'s alias back to \'first-alias\' does not break page load with a circular redirect.');
...
+    $this->assertResponse(200,'Changing node\'s alias back to \'second-alias\' does not break page load with a circular redirect.');

Please add a space after the comma and put the string in double quotes so that you don't have to escape some parts.

stefan.r’s picture

stefan.r’s picture

Status: Needs work » Needs review
alexweber’s picture

Title: Fix and prevent circular redirects » How to fix and/or prevent circular redirects
Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs issue summary update

Just tested and #106 works for me.

alexweber’s picture

Issue summary: View changes

Updated issue summary.

Owen Barton’s picture

Status: Needs review » Reviewed & tested by the community

This patch is working for me also, handling existing and preventing new "back-and-forth-rename" redirect loops. Code looks good, and tests pass so RTBC.

fenstrat’s picture

Also confirming the RTBC status of #106.

We've used the patch in #48 in production for quite a while, #106 improves on it by removing existing redirects on update if they match the new alias.

Would be great to see this committed.

fenstrat’s picture

fenstrat’s picture

Issue summary: View changes

Pasted template. Added Related Issues. It's a start.

fenstrat’s picture

Title: How to fix and/or prevent circular redirects » Fix and prevent circular redirects
Issue tags: -Needs issue summary update

Updated issue summary.

fenstrat’s picture

Issue summary: View changes

Updated issue summary.

jyee’s picture

Adding a +1 to #106. This patch worked well for me.

jyee’s picture

Issue summary: View changes

Updated issue summary, adding related issue #1936820

acrosman’s picture

Patch in #106 worked for me.

fenstrat’s picture

Title: How to fix and/or prevent circular redirects » Fix and prevent circular redirects
Issue summary: View changes
Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs issue summary update

Reverting changes, I assume these somehow got reset on the d.o D7 upgrade.

The patch in #106 is still very much RTBC.

caspervoogt’s picture

#106 looks good. Tried it but ran into some errors... tried both the 7.x-1.x and master branches.. not sure which of those corresponds to 7.x-1.x-dev.

In case it means anything to some of you, here's the error I got;

error: while searching for:
    //$this->assertRedirect($redirect);

    $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'first-alias'), 'Save');
    //$redirect = redirect_load_by_source('second-alias');
    //$this->assertRedirect($redirect);

    $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), 'Save');
    //$redirect = redirect_load_by_source('first-alias');
    //$this->assertRedirect($redirect);
  }

error: patch failed: redirect.test:234
error: redirect.test: patch does not apply

Perhaps Redirect could also (in addition to #106?) check for infinite loops when cron runs, and then delete them. I wrote a sandbox module that does this; https://drupal.org/sandbox/plethoradesign/2138793.

fenstrat’s picture

@plethoradesign The patch in #106 applies cleanly to redirect-7.x-1.x-dev for me.

As for cleaning up existing infinite loops, I agree it's a good idea. However given this issue has been going on for so long and we've finally got some consensus on the patch in #106 as the way to fix creating new infinite redirects (as well as removing them if they exist on path update). I think we should get this in first. Removing existing ones should probably be a follow up issue.

caspervoogt’s picture

@fenstrat, I totally agree #106 needs to get in first. My sandbox module has been tiding me over in terms of dealing with existing ones. Will follow up once #106 is committed.

deviantintegral’s picture

deviantintegral’s picture

Status: Reviewed & tested by the community » Needs review

The above patch is combined with the patch from https://drupal.org/comment/7805833#comment-7805833 as they both covered removing duplicate redirects on entity updates. If it passes tests I'd like to close that issue as a duplicate.

deviantintegral’s picture

Issue summary: View changes
stefan.r’s picture

FileSize
2.01 KB

For reference I will post an interdiff between #119 and my patch in #106.

The difference seems to be that #119 also runs the deletion check upon hook_path_insert instead of only on hook_path_update, and goes about the deletion differently. The deletion has seemingly the same result; unless multiple redirects with the same problematic source exist, in which case it will delete all of them instead of just a single one.

Looks good to me! :)

fenstrat’s picture

Status: Needs review » Needs work

The changes in #119 look good. Thanks for the interdiff in #122 @stefan.r

+++ b/redirect.module
@@ -394,6 +394,8 @@ function redirect_path_update(array $path) {
+    // Delete all redirects that would redirect the alias.

This comment should be the same as it is below for consistency:
Delete all redirects having the same source as this alias.

With that done I can RTBC this.

stefan.r’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
3.57 KB
523 bytes
j0rd’s picture

alexweber’s picture

Woohoo! Can we get this committed?

aze2010’s picture

please commit!
*subscribing*

whthat’s picture

Bangin, #124 is wonderful with Workbench Moderation. I owe everyone a drink.

quagmire’s picture

I 2nd (or 3rd or 4th) -- please commit! Thanks for the great work on this.

Leeteq’s picture

and 5th... - time to commit this to -dev.

anavarre’s picture

Yup, tested #124 just now with one or several redirects and it works as advertised always.

Leksat’s picture

Just want to warn you about MySQL collation. By default it's utf8_general_ci (I don't know from where this coming, from Drupal or from MySQL itself). And, in this case, MySQL make no difference between umlaut symbols and them "normal" analogs. This basically means that, for example, "é" = "e".

Because of this peculiarity I met redirect loops. After installing the transliteration module, I got a record in the redirect table with "personnalisées" source and "personnalisees" redirect. And this query (simplified)

SELECT * FROM redirect
WHERE source = "personnalisees"

returned that record.
To resolve this I had to change the collation of the source field to utf8_bin.

This issue may happen in redirect_delete_by_path() function, so correct redirect record may be deleted.

vegardjo’s picture

I can also confirm that 124 works

SylvainM’s picture

This patch is great, thanks.
I think a hook_update_N would be useful to remove bad data from the db.
What do you think about this attached patch ?

drupov’s picture

Cool, #135 solves the issue for me (no infinite loop after setting the path back to the original path)

Dries Arnolds’s picture

I tested #135 and it works for me.

mahaprasad’s picture

I have applied patch #135 & it is working fine for me.

RoloDMonkey’s picture

Status: Reviewed & tested by the community » Needs review

I am changing the status back to "Needs review". At this point, it is not clear if we should be committing the patch in 119, 124 or 135.

RoloDMonkey’s picture

Issue summary: View changes
stefan.r’s picture

#124 is just #119 with a correction of a comment as pointed out in #123

All #135 is, is #124 with an update hook (which seems OK to me).

So #135 seems the most complete and is probably the one that should be committed.

RoloDMonkey’s picture

Thank you for the explanation.

Has anyone tested the update hook? We don't want to accidentally delete valid redirects.

fenstrat’s picture

Status: Needs review » Needs work

I've not yet tested the hook_update_n() addition made in #135. However at first glance:

  1. +++ b/redirect.install
    @@ -356,3 +356,29 @@ function _redirect_migrate_path_redirect_variables() {
    + * Remove existing redirect causing infinite loops
    

    Missing trailing period.

  2. +++ b/redirect.install
    @@ -356,3 +356,29 @@ function _redirect_migrate_path_redirect_variables() {
    +function redirect_update_7100(&$sandbox) {
    

    Shouldn't this be redirect_update_7001()?

  3. +++ b/redirect.install
    @@ -356,3 +356,29 @@ function _redirect_migrate_path_redirect_variables() {
    +  // Thanks to https://drupal.org/comment/6786640#comment-6786640
    

    No need for this comment.

  4. +++ b/redirect.install
    @@ -356,3 +356,29 @@ function _redirect_migrate_path_redirect_variables() {
    +  $rid2delete = array();
    

    We don't usually concatenate variables in Drupal. $rids_to_delete would probably be better.

  5. +++ b/redirect.install
    @@ -356,3 +356,29 @@ function _redirect_migrate_path_redirect_variables() {
    +    $deleted = db_delete('redirect')->condition('rid', $rid2delete, 'IN')->execute();
    

    Due to the possibility of many existing redirects I can understand why you've done and 'IN' query here. Not yet tested it for performance, but I think the theory is sound.

  6. +++ b/redirect.install
    @@ -356,3 +356,29 @@ function _redirect_migrate_path_redirect_variables() {
    +    return format_plural($deleted, '1 redirect was deleted.', '@count redirects were deleted.');
    

    'redirect was deleted' => 'circular redirect causing infinite loop was deleted'.

  7. +++ b/redirect.install
    @@ -356,3 +356,29 @@ function _redirect_migrate_path_redirect_variables() {
    +    return t('No redirect was deleted');
    

    Needs to mention 'circular redirect causing infinite loop' as above. Also missing trailing period.

DamienMcKenna’s picture

Regarding the update script's number, per the hook_update_N documentation, 71xx updates are for update that match to v7.x-1.x of the module, 70xx updates are for the "initial porting of your module to a new Drupal core API" which does not apply here.

fenstrat’s picture

Baah, brain fart on my behalf. Thanks for clearing that up Damien.

The rest of my nit picks still apply from #143.

SylvainM’s picture

Status: Needs work » Needs review
FileSize
4.64 KB

Thanks for the review, I think all points are fixed in attached patch.
Please test and review :-)

fenstrat’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

Great, thanks @SylvainM.

I've tested the update hook on a few sites and it works as expected.

The general approach here was RTBC back in #109. The addition of the update hook to remove existing circular redirects makes sense. So marking this as RTBC yet again. This is good to go.

Dries Arnolds’s picture

Tested patch in #146 on a test site with several scenario's that used to cause an infinite loop. After that went well I am now running it on a production site for a few days. Had no trouble. So imho also RTBC.

Pere Orga’s picture

#146 works for me too

mrmikedewolf’s picture

#146 fixed a recurring issue we were running into on one of our projects. Thank you much!

Murz’s picture

Confirm that #146 fixes this issue for my sites too.

tonylegrone’s picture

I also confirm #146. Thanks!

johnennew’s picture

Brilliant - I can confirm this works!

ChrisGrewe’s picture

Confirmed working for me as well.

sanduhrs’s picture

Works as advertised +1

ckng’s picture

#146 tested working.

nwehner’s picture

#146 fixed *several* infinite redirect loops I had inadvertently created on my site - many thanks for this! Pushing it over to my live environment now :)

candelas’s picture

#146 installed for some hours and it works. if i have problems, i will come back and report. thanks a lot to the people that fixes bugs :)

ChaseOnTheWeb’s picture

pontus_nilsson’s picture

#146 works well for me.

sch2’s picture

Thanks SylvainM for the fix (#146). Just applied the patch and worked perfectly.

narendraR’s picture

drumm’s picture

quotesBro’s picture

#146 works for me too, thanks.

rwohleb’s picture

Seems like this has been accepted by the community, so let's get it committed.

wwhurley’s picture

Tested it with a couple of sites and it appears to have resolved the issues we were experiencing. RTBC for me, too.

Pere Orga’s picture

heddn’s picture

thedavidmeister’s picture

works for me too. Please commit this.

rcodina’s picture

It also works for me!

tannerjfco’s picture

Same as #146 but with bump to hook_update function to 7101, as fix for #2051313: uid not stored when redirects added via Add Redirect page is now committed to dev and uses 7100

loparr’s picture

hi,
I see that changes are made to .install file as well. So is it advised to uninstall module first and then install patched module? Uninstalling would remove database entries and that I do not want.
So I patched all other files except .install file and it is working.
Should I be worried about .install file? Thank you.

Alan D.’s picture

No, just run update.php and it will run these functions.

loparr’s picture

After patching current dev with #146 and running upgrade I get error:
Fatal error: Cannot redeclare redirect_update_7100() (previously declared in .../sites/all/modules/redirect/redirect.install:271) in .../sites/all/modules/redirect/redirect.install on line 389

Patch #172 works against current dev version.

Just to make sure - so which patch against what version is advised?
Thank you.

pjcdawkins’s picture

@loparr
Patches should always be against the current -dev version.

Here is the difference between #146 (which has been RTBC for a long time) and #172:

13c13
< +function redirect_update_7100(&$sandbox) {
---
> +function redirect_update_7101(&$sandbox) {

This is a trivial yet essential update. So we can assume #172 is RTBC.

rooby’s picture

Hiding the old patch.

jwilson3’s picture

Issue was reported exactly 1 year and 11 months ago, as of yesterday. Can we beat the 2 year mark!?!

thedavidmeister’s picture

I'd totes commit this myself if I could.

CatherineOmega’s picture

Want to open a ticket requesting to be a comaintainer, thedavidmeister? :)

thedavidmeister’s picture

I hope it doesn't come to that, I wish there was a way to "escalate" a ticket without needing to apply for co-maintainer.

Honza Pobořil’s picture

#172 works

parasolx’s picture

confirm that #172 works. tested on large site with 10000+ content.

i think should it deploy into release version since a lot of site having the same problem.

Alan D.’s picture

The community have had a very strong voice here, something that I can only dream about in my own projects ;)

Push to dev, and start child issues for following up tasks? If unsure, tag a new release and then commit to dev? It would nice to see this resolved before Drupal 8 is released.

IMHO, this and #905914: Merge global redirect functions into Redirect module would be blockers issues to determine the base functionality that would be used as the milestone for doing the Drupal 8.x port.

heddn’s picture

If we were to read back through the history of this issue, a version of the patch has been RTBCed for over a year. With several, I mean several, confirmations. Can this just get committed?

DuaelFr’s picture

This patch must be in about all the makefiles of the planet. It's time to push it to the dev release at least!
It has an upgrade path so it's safe and the code is well optimized so d.o would not suffer from this awesome fix.

Please! Please! Commit.
From France with Love.

Leeteq’s picture

Yes, please push it at least to -dev.

SGhosh’s picture

Please push it to some version. Everyone needs to keep applying this patch. #172

thedavidmeister’s picture

I have put my hand up to be a co-maintainer and apply some of the outstanding RTBC patches in the queue #2342117: Application for co-maintainer: thedavidmeister.

deanflory’s picture

#172 appears to have applied whereas#146 had the WSOD error.

Here's the patching results from #172 when applied to 7.x-1.x-dev after running the "redirect-drupal-subdirectory-fix-1198028-13.patch" patch:

patch < redirect.circular-loops.1796596-172.patch
patching file redirect.install
Hunk #1 succeeded at 363 (offset 7 lines).
patching file redirect.module
Hunk #1 succeeded at 376 (offset -18 lines).
Hunk #2 succeeded at 392 (offset -18 lines).
Hunk #3 succeeded at 843 (offset -18 lines).
Hunk #4 succeeded at 1006 (offset -28 lines).
patching file redirect.test

Dave Reid’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/redirect.install
    @@ -356,3 +356,28 @@ function _redirect_migrate_path_redirect_variables() {
    +  $rid_to_delete = array();
    +  while ($record = $result->fetchAssoc()) {
    +    $rid_to_delete[] = $record['rid'];
    +  }
    

    Could just use $rid_to_delete = $query->execute()->fetchCol();

  2. +++ b/redirect.install
    @@ -356,3 +356,28 @@ function _redirect_migrate_path_redirect_variables() {
    +    $deleted = db_delete('redirect')->condition('rid', $rid_to_delete, 'IN')->execute();
    

    Should this be using redirect_delete_multiple()?

  3. +++ b/redirect.module
    @@ -408,6 +410,16 @@ function redirect_path_update(array $path) {
    +function redirect_path_insert(array $path) {
    +  if (!empty($path['alias'])) {
    +    // Delete all redirects having the same source as this alias.
    +    redirect_delete_by_path($path['alias'], $path['language'], FALSE);
    +  }
    +}
    +
    

    This is interesting. I wonder if it would be better *not* to delete existing data, and instead just make it not work and allow the alias to work. I don't feel great about deleting manual redirects/data.

  4. +++ b/redirect.module
    @@ -1017,6 +1034,10 @@ function redirect_redirect($redirect = NULL) {
    +    // Prevent circular redirects.
    +    if ($GLOBALS['base_root'] . request_uri() == url($redirect->redirect, array('absolute' => TRUE) + $redirect->redirect_options)) {
    +      return FALSE;
    +    }
    

    This feels a little bit like the wrong location for this code. Should this be in path_redirect_alter() (as an implementation of hook_redirect_alter() for core's path.module)?

  5. +++ b/redirect.test
    @@ -234,10 +234,14 @@ class RedirectFunctionalTest extends RedirectTestHelper {
    +    $this->assertResponse(200,"Changing node's alias back to 'first-alias' does not break page load with a circular redirect.");
    

    Missing spacing between the parameters here.

  6. +++ b/redirect.test
    @@ -234,10 +234,14 @@ class RedirectFunctionalTest extends RedirectTestHelper {
    +    $this->assertResponse(200,"Changing node's alias back to 'second-alias' does not break page load with a circular redirect.");
    

    Same here.

Dave Reid’s picture

Would it help if we could change redirects into some kind of 'disabled' state when this happens? I'd really rather not delete.

vinmassaro’s picture

@Dave Reid: we see this issue happen only as described in #1, and the redirects created are never needed and need to be manually deleted to fix the bug. What's the use case for keeping them around, but disabled?

azinck’s picture

I agree with Dave Reid about keeping the redirects around. All it takes is a content editor unwittingly setting an alias that collides with some important redirects and the redirects are silently killed, never to be seen again.

I'd rather see logic implemented that prefers to serve the page with the alias if there's a collision, but doesn't delete the redirects.

heddn’s picture

re: #192.2
Can we do what commerce did recently? Way back in #51 I voiced caution about deleting redirects.

I really don't want us to bikeshed on this, as some version of a patch is in about every install profile that exists. What is the best path forward? I'd say we remove the hook_update_N and punt that to a follow-up.

heddn’s picture

azinck’s picture

I'm with heddn in #196, but I'd say we need to not only remove the hook_update_N but also the calls to redirect_delete_by_path() in redirect_path_update() and redirect_path_insert().

Dave Reid’s picture

Correct, those calls I'm also concerned about. Just moving the update hook doesn't solve it.

drumm’s picture

We haven't had any problems, at least that I know about, on Drupal.org, after running this on Drupal.org for a few weeks. That said, having the disabled redirects kept around would be a big help, if there were any problems. I think adding a disabled state for redirects would be a separate issue, which needs to be resolved before this.

Charles Belov’s picture

If there's a collision, why doesn't the new redirect add -0, -1, etc.?

We really can't know which side of the collision to disable, the original or the new.

GoZ’s picture

Don't understand why you don't want delete redirects which are not needed anymore.

Redirect is made for SEO and to be sure old path can be hit and return to right page (so new path).
If an another page build a path which is same as a redirect, redirect is no needed anymore and should be removed. Next time this path will be updated, a new redirect will be created to redirect to this new one.

Keeping old redirects disabled instead of deleted can be only useful for history, and to have a redirect table bigger (and so lower).

cmseasy’s picture

I agree with GoZ.
My advice:

  • Commit patch #174 it is RTBC: it solves circular redirects.
  • Make a new issue about the deletion/disable control issue (for all redirects).
azinck’s picture

I disagree with #202 and #203. Circular redirects are solved without the deletion of colliding redirects. You can remove that bit *and still have circular redirects solved*.

Especially on large sites with lots of editors I think it's bad policy to silently delete content (redirects) when people save nodes with colliding path aliases. I should not have to be nervous that creating some test page with any old path I might choose (or might have chosen for me by the pathauto settings) might, without my knowing it, deal hard-to-recover-from damage to someone else's carefully crafted redirects.

I understand that some (many?) people want the Redirects cleaned up. But I do not think that should be the default behavior. Redirect does not currently try to clean up collisions and adding that goes beyond the scope of what's needed to fix the actual problem at hand.

GoZ’s picture

Redirect aren't real content but 301 to not loose users and seo.

Right now, creating node with title which will make same path as existing redirect will generate this path but never be reachable. Redirect take hand so we will never can access this node.

If we take consideration of your example: you create some test page with same path as existing redirect.
1\ Right now, you're page will never be available on your path, redirect will redirect to old page.
2\ If redirect is deleted, you're page will be available. If you delete your page, redirect will be lost (that you don't want).
3\ If redirect is disabled, you're page will be available. If you delete your page, redirect can be enable, but only if there is one redirect. What's happened if there is other disabled redirect with this path ? Which one do you want to enable ?

For 2 and 3, you must also take care of unpublish content. If you create page with same path as redirect and this one is deleted or disabled, until content is published, users will see an 'access denied' on this page.

Another solution will be:
4\ Never allow to create same path as existing redirect. If redirect exists, set -n+1 to path like pathauto already does with path collisions.

azinck’s picture

I'd prefer your option #4 to ensure that you can't create aliases or redirects on the same path as an existing redirect, but I don't think that's really tenable. There are many possible ways to create aliases and this would require API changes.

You are considering too narrow a use-case. On some sites redirects *should* be considered as content. Many sites use redirects to map multiple paths to a single node for SEO, marketing, or other content strategy purposes. Some sites may even use redirects in this way to maintain url mappings from old systems (pre-Drupal) to new content locations.

GoZ’s picture

Here some patch to not delete old redirects. During path insert or update, also check redirect, and re-generate another alias if redirect already exists.
Take care of pathauto.

This can be improved adding hook_update to take care of old redirects conflicts but it will be good to have your comments, and to know if this solution is better for you.

azinck’s picture

Thanks, GoZ. I appreciate you taking the time to do this.

I think this is a feasible solution though it may still be confusing to some content editors. I'm curious to hear from others.

I'd probably prefer the following solution:
* Allow redirect/alias collisions
* Attempting to access a path on which there is a collision should result in Drupal preferring the alias. (the redirects should be effectively ignored).
* Page that is ultimately served should have a notice set at the top that there are existing redirects that conflict with this path, and provide links to go repair. This notice could be limited to users with ability to edit the node or those with permission to administer redirects.

The doesn't interfere with existing workflows but prevents loops and provides users with the info the need to fix any collisions.

hitesh.koli’s picture

Can someone confirm if the redirect.circular-loops.1796596-172.patch is in the recommended version ?

deanflory’s picture

@hitesh.koli, the "recommended" version 7.x-1.0-rc1 is from 2012 and the patch in #172 is from 3 months ago, therefore it is not in the recommended version.

It's probably not in the 7.x-1.x-dev from 2014-Jul-23 since the #172 patch applies to that dev version (see my results in #191) without stating it is already in there.

stefan.r’s picture

stefan.r’s picture

Status: Needs work » Needs review
stefan.r’s picture

Status: Needs work » Needs review
FileSize
13.66 KB

This is a first stab at incorporating Dave Reid's feedback to the patch in #172 and allowing aliases to be disabled (not tested in production yet!). Before I work on this further perhaps someone can do an initial review?

I hadn't seen #207, sorry GoZ if I duplicated work here.

stefan.r’s picture

Status: Needs work » Needs review
FileSize
13.94 KB

Updated tests

stefan.r’s picture

Status: Needs work » Needs review
FileSize
13.94 KB

And that last update had a typo :)

stefan.r’s picture

This is green now, so please can anyone review the patch in #219?

davidneedham’s picture

The patch in #219 applies cleanly and the db updates do too. I can confirm that it stops existing circular redirects and keeps them from being created.

hanoii’s picture

patch #219 worked for me as well on an old site, that after upgrade, was triggering infinite loops.

Steven Jones’s picture

Status: Needs review » Needs work

This patch doesn't quite work for me:

+++ b/redirect.module
@@ -877,6 +930,42 @@ function redirect_delete_by_entity_path($entity_type, $entity) {
+        ->fields(array('status' => 1))

I think this needs to set the status to 0, which is the disabled state, rather than 1.

This would also suggest that the tests added by the patch are insufficient. I did the following:

  1. Create node with an alias
  2. Edit node and change the alias
  3. Create a second node with the same alias as the first had originally.

With the patch in #219 this left the redirect created during step 2 in tact, because of the above bug. I think we need to assert that not-only that we didn't get a circular redirect, but that we get the right page.

Steven Jones’s picture

Status: Needs work » Needs review
FileSize
14.94 KB

Right. Here's a patch that changes the 1 for a 0, and adds a new test method to make that my test case is covered, and that that fix is picked up.
I think that that it breaks one of the existing tests though, but I'm not sure why.

Steven Jones’s picture

Ah okay, this is because in redirect_path_update we try to see if the redirect exists in the DB already, by calling redirect_load_by_hash

Steven Jones’s picture

Status: Needs work » Needs review
FileSize
14.56 KB

Actually, there's no reason to not load disabled redirects when calling redirect_load_by_hash that I can see.

So here's a patch that removes that condition from the query.

stefan.r’s picture

Thanks Steven, that 1 was obviously supposed to be a 0 :)

As to removing status from "load by hash", that makes sense indeed. We shouldn't want to be able to create a new redirect with an existing source, even if that old redirect is marked as disabled).

I have made some further refinements to this patch:

a) Moved Enabled checkbox to sit under Language dropdown and a added a help text.

b) By removing status from load_by_hash we are implicitly disallowing people to create any new redirects if another disabled redirect with that same source already exists. Added that same check elsewhere:

@@ -533,10 +535,12 @@
   $redirect = (object) $form_state['values'];

   if (empty($form_state['values']['override'])) {
-    if ($existing = redirect_load_by_source($redirect->source, $redirect->language)) {
+    // Find out if any (disabled or enabled) redirect with this source already exists.
+    if ($existing = redirect_load_by_source($redirect->source, $redirect_language, array(), FALSE)) {
       if ($redirect->rid != $existing->rid && $redirect->language == $existing->language) {

c) Changed wording from "The base source path %source is already being redirected" to "A redirect already exists for the source path %source.". This as this existing redirect might be disabled, at which point the source is not actually "already being redirected".

d) Hid disabled redirects under "Redirects" tab in the node edit screen.

One catch I did make (which is still an issue in all of the patches here) is that if you:

1. edit a node and change a URL alias from first-alias to second-alias
2. edit it again and change the alias back to first-alias (where upon submission the first-alias redirect is disabled to prevent a circular redirect)
3. edit it again and change the alias back to second-alias...

...going to first-alias will give a 404 as the redirect is still disabled. I am not sure there is an easy fix for this though as we can't distinguish between automatically or manually disabled redirects.

stefan.r’s picture

FileSize
6.15 KB
Steven Jones’s picture

...going to first-alias will give a 404 as it is still disabled. I am not sure there is an easy fix for this though as we can't distinguish between automatically or manually disabled redirects.

I think I'd expect redirect module to always try and put a redirect in place if I'm changing the alias, and that's what its configured to do.
In this case shouldn't we just always attempt to add an active redirect unless there's one there already, and it's enabled?

stefan.r’s picture

Fixed a typo in the code

stefan.r’s picture

I think I'd expect redirect module to always try and put a redirect in place if I'm changing the alias, and that's what its configured to do.
In this case shouldn't we just always attempt to add an active redirect unless there's one there already, and it's enabled?

So you're saying to "add an active redirect" if the existing redirect is already disabled, right? That could work.

My worry was:

a) having multiple (disabled and enabled) redirects with the same source in the database, where only one can be enabled at the same time -- which seemed troublesome to implement

and b) annoying users who had manually deactivated a redirect by reactivating it again. But upon further thought, manually disabling those specific redirects sounds like an extreme edge case, and as you well note adding those redirects is what the module is configured to do. If anything we could add a warning that we just automatically reactivated a disabled redirect, but it's hard to not make that message seem cryptic and irrelevant to the large majority of users.

So if by "add an active redirect" you mean update the existing redirect and make it active, yes that may make sense.

Steven Jones’s picture

Yeah, so if there's an existing redirect with the same hash, but just disabled, then we should just re-enable it, because that's really what the user wants, it doesn't matter who disabled it.

stefan.r’s picture

vinmassaro’s picture

Patch in #236 worked for me. It successfully disabled the redirects that caused infinite loops.

stefan.r’s picture

@vinmassaro good to hear! Anyone else care to review?

One little UI thing I was wondering about is in the redirect administration screen (admin/config/search/redirect) the patch lists "disabled" redirects whereas on the node edit screen it hides disabled redirects (in the "URL redirects" tab), assuming content managers would usually just be confused by disabled redirects, especially if they're disabled automatically to prevent a circular redirect. Does this make sense? Perhaps we should add a little snippet to the tab that says only enabled links are listed?

kingofdeal’s picture

Can anybody help me or guide me how to install this patch.

Because I am 100% New. and only thing I know how to install directly on admin panel modules and theme..

Please help me. thanks

tripper54’s picture

@kingofdeal, try https://www.drupal.org/project/redirect/git-instructions .

You'll to install git if you haven't already.

Steven Jones’s picture

@stefan.r yeah I think it's a bit weird that we don't list disabled redirects on the entity forms, so here's a patch that does, I think that makes it much clearer what's going on. I've also added 'bulk operations' for 'disabling' and 'enabling' as that might be a useful thing to do on the admin listing page.

Attached also is an 'only tests' patch so that we can make sure that our tests actually test for something that isn't working in the current code.

Steven Jones’s picture

FileSize
2.54 KB

Sorry, that 'only tests' patch was invalid. Here's a better one.

Steven Jones’s picture

Status: Needs work » Needs review

Awesome, so the patch to review is in #242 and is nice and green. All working for me too.

stefan.r’s picture

@Steven Jones

+    try {
+      // Let modules react to the individual redirects being disabled

"Enabled" :) I also wonder if we should run the hook_redirect_enable_multiple API hook on nodes that are already enabled if enabled nodes are in the function argument of redirect_enable_multiple()? We may want to test if any of the nodes had been enabled already when invoking the enable API hook. Same for the disable API hook, where we'd want to test for nodes that are already disabled and skip those.

Also, the summary in the vertical tabs says "3 redirects" if there is 1 enabled redirect and 2 disabled redirects -- perhaps it should say how many redirects are enabled/disabled?

Otherwise looks good.

trrroy’s picture

This worked for me too. Right after running the update, when I first checked the list of redirects it appeared all were Disabled. I clicked through to edit one and noticed it was Enabled so I cleared cache and then all showed in the list as Enabled (actually a few were disabled as they should be after the update).

stefan.r’s picture

#242 with feedback from #247 included.

Please can we get some further reviews so this can get back to RTBC?

elmertoft’s picture

Patch in #249 works well for me.

pjcdawkins’s picture

Status: Needs review » Reviewed & tested by the community

Tested #249

stefan.r’s picture

@pjcdawkins, thanks!

vinmassaro’s picture

We're having this problem on many sites and the patch in #249 fixes the issue. This patch updates the database so we are a little hesitant to apply it if the patch could change again when the maintainer reviews it. Hoping it gets committed as is with any changes going forward just being code changes, not database. Thanks!

davidneedham’s picture

The patch in #249 applies cleanly, but seems to fail on db updates.

Update: This was my bad. I failed to run updates after updating redirect to the latest dev prior to applying this patch. Everything seems fine now and the db updates apply cleanly and appear to have resolved the issues.

stefan.r’s picture

@davidneedham might your issue have been due to this? #1662704: move RedirectController to a separate include file

stefan.r’s picture

@vinmassaro, I realize at this point several people are already running this patch so if the database changes, I'll endeavor to provide update hooks.

In any case the patch seems close enough to be committed at this stage.

jsenich’s picture

Patch #249 works for me. Big thanks to everybody that worked on it!

weri’s picture

We had the problem with circular redirects on multiple sites. The Patch #249 fixes the problem and seems to work properly on all sites.

candelas’s picture

Patch in #249 works well for a multilingual site. Thanks a lot. I hope this goes to dev :)

askibinski’s picture

Same here, applied #249 to several sites without any problems.

mrmikedewolf’s picture

I prefer the patch in #146. In my opinion #249 makes too many changes to the structure of the module. #146 is simple, concise, and solves the problem without reengineering. The firm that I work with has been using this patch on enterprise drupal websites for over a year and have never had a problem. Many thanks to SylvainM.
I've renamed it to follow drupal conventions. If this is committed, please credit the original author.

stefan.r’s picture

@mrmikedewolf: there was some discussion about that earlier and the module maintainer (Dave Reid) did not want to take the approach in #146, as such there is very little chance of that ever getting committed. Refer to comments #193, #195, #199, #204 for reference.

stefan.r’s picture

Anonymous’s picture

peterx’s picture

Status: Needs review » Reviewed & tested by the community

Works on my site as described on the box. Given the age of the RC2, this change should be committed then cut to RC3. The module page will need a reminder to run update.php.

candelas’s picture

I think it is very important to commit this patch, since there are other patches like https://www.drupal.org/node/905914 that will have to be modified. Thanks a lot for your excellent work :)

candelas’s picture

Also I have been using it in a multilingual site for 13 days, renaming nodes and no circular redirects have shown.

vadym.kononenko’s picture

Patch updated to current -dev.

stefan.r’s picture

#249 is already against current dev, there is no need to re-roll

candelas’s picture

Status: Needs work » Reviewed & tested by the community

I change the status that was changed by #270 after the failure.

Charles Belov’s picture

I'm concerned about "Includes a hook_update_N() to remove any existing redirects." in the proposed solution, at least the proposed solution that is in the summary at the top of this issue.

(Edit: Removed steps involving Workbench Moderation from this issue, moved them to #2395917: Redirection and alias created with same alias/node pair when renaming draft node per @stefan.r in #278 and related that issue to this one, so as not to cause confusion in this current issue.)

stefan.r’s picture

@Charles Belov does the patch in #249 address this concern? That update hook disables redirects, it doesn't delete them.

Charles Belov’s picture

Stephen - If #249 disables redirects, then it sounds like it doesn't do what I want.

The following is a description of what I would be seeking, without reference to #249.

I want to keep all but one of the redirects and have them be still active. What I don't want is something to have the same entry in both the redirect table and the alias table; such an entry belongs in the alias table only. The circular redirect appears to me to be the fault of the newest page alias being present in both the redirect table and the alias table.

(Edit: Removed lengthy description involving Workbench Moderation from this issue, moved them to #2395917: Redirection and alias created with same alias/node pair when renaming draft node per @stefan.r in #278 and related that issue to this one, so as not to cause confusion in this current issue.)

If I have misunderstood the original issue and my steps in #274 are actually a separate, but related, issue, then I apologize for inadvertently hijacking this thread. Please let me know and I'll be happy to create a new issue and relate it to this issue.

If I have misunderstood what #249 is disabling and it's actually doing one of the above two options, please clarify and I'll be happy to try it. As described it sounds like it's not doing what I'm trying to accomplish.

alfaguru’s picture

@Charles, yes, I have just come here having previously applied patch #249 yet seeing an infinite loop after reverting a file revision. This module should never permit a redirect to exist that matches the current alias for an entity. Unfortunately when making reversions this can happen, and I suspect it's quite hard to guard against, given the complexities involved.

Can I suggest that the old logic that displayed a message be resurrected? At the same time, if that event occurs the active redirect should be automatically disabled. Also, given that it may be seen by people who are not developers the message text should be made more appropriate for a general audience.

stefan.r’s picture

@Charles I may be wrong but that sounds like a separate issue (and @alfaguru's sounds like yet another one, perhaps hook_path_update() is not triggered during a reversion?)

Also as to the ""Oops, looks like this request tried to create an infinite loop. We do not allow such things here. We are a professional website!" message, that exists only in older versions of redirect and doesn't appear in the dev version anymore.

alfaguru’s picture

@stefan as regards the message, I know it no longer appears, but it's the logic associated with it that I think has some value. First, it stops the page breaking, which is surely a good thing, and offers the opportunity to do something about the underlying problem.

I don't know, because I haven't investigated, how the path is getting updated in this case and whether that hook is triggered. Ideally I would like this module to be more robust and less dependent on other modules doing the right thing. I got another infinite loop shortly after reporting this one, for a node this time, so it's not just file revisions (which are pretty bleeding edge so one expects some hiccups).

stefan.r’s picture

Can you investigate how that node infinite loop happened, or give steps to recreate that issue?

You're the first one to report this issue and the patch in this issue has been having essentially the same behavior for over a year so your specific issue may not be due to the patch.

peterx’s picture

A bad message is better than a white screen of death. Anything is better than leaving us with a dead screen. Given that the bad message would be seen mostly by internal staff, editors etc, it is better than the dead screen. I think we should put through a fix for the original dead screen problem then work on other cases.

rcodina’s picture

I just can't understand why this issue is taking so long to be fixed (2 years). There are a lot of users of this module experiencing this problem and most of them don't know about php/git and only mind modules to work correctly. The patch on #146 works like a charm. Why it hasn't been commited yet? I know you want to keep old links that produces the loops but what is more important here is to fix the issue ASAP so users don't get pissed off.

stefan.r’s picture

@peterx if you want back the old message, you can apply a reverse patch of the patch posted in #57 over at #1263832: Remove session-based flood / loop detection since it performs poorly, and has unfixable race conditions. Keep in mind there are issues with it (as mentioned in the other thread).

Just to confirm, you are running the dev version with patch #249 and have run all the update hooks?

As to why we aren't committing anything, or even any "quick-and-dirty" fixes which may introduce other issues such as mentioned in #281/#282, it's likely because the maintainer is busy -- last time he popped into this thread seeems to be September 24, 2014.

rcodina’s picture

@stefan.r A lot of people told patch #146 was fine with no undesired side effects. In fact, I'm running it on several websites with no problems so far. If mantainer is busy maybe someone can ask to be co-mantainer. I think a critical issue like this cannot be open for 2 years (so far).

peterx’s picture

@stephan, I am not thinking of using the old message for the basic fix. I was thinking of using the basic fix for the most common case then put up messages for the edge cases while we develop fixes for them. Have a "quick and dirty" fall through message when all else fails. Given that loops end up as dead screen space with nothing to help the user, making something visible is preferable.

The loops can also silently kill cron and processes. Something, anything, in watchdog could be the first response to each new case.

fenstrat’s picture

The issue summary really needs updating here. The approach of deleting the redirects was changed from #193 onwards based on feedback from @Dave Reid (#195 has a use case). Disabling the redirects is a sound alternative and the direction the module maintainer indicated. With an updated issue summary this is much more likely to get committed.

stefan.r’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update
stefan.r’s picture

Issue summary: View changes
candelas’s picture

@stefan.r a new dev has been committed. I don't know if you will have to change something. In my site your patch is working grate.

stefan.r’s picture

@candelas looks like tests still pass

candelas’s picture

@stefan.r super. I hope it gets in dev since it is the last critical bug and many patches are pending and maybe need to be changed after this. Do you think that it can be set to patch to be ported?

chiebert’s picture

Just tested against latest -dev (2015-Jan-05), and while the patch in #249 applied cleanly, I now get a PDOException:

PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'status' in 'where clause': SELECT redirect.rid AS rid FROM {redirect} redirect WHERE (status = :db_condition_placeholder_0) AND( (source LIKE :db_condition_placeholder_1 ESCAPE '\\') OR (source = :db_condition_placeholder_2) )AND (language IN (:db_condition_placeholder_3, :db_condition_placeholder_4)) ; Array ( [:db_condition_placeholder_0] => 1 [:db_condition_placeholder_1] => landing [:db_condition_placeholder_2] => [:db_condition_placeholder_3] => en [:db_condition_placeholder_4] => und ) in redirect_load_by_source() (line 580 of /Users/my-user/Sites/mysite/sites/all/modules/redirect/redirect.module).

Running on local MAMP environment, PHP 5.3.29, drupal 7.34, redirect-7.x-1.x-dev (2015-Jan-05) patched only with #249

vinmassaro’s picture

@chiebert: Did you apply database updates?

chiebert’s picture

@vinmassaro Clearly not. Lesson learned. Patch #249 applies cleanly against redirect-7.x-1.x dev (2015-Jan-05), and works as designed once the lame user runs drush updb...

Excuse me while I go get another coffee...

rwilson0429’s picture

I can't get #249 to apply cleanly. When I try to apply the patch I get the following messages. I tried to patch on both my local Windows7 machine and on my staging Linux host system. Any help on what I'm doing wrong is much appreciated.

$ git apply -v fix_and_prevent-1796596-249.patch
Checking patch redirect.admin.inc...
Checking patch redirect.api.php...
Checking patch redirect.install...
Checking patch redirect.js...
Checking patch redirect.module...
Hunk #1 succeeded at 377 (offset 1 line).
Hunk #2 succeeded at 386 (offset 1 line).
error: while searching for:
 * @param $source
 *   The source of the URL redirect.
 *
 * @return
 *   An array of URL redirect objects indexed by redirect IDs.
 *

error: patch failed: redirect.module:529
error: redirect.module: patch does not apply
Checking patch redirect.test...
$
stefan.r’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
22.27 KB

re-roll of #249 -- looks like a commit from yesterday finally stopped the patch from applying :)

IMPORTANT: remember to run database updates after applying this patch!

stefan.r’s picture

Issue summary: View changes
stefan.r’s picture

Status: Needs review » Reviewed & tested by the community

Setting back to RTBC

candelas’s picture

@stefan.r looks like Dave Reid is reviewing the issue and committing. I hope he decides to commit this one and other bugs can be checked from this and you don't have to adapt the patch... :)

alfaguru’s picture

Further to my comment above I found that the code I was using had not got the latest patch after all. Have applied it now and all seems good so far. Apologies for any confusion.

jackalope’s picture

The patch in #297 kills my D7 site when I apply it to the most recent dev release, causing a "The website encountered an unexpected error. Please try again later." message to display on the site.

After reverting the patch I can access the site again and see this error in the database log:

PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'status' in 'where clause': SELECT redirect.rid AS rid FROM {redirect} redirect WHERE (status = :db_condition_placeholder_0) AND (source LIKE :db_condition_placeholder_1 ESCAPE '\\') AND (language IN (:db_condition_placeholder_2, :db_condition_placeholder_3)) ; Array ( [:db_condition_placeholder_0] => 1 [:db_condition_placeholder_1] => in-the-news/29588/194 195 206 239 [:db_condition_placeholder_2] => en [:db_condition_placeholder_3] => und ) in redirect_load_by_source() (line 584 of /var/www/sitename/public_html/sites/all/modules/redirect/redirect.module).

Trying to apply older patches causes the same problem, so I've just reverted to an earlier (patched) dev version of redirect for now.

vinmassaro’s picture

@jackalope: Did you apply database updates? See comment #295, fixed after applying database updates.

jackalope’s picture

@vinmassaro - I did apply database updates, but unfortunately the site remained broken with that error message until I reverted the update & patch.

stefan.r’s picture

@jacklope running the update hooks should fix the problem you are having. Update hook number 7101 adds the status column to the redirect table, and it is impossible for that error to appear when the column already exists. So if it's not being added, something obvious is being overlooked on your setup... Some suggestions:

a) You'd need to first apply the patch and then run database updates (drush updatedb or going to update.php). The 7101 update hook should be listed among updates to apply -- this is the update hook that adds the "schema" field. If this is not the case, then:

b) Make sure that the redirect files you have patched are the ones drupal is actually using (check by doing SELECT filename FROM system WHERE name = 'redirect';). Check that that file has actually been patched, and whether you didn't accidently patch another one.

c) Double check that schema_version = 7100 before running update hooks when doing SELECT schema_version FROM system WHERE name = 'redirect';

stefan.r’s picture

FileSize
185.3 KB
35.5 KB

I just double checked that update hooks still run correctly with the latest dev release and patch. This still seems to be the case.

Before updates, the correct updates appear in update.php:

After updates, the status column is created correctly on the redirect table:

squarecandy’s picture

I was able to apply the patch successfully and it is working as expected so far.

However, I did need to run drush updatedb twice to get it to work. Not sure if there's a way to get the full database update to run in one go…that's beyond my skill set. But here's what my drush updatedb looked like.

[root@xxxxxxxxxxxxxxxxx redirect]# drush updatedb
 Redirect  7100  Empty update to trigger registry and entity info rebuild.  
 Redirect  7101  Add status field to redirect table.                        
 Redirect  7102  Disable existing redirects causing infinite loops.
Do you wish to run all pending updates? (y/n): y
Performed update: redirect_update_7100                                                                                                     [ok]
Performed update: redirect_update_7101                                                                                                     [ok]
PHP Fatal error:  Class 'RedirectController' not found in /var/www/vhosts/example.com/httpdocs/includes/common.inc on line 7885

Fatal error: Class 'RedirectController' not found in /var/www/vhosts/example.com/httpdocs/includes/common.inc on line 7885
Drush command terminated abnormally due to an unrecoverable error.                                                                         [error]
Error: Class 'RedirectController' not found in /var/www/vhosts/example.com/httpdocs/includes/common.inc, line 7885
The external command could not be executed due to an application error.                                                                    [error]
'all' cache was cleared.                                                                                                                   [success]
Finished performing updates.                                                                                                               [ok]
[root@xxxxxxxxxxxxxxxxx redirect]# drush updatedb
 Redirect  7102  Disable existing redirects causing infinite loops.
Do you wish to run all pending updates? (y/n): y
Performed update: redirect_update_7102                                                                                                     [ok]
'all' cache was cleared.                                                                                                                   [success]
Finished performing updates.
squarecandy’s picture

just to follow up - the patch is working for me and this is a pretty essential fix for this module. I would vote for adding it to the dev version ASAP even if the database update situation doesn't have an easy fix.

jackalope’s picture

@stefan.r -- thanks for the tips. The key seems to be this:

c) Double check that schema_version = 7100 before running update hooks when doing SELECT schema_version FROM system WHERE name = 'redirect';

For some reason, schema_version = 7101 before I run the new update hooks, rather than 7100. This has been the case on each of my Drupal 7 sites; I'm not sure why! But running UPDATE system SET schema_version=7100 WHERE name = 'redirect'; fixes that and lets me use the newest patch.

ChaseOnTheWeb’s picture

@jackalope -- You didn't happen to be using one of the early patches from this thread, did you? Those early patches defined a 7101 update before the maintainer decided to go in a different direction, so the "official" 7101 in the newer patches isn't invoked.

Though that's a potential gotcha anytime you use a patch with an update hook, the need to fix the system table might be good to note in the release notes since quite a few people throughout this thread (and surely many lurkers) have been using the early patches.

jackalope’s picture

@marleythedog -- yes, I've definitely used some of the early patches on the thread. Makes sense that that's the problem on all of the sites I maintain. UPDATE system SET schema_version=7100 WHERE name = 'redirect'; is a fairly easy solution.

Thanks so much for the help, everyone!

stefan.r’s picture

@squarecandy: the RedirectController issue is very unlikely to be related to this patch, as of 4 days ago it's in redirect.controller.inc (#1662704: move RedirectController to a separate include file) and this file is included in redirect.info. Could you cross-post your error message in that issue? The update hook there may need a cache clear.

stefan.r’s picture

Just to summarize in case any maintainer reviews this issue: the patch in the current form has been RTBC since #249, for over 2 months (ie. November 5, 2014), and no issues have come up yet other than some support requests and a possible incompatibility reported by "Charles Belov" which we can't replicate.

Other than that, patch #249 has received positive reviews from the following users:

  • elmertoft
  • pjcdawkins
  • vinmassaro
  • davidneedham
  • jsenich
  • weri
  • candelas
  • askibinski
  • peterx
  • chiebert
  • alfaguru
  • squarecandy
  • vegardjo
vegardjo’s picture

I can also confirm that patch in #249 works for me.

Leeteq’s picture

Thanks for pointing out the status so clearly, @stefan.r , high time to get this into -dev.
It cannot be worse to leave -dev in its current state than committing this one.
Depending on other issues, perhaps enough to trigger a new "recommended" release, although in review of issues such as this one, "recommended" has long lost its meaning anyway.

Edit: I meant "recommended", not "stable". Corrected.

kylebrowning’s picture

Status: Reviewed & tested by the community » Needs work

Patch seems to fail against current dev.

Hunk #1 FAILED at 18.
Hunk #2 FAILED at 85.
Hunk #3 FAILED at 367.
Hunk #4 FAILED at 380.
Hunk #5 FAILED at 535.
Hunk #6 FAILED at 831.
Hunk #7 FAILED at 852.
Hunk #8 FAILED at 871.
8 out of 8 hunks FAILED -- saving rejects to file patch.rej
patching file redirect.api.php
patching file redirect.install
patching file redirect.js
patching file redirect.module
Hunk #1 succeeded at 377 (offset 1 line).
Hunk #2 succeeded at 386 (offset 1 line).
Hunk #3 succeeded at 552 with fuzz 2 (offset 5 lines).
Hunk #4 succeeded at 564 (offset 5 lines).
Hunk #5 succeeded at 711 (offset 5 lines).
Hunk #6 succeeded at 734 (offset 5 lines).
Hunk #7 succeeded at 778 (offset 5 lines).
Hunk #8 succeeded at 887 (offset 5 lines).
Hunk #9 succeeded at 946 (offset 5 lines).
Hunk #10 succeeded at 1134 (offset 5 lines).
Hunk #11 succeeded at 1626 (offset 5 lines).
Hunk #12 succeeded at 1684 (offset 5 lines).
patching file redirect.test
vinmassaro’s picture

Status: Needs work » Reviewed & tested by the community

@kylebrowning: patch in #297 applies cleanly to 7.x-1.x-dev for me:

/tmp $ wget -q https://www.drupal.org/files/issues/fix_and_prevent-1796596-297.patch
/tmp $ git clone --branch 7.x-1.x http://git.drupal.org/project/redirect.git
Cloning into 'redirect'...
remote: Counting objects: 394, done.
remote: Compressing objects: 100% (245/245), done.
remote: Total 394 (delta 257), reused 220 (delta 148)
Receiving objects: 100% (394/394), 123.20 KiB | 210.00 KiB/s, done.
Resolving deltas: 100% (257/257), done.
Checking connectivity... done.
/tmp $ cd redirect
/tmp/redirect $ patch -p1 < /tmp/fix_and_prevent-1796596-297.patch
patching file redirect.admin.inc
patching file redirect.api.php
patching file redirect.install
patching file redirect.js
patching file redirect.module
patching file redirect.test

Testbot is still green, last tested 01/06/15, a day after the last commit to 7.x-1.x-dev.

DamienMcKenna’s picture

+1 for the patch in #297 applying correctly against the current 7.x-1.x-dev (I just tested it).

Anybody’s picture

This is a really really imporant issue and I'm happy to confirm that the latest patch works great. Can we get this commited? :)

candelas’s picture

I agree with @Anibyody, @stefan.r don't you think that it can be classified as Patch (to be ported)?

The last submitted patch, 269: redirect--fix_and_prevent-1796596-249.patch, failed testing.

Anybody’s picture

@Dave Reid: #297 should be the right one

stefan.r’s picture

Woohoo! Tests are green! :)

Dave Reid’s picture

  1. +++ b/redirect.install
    @@ -96,6 +103,7 @@ function redirect_schema() {
           'source_language' => array('source', 'language'),
    +      'status' => array('status'),
    

    So the 'source_language' index was designed specifically for the redirect_load_by_source(). Should 'status' also be included in that index instead of it's own separate index?

  2. +++ b/redirect.install
    @@ -363,3 +372,40 @@ function _redirect_migrate_path_redirect_variables() {
    +  $query = db_select('redirect', 'r');
    +  $query->join(
    +    'url_alias',
    +    'u',
    +    'r.source = u.alias AND r.redirect = u.source AND r.language = u.language'
    +  );
    +  $query->fields('r', array('rid'));
    +  $result = $query->execute();
    +  $rids_to_disable = $query->execute()->fetchCol();
    +  if (count($rids_to_disable) > 0) {
    

    Can be simplified just using $rids = db_query("SELECT r.rid FROM redirect r INNER JOIN url_alias u ON r.source = u.alias AND r.redirect = u.source AND r.language = u.language")->fetchCol() since that query is valid SQL-99.

  3. +++ b/redirect.install
    @@ -363,3 +372,40 @@ function _redirect_migrate_path_redirect_variables() {
    +    return t('No circular redirect causing infinite loop was found.');
    

    This statement looks odd if it's not plural.

  4. +++ b/redirect.module
    @@ -384,9 +386,25 @@ function redirect_path_update(array $path) {
    +    // If the existing redirect is disabled, re-enable it:
    

    Should end in a period not a colon.

  5. +++ b/redirect.module
    @@ -534,6 +552,9 @@ function redirect_load_by_hash($hash, $reset = FALSE) {
      *
    

    Should not be an extra line before the previous @param statement.

  6. +++ b/redirect.module
    @@ -687,7 +711,7 @@ function redirect_validate($redirect, $form, &$form_state) {
    -      form_set_error('source', t('The source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => redirect_url($redirect->source, $redirect->source_options), '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid))));
    +      form_set_error('source', t('A redirect already exists for the source path %source. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => redirect_url($redirect->source, $redirect->source_options), '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid))));
    

    Not sure why this is really changed.

  7. +++ b/redirect.module
    @@ -853,6 +887,36 @@ function redirect_delete_by_path($path) {
    +function redirect_disable_by_path($path, $language) {
    +  $query = db_select('redirect');
    +  $query->addField('redirect', 'rid');
    +  $query_or = db_or();
    +  $query_or->condition('source', db_like($path), 'LIKE');
    +  $query->condition($query_or);
    +  if ($language) {
    +    $query->condition('language', $language);
    +  }
    +  $rids = $query->execute()->fetchCol();
    +
    +  if ($rids) {
    +    return redirect_disable_multiple($rids);
    +  }
    +}
    

    This seems to duplicate a lot of the query from redirect_load_by_source(). Seems like it might be useful to add a new redirect_fetch_rids_by_path() function which both this and redirect_load_by_source() would use.

  8. +++ b/redirect.module
    @@ -882,6 +946,82 @@ function redirect_delete_by_entity_path($entity_type, $entity) {
    +function redirect_disable_multiple(array $rids) {
    

    This function seems really strange to me. I'd rather this loop through each redirect and manually set $redirect->status and call redirect_save(). Plus rather than needing two functions we could have just one: redirect_change_status_multiple(array $rids, $status);

DamienMcKenna’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
22.24 KB

This patch addresses items 2, 3, 4 and 5 from Dave Reid's code review.

stefan.r’s picture

FileSize
1.84 KB

Interdiff for reference. Changes look good to me.

stefan.r’s picture

Status: Needs review » Needs work

The last submitted patch, 329: redirect-n1796596-329.patch, failed testing.

stefan.r’s picture

FileSize
22.47 KB
182 bytes

This addresses items 1, 7 and 8.

As to why #6 was changed, with this patch it's not necessarily true that "the source path is already being redirected". A redirect with that source path exists, but it may be disabled (ie. not redirecting the source path).

stefan.r’s picture

Status: Needs work » Needs review
stefan.r’s picture

Anybody’s picture

#331 works for me. Further feedback please to get this live.

kylebrowning’s picture

Do we need to do anything if we applied #297 before applying #331?

stefan.r’s picture

Nope, we changed a mysql index, but for now that shouldn't matter.

I'll provide an upgrade path from #297 to the final patch as soon as this gets committed.

Nebel54’s picture

I checked out patch #331, it looks very promising, but:

+++ b/redirect.install
@@ -363,3 +375,43 @@ function _redirect_migrate_path_redirect_variables() {
+  if (count($rids_to_disable) > 0) {
+    redirect_disable_multiple($rids_to_disable);
+    return format_plural(count($rids_to_disable), '1 circular redirect causing infinite loop was disabled.', '@count circular redirects causing infinite loop were disabled.');
+  }

The function redirect_disable_multiple doesn't exist anymore, so the update_hook is broken when redirect loops exist.

stefan.r’s picture

FileSize
22.48 KB
509 bytes
rv0’s picture

Tried #338
Also had to run the update twice, class 'RedirectController' not found
Here's what happened:

drush dl redirect --dev
Install location sites/all/modules/contrib/redirect already exists. Do you want to overwrite it? (y/n): y
Project redirect (7.x-1.x-dev) downloaded to sites/all/modules/contrib/redirect. [success]
cd sites/all/modules/contrib/redirect/
wget https://www.drupal.org/files/issues/redirect-n1796596-338.patch
git apply -v redirect-n1796596-338.patch
drush updatedb
Redirect 7100 Empty update to trigger registry and entity info rebuild.
Redirect 7101 Add status field to redirect table.
Redirect 7102 Disable existing redirects that could cause infinite loops.
Do you wish to run all pending updates? (y/n): y
Performed update: redirect_update_7100 [ok]
Performed update: redirect_update_7101 [ok]
Drush command terminated abnormally due to an unrecoverable error. [error]
Error: Class 'RedirectController' not found in /home/domains/example.com/public_html/includes/common.inc, line 7885

Also, it did not solve circular redirect on my site (EDIT: fixed the issue, but browser had cached the redirect)

stefan.r’s picture

@rv0, if you first run the updates, and then apply the patch (after) before running updates again, you'll see that the RedirectController issue is not caused by this patch. See #1662704: move RedirectController to a separate include file

It looks like that empty update hook (#7100) doesn't actually cause a registry rebuild?

Perhaps you can try drush registry-rebuild after downloading the latest dev, as with this fatal error, update hook #7102 never runs, so the circular redirect can't be fixed.

heddn’s picture

Status: Needs review » Needs work

re #340: update.php/ drush updb will only flush cache and rebuild the registry at the end of its processing. Since there are multiple update hooks for redirect, the earlier one won't rebuild the registry in time for the second.

Since this isn't the first report of the issue, I'm marking as needs work. The only option I can think of is to manually rebuild the registry in this hook_update. Unless you can think of another solution.

stefan.r’s picture

@heddn: as that RedirectController issue is unrelated to this patch/this issue, shouldn't that be in a separate drupal.org issue?

Indeed the solution seems to be as simple as putting a registry_rebuild() in an update hook. redirect_update_7100 seems like an obvious candidate :)

heddn’s picture

re #342: It is only an issue because of the update hook in here. Normally, cache clear isn't needed between update hooks so adding an unnecessary registry rebuild seems unneeded. But since this patch's update does use something that requires a registry rebuild, I feel it is the place of this issue to resolve. Image that the hook_update in this issue never got committed (for some reason). Then the rebuild in redirect_update_7100 is entirely unneccessary.

stefan.r’s picture

Status: Needs work » Needs review
stefan.r’s picture

FileSize
22.77 KB
rv0’s picture

RE: @stefan.r

Perhaps you can try drush registry-rebuild after downloading the latest dev, as with this fatal error, update hook #7102 never runs, so the circular redirect can't be fixed.

well, I did run "drush updatedb" twice, so update 7102 did successfully run after all.. So all is fine. (for some reason the relevant part got trimmed in my reply, I tried to edit but it got trimmed every time.. d.o. bug i guess..)

Anyhow, the need to run it twice was already reported in #307

Nebel54’s picture

Just wanted to report back that the patch #338 did work fine in our setup, it detected and removed 40 circular redirects during the update.

stefan.r’s picture

Just for reference, here's an interdiff between #297 (that was marked RTBC and revised after review by Dave Reid) and the current patch (#345).

To summarize, it includes fixes to the points pointed out by Dave Reid as well as a modified update hook #7000 in order to address the issue with the registry being outdated pointed out in #254, #307, #339, #341

It would be good if we could get some further reviews so we can get this back to RTBC!

stefan.r’s picture

nicholasruunu’s picture

Status: Needs review » Reviewed & tested by the community

#349,

Just updated to redirect --dev and patched with #345.
It seems to work great, in update.php I got the message that 5 redirects were removed.
I tried changing a url on a node (got redirect) and when changing back to the original url the redirect was disabled.

Only problem is that I have no way of checking which redirects that was removed.
It is a bit scary not being able to check affected nodes so it would be nice to at least display them in the update message if possible.

nicholasruunu’s picture

Status: Reviewed & tested by the community » Needs review

Hm, I went back and checked this, got the 5 rids from $rids_to_disable, checked them all and they had status 1.
After the update when they're supposed to be disabled, all 5 still have status 1.

Can someone else reproduce this, or is it a problem with my db?

Update:
Problem seems to be redirect_change_status_multiple doesn't load the redirects with any status.

That leads to $redirect->original->status not existing and never goes into this:
redirect_save function L:799

    // Invoke post-disable hooks if disabling a redirect.
    if ($redirect->status == 0 && isset($redirect->original) && $redirect->original->status == 1) {
      module_invoke_all('redirect_disable', $redirect);
    }

Update:
Seems like the schema cache might be the problem.

drupal_get_schema('redirect') in this process doesn't return status in fields so it won't be updated.
common.inc:7111

nicholasruunu’s picture

I have fixed the cache problem by running cache_clear_all('schema', 'cache') after the new fields are added to the db.
I've also added which routes that gets disabled so you have a chance to make sure they work, here's an example:
http://cl.ly/image/1S2C2V2R3B29

Status: Needs review » Needs work

The last submitted patch, 352: redirect-schema-cache-fix-1796596-352.patch, failed testing.

stefan.r’s picture

Status: Needs work » Needs review
FileSize
1.82 KB
23.47 KB

re-roll of @nicholasruunu's patch.

Good find on the caching issue and the new message is great. Changes look good!

nicholasruunu’s picture

Status: Needs review » Reviewed & tested by the community

#354, Thanks for rerolling. This works for me, reverted db and code and applied the latest patch.
One issue might be that updating the module without running update.php will give fatal error pretty much everywhere.
Is this something that could/should be fixed? I'm happy with this though.

Alan D.’s picture

updating the module without running update.php will give fatal error pretty much everywhere... Is this something that could/should be fixed?

Only if it blocks the site from running update.php / drush update db

stefan.r’s picture

The fatal error is about the class RedirectController not being found due to a file being renamed, correct?

I think you can still run update.php when that is the case, so we should be able to live with this.

stefan.r’s picture

stefan.r’s picture

Issue summary: View changes
nicholasruunu’s picture

@stefan.r, My fatal error was trying to fetch redirect.status from the db, but update.php works, not sure about drush updatedb.

rv0’s picture

Tested #354 on another site, no issues at all, applies cleanly and seems to fix the issues.

hass’s picture

Status: Reviewed & tested by the community » Needs work

"Save" strings in tests need to be t'ified.

nicholasruunu’s picture

@hass, can you elaborate? I don't quite understand.

stefan.r’s picture

Status: Needs work » Needs review
FileSize
23.48 KB
jackalope’s picture

Just tried the patch in #364 against redirect-7.x-1.x-dev, and the changes to redirect.test fail!

Status: Needs review » Needs work

The last submitted patch, 364: redirect-schema-cache-fix-1796596-364.patch, failed testing.

stefan.r’s picture

Status: Needs work » Needs review
FileSize
2.79 KB
23.88 KB
hass’s picture

Great. In what century is this case getting fixed? ~2 years are really crazy if basic functionality of this module makes it completely broken and requires a downgrade. No idea why so many people have rc1 installed.

Charles Belov’s picture

@nicholasruunu #363 t'ified = All text strings need to be wrapped in the t() function to make them translatable.

stefan.r’s picture

@hass can this go back to RTBC?

candelas’s picture

@stefanr thanks for all your work. It would be good if you use t() on the few lines that they haven't. Good work :)

stefan.r’s picture

@candelas which lines are that? Aren't all the "Save" strings wrapped in t() in #367?

candelas’s picture

From your patch. Maybe I am wrong... :) I mark with //NEEDS T() I hope it helps :)

+/**
* Act on a redirect being redirected.
*
* This hook is invoked from redirect_redirect() before the redirect callback
diff --git a/redirect.install b/redirect.install
index 6ccb75b..2b2a7d3 100644
--- a/redirect.install
+++ b/redirect.install
@@ -86,7 +86,14 @@ function redirect_schema() {
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
- 'description' => 'The timestamp of when the redirect was last accessed.'
+ 'description' => 'The timestamp of when the redirect was last accessed.', //NEEDS T()
+ ),
+ 'status' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 1,
+ 'description' => 'Boolean indicating whether the redirect is enabled (visible to non-administrators).', //NEEDS T()
),
),
'primary key' => array('rid'),

----------------------------------------------------------

@@ -363,3 +376,71 @@ function _redirect_migrate_path_redirect_variables() {
variable_del($old_variable);
}
}
+
+/**
+ * Add status field to redirect table.
+ */
+function redirect_update_7101() {
+ $field = array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 1,
+ 'description' => 'Boolean indicating whether the redirect is enabled (visible to non-administrators).', //NEEDS T()
+ );
+ db_add_field('redirect', 'status', $field);
+ db_drop_index('redirect', 'source_language');
+ db_add_index('redirect', 'status_source_language', array(
+ 'status',
+ 'source',
+ 'language',
+ ));

-----------------------------------------------------------

diff --git a/redirect.test b/redirect.test
index 1c237f8..33c6d23 100644
--- a/redirect.test
+++ b/redirect.test
@@ -229,16 +229,59 @@ class RedirectFunctionalTest extends RedirectTestHelper {
$node = $this->drupalCreateNode(array('type' => 'article', 'path' => array('alias' => 'first-alias')));

// Change the node's alias will create an automatic redirect from 'first-alias' to the node.
- $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), 'Save');
- //$redirect = redirect_load_by_source('first-alias');
- //$this->assertRedirect($redirect);
+ $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), t('Save'));
+ $this->drupalGet('first-alias');
+ $this->assertText($node->title);
+
+ $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'first-alias'), t('Save'));
+ $this->assertResponse(200, "Changing node's alias back to 'first-alias' does not break page load with a circular redirect."); //NEEDS T()
+ $this->assertNoText('Infinite redirect loop prevented.'); //NEEDS T()
+ $this->drupalGet('second-alias');
+ $this->assertText($node->title);
+
+ $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), t('Save'));
+ $this->assertResponse(200, "Changing node's alias back to 'second-alias' does not break page load with a circular redirect."); //NEEDS T()
+ $this->assertNoText('Infinite redirect loop prevented.');
+ // Check that first-alias redirect has been re-enabled.
+ $this->drupalGet('first-alias');
+ $this->assertText($node->title);
+ }
+
+ function testPathAddOverwriteRedirects() {
+ // Create an initial article node with a path alias.
+ $first_node = $this->drupalCreateNode(array('type' => 'article', 'path' => array('alias' => 'first-alias')));
+ // Change the node's alias will create an automatic redirect from 'first-alias' to the node.
+ $this->drupalPost("node/{$first_node->nid}/edit", array('path[alias]' => 'second-alias'), t('Save'));
+
+ // Now create a second article node with the same alias as the redirect
+ // created above.
+ $second_node = $this->drupalCreateNode(array('type' => 'article', 'path' => array('alias' => 'first-alias')));

- $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'first-alias'), 'Save');
- //$redirect = redirect_load_by_source('second-alias');
- //$this->assertRedirect($redirect);
+ // Visit the path 'first-alias' which should be an alias for $second_node.
+ $this->drupalGet('first-alias');
+ $this->assertNoText($first_node->title, 'Adding a new path alias that matches an existing redirect disables the redirect.'); //NEEDS T()
+ $this->assertText($second_node->title, 'Adding a new path alias that matches an existing redirect disables the redirect.'); //NEEDS T()
+ }

- $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), 'Save');
- //$redirect = redirect_load_by_source('first-alias');
- //$this->assertRedirect($redirect);
+ function testDisableEnableRedirect() {
+ // Add a new redirect.
+ $redirect = $this->addRedirect('redirect', 'node');
+ // Check that it is enabled.
+ $this->assertEqual($redirect->status, 1);
+
+ // Disable the redirect.
+ $edit = array('status' => FALSE);
+ $this->drupalPost("admin/config/search/redirect/edit/{$redirect->rid}", $edit, t('Save'));
+ $redirect = redirect_load($redirect->rid);
+ // Check that it has been disabled.
+ $this->assertEqual($redirect->status, 0);
+ $this->drupalGet("admin/config/search/redirect/edit/{$redirect->rid}");
+ $this->assertNoFieldChecked('edit-status', 'status is unchecked'); //NEEDS T()
+ $this->assertNoRedirect($redirect);
+
+ // Re-enable the redirect.
+ $edit = array('status' => 1);
+ $this->drupalPost("admin/config/search/redirect/edit/{$redirect->rid}", $edit, t('Save'));
+ $this->assertRedirect($redirect);
}
}

Berdir’s picture

None of those need t(), schema descriptions and assertion messages are not translated.

candelas’s picture

Thanks @Berdir. So much to learn in Drupal :)
I hope this patch gets in dev. It is really needed.

hass’s picture

@Bedir: t('Save') is correct. This is not an assertion message. Without t() it is not correct. In case you are on a translated page the test will not be able to find and press the save button. Database schema will not be translated like assertion messages.

The last patch was correct change. Nothing else need to be changed.

hass’s picture

#373: None of these require t().

nicholasruunu’s picture

I noticed that from my patch you can get duplicate rids back so some links will come out twice.
If someone wants to group the query in redirect_update_7102 on rids that would be great, otherwise I'll try to fix it when I can.

stefan.r’s picture

@nicholasruunu, as rid is the primary key and the query is SELECT r.rid, r.source, r.redirect FROM redirect r INNER JOIN url_alias u ON r.source = u.alias AND r.redirect = u.source AND r.language = u.language, how is it even possible to get duplicate results from there?

nicholasruunu’s picture

@stefan.r,

You'd think it wouldn't, but still:
http://cl.ly/image/0H371I321q1F

Fixed:
http://cl.ly/image/0Z461k093n16

Update:
We should probably at the same time surround tables with {} for table prefix compatibility.

Update:
It happens when url_alias have two entries with the same source/alias:
http://cl.ly/image/2O2C2Z432N2L

stefan.r’s picture

nicholasruunu’s picture

Status: Needs review » Reviewed & tested by the community

@stefan.r,

Good job, works as expected.

PQ’s picture

Just wanted to show some appreciation for @stefan.r and everyone else on this thread who's been working on this for so long. Thanks for putting so much time and patience in working towards fixing this issue.

hass’s picture

+++ b/redirect.install
@@ -363,3 +376,72 @@ function _redirect_migrate_path_redirect_variables() {
+    $disabled_redirects_list = theme_item_list(array(
+      'title' => '',
+      'items' => $disabled_redirects_list,
+      'type' => 'ul',
+      'attributes' => array(),
+    ));
+
+    $disabled_redirects_message = format_plural(count($rids_to_disable),
+      '1 circular redirect causing infinite loop was disabled:',
+      '@count circular redirects causing infinite loop were disabled:');
+
+    return $disabled_redirects_message . $disabled_redirects_list;
+  }

Not only that this translatable strings are not correct, this can run into serious troubles. You may run into a timeout or megabytes of webpage inside the batch. This makes the batch page failing. I strongly suggest to change this into watchdog message(s).

stefan.r’s picture

@hass what's incorrect about the strings? I'm also reluctant to take out the displayed message, I quite like it, and for a timeout or for "megabytes" to happen we'd need the Drupal install to have thousands and thousands of circular redirects. Maybe I'm wrong but isn't that an extreme edge case?

A watchdog message upon disabling/enabling may be useful in any case, perhaps that should be happening every time a redirect gets disabled/enabled, and not just in the update batch?

hass’s picture

'1 circular redirect causing infinite loop was disabled: !disabled_redirects_list'
This is correct.

There are sites that are large scale. Outputting 10.000 found redirect loops in a list sounds not unrealistic to me and this may bring the batch down. I think an update should be design wise reliable. We should use watchdog for logging and just tell the user there have been xxx redirect loops found and these deleted ones can be found in watchdog. Do not output sooo much data on one page, please. That is very risky.

nicholasruunu’s picture

@hass, That's a really small operation, can't really see how that would be a problem.
I don't really know why watchdog would help with this either.

Did you have any problems with the batch job failing?
Because I had to enable redirect on one site since a function from redirect.module is being used.

And isn't this operation being run in a batch job and thus timeout shouldn't be a problem anyway?

Update:
If the problem is with too much output, perhaps we can limit the output to say 200? It was really useful for me on a site with ~200 redirects.
Then we could put the rest on watchdog.

But what would happen if dblog is disabled?

stefan.r’s picture

Status: Reviewed & tested by the community » Needs work

@hass what specifically makes you concerned about a timeout?

stefan.r’s picture

Status: Needs work » Needs review
FileSize
25.12 KB
2.62 KB

(untested) implementation of @nicholaasruunu's suggestion

For those few sites with no logging at all and more than 200 circular loops, you'd expect them to have a test environment where they can enable logging and run updates before doing them on a live site.

If they lose that bit of information it's not a huge deal anyway. It's not that hard to find the recently disabled redirects if really needed.

Status: Needs review » Needs work

The last submitted patch, 389: redirect-schema-cache-fix-1796596-389.patch, failed testing.

nicholasruunu’s picture

Perhaps just scrap the urls then, you could always just query for status = 0.
Since if you're running the migration you wouldn't have had status before anyway.

stefan.r’s picture

OK, go ahead and take them out of my latest patch :)

Guessing there's a syntax error somewhere as well.

jwilson3’s picture

In addition to syntax error (which i couldn't find reading the patch), a couple minor tweaks:

  • +++ b/redirect.install
    @@ -363,3 +376,82 @@ function _redirect_migrate_path_redirect_variables() {
    +    $max_displayed_results = 200;
    ...
    +    if ($number_of_results > $max_displayed_results) {
    +      $disabled_redirects_message .= t('Only 200 results are displayed. For further results, please check the Drupal log.);
    +    }
    

    Should be something like

    t('Only !max_displayed_results affected redirects are displayed. A complete list is available in the Drupal log.', array('!max_displayed_results' => $max_displayed_results));
    
  • +++ b/redirect.install
    @@ -264,10 +275,11 @@ function redirect_update_7000(&$sandbox) {
    - * Empty update to trigger registry and entity info rebuild.
    + * Registry and entity info rebuild.
      */
     function redirect_update_7100() {
    -  // Do nothing.
    +    registry_rebuild();
    +    entity_info_cache_clear();
     }
    

    Should explain *why* the registry and edit info rebuild is actually needed, if not in the doc block comment, then at least an inline comment inside the functions curly braces.

rooby’s picture

+++ b/redirect.install
@@ -363,3 +376,82 @@ function _redirect_migrate_path_redirect_variables() {
+    if ($number_of_results > $max_displayed_results) {
+      $disabled_redirects_message .= t('Only 200 results are displayed. For further results, please check the Drupal log.);
+    }

The syntax error is here. No quote ending the message text.

stefan.r’s picture

Status: Needs work » Needs review
FileSize
24.25 KB

I've taken it out as requested by @nicholasruunu & @hass. It's logged in watchdog now.

Status: Needs review » Needs work

The last submitted patch, 395: redirect-schema-cache-fix-1796596-395.patch, failed testing.

stefan.r’s picture

Status: Needs work » Needs review
FileSize
24.38 KB

Commented update hook 7100 changes as per #393.

hass’s picture

Did you have any problems with the batch job failing?

Because my database updates are incomplete? A failing update hooks are show stoppers.

And isn't this operation being run in a batch job and thus timeout shouldn't be a problem anyway?

No, a batch step itself can timeout. Only if you split the task into several rounds your are able to prevent a timeout. With 50.000 url aliases not unrealistic.

what specifically makes you concerned about a timeout?

The update hook need to complete. If it fails users are lost. This is not an acceptable situation for an update hook.

vinmassaro’s picture

@stefan.r: what is the procedure for testing the patch in #397 if we've already patched with #249? I'd like to give it a test to throw my hat in the ring to to move this forward, thanks!

nicholasruunu’s picture

@vinmassaro, if you have a database dump pre-update, download the development version of Redirect and apply the patch. Look over the interdiff since last reviewed patch. If it looks good, go through the update procedure again, and if successful, mark as reviewed and tested by the community. Thank you for being willing to participate!

jphelan’s picture

I've applied #397. It applied cleanly to 7.x-1.x-dev and all seems well. Ran updb via drush with no issues. I did a simple test changing the url and changing it back to the original and it disabled the first redirect as expected. I had NOT ever applied #249. Thanks for your work on this stefan.r.

travelvc’s picture

Thank you @stefan.r and everyone for testing. Patch #249 worked for me. I'm now waiting for an official release.

ar-jan’s picture

@travelvc: then it would be better to test #397, because that's the latest patch, currently under review.

hanoii’s picture

@nicholasruunu I second @vinmassaro question on what to do if you applied patch #249, what about when you don't have a pre-update dump of the db to re-apply cleanly?

heddn’s picture

re #404: in your own custom module's hook_install add:

module_load_install('redirect');
redirect_update_710x();
etc.

And in the future when applying a patch that isn't committed yet, move the patch's hook_install logic into your own custom module's hook_install. The reason you have to do this is because the previous run of hook_install has already updated Drupal's system table patch level.

hanoii’s picture

@heddn, thanks, you could also edit the system table to run the update again, but what I am asking is if it's safe to just run the update_710x again or the changes done by the patch in #249 could conflict with the changes that should be done with the latest patch.

candelas’s picture

@heddn thanks I didn't know. Do I have to do that with #146 and #249 patches that I have in two sites? I will investigate that as soon this is in dev, now I am solving other problems.

(@stefan.r 4 more people saying that the patch works and we change status to reviewed and tested by the comunity, don't we?)

Almost there! (it is the longest issue that I met in Drupal)

Alan D.’s picture

Status: Needs review » Needs work

Probably needs the maintainers feedback, but I think that this is still flagged as needs work by:

Outputting 10.000 found redirect loops in a list sounds not unrealistic to me and this may bring the batch down.

So redirect_update_7102(&$sandbox) needs to be batched to avoid this issue.

Completely untested starter for someone:

function redirect_update_7102(&$sandbox) {
  // Initialize information needed by the batch update system.
  if (!isset($sandbox['progress'])) {
    $sandbox['progress'] = 0;
    $sandbox['max'] = db_query('SELECT COUNT(DISTINCT r.rid) FROM {redirect} r INNER JOIN {url_alias} u ON r.source = u.alias AND r.redirect = u.source AND r.language = u.language')->fetchField();
      
    // If there's no data, don't bother with the extra work.
    if (empty($sandbox['max'])) {
      return t('No circular redirects were found that could cause infinite loops.');
    }
  }

  // Process records in groups of 1000.
  $limit = 1000;
  $rids_to_disable = db_query_range('SELECT DISTINCT r.rid FROM {redirect} r INNER JOIN {url_alias} u ON r.source = u.alias AND r.redirect = u.source AND r.language = u.language', $sandbox['progress'], $limit)->fetchCol();
  
  if (!empty($rids_to_disable)) {
    // Disable redirects
    redirect_change_status_multiple($rids_to_disable, 0);
  }

  // Update our progress information for the batch update.
  $sandbox['progress'] += $limit;

  // Indicate our current progress to the batch update system, if the update is
  // not yet complete.
  if ($sandbox['progress'] < $sandbox['max']) {
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
  }
}

Double check the rewritten queries produce the same result, and set $limit really low for testing, like 2 or 3, etc.

cmseasy’s picture

@Alan D
I'm not sure but, maybe VBO https://www.drupal.org/project/views_bulk_operations can solve your issue.

I think #408 is a new feature request.
Is it not better to create a new issue for #408, so this issue 'Fix and prevent circular redirects' can close and the solution commited?

nicholasruunu’s picture

I am getting really angry now, so please excuse my ignorance and directness.

This module is from 2003 and is still a work in progress as a Drupal 7 module working with global redirect and whatever.

This issue is over 2 years old. This module is called redirect and is a valuable module for all of us.

People have asked for maintainer feedback, this thread is again over 2 years old. It's irresponsible to the Drupal community and most to all of us trying to bring this patch forward.

The ONLY reason this is not part of our build package is this bug. We've spent hundreds of dollars of customer money to try and get this fixed. WHICH IT ALREADY IS. It's just about semantics and edge cases at this point.

This is the actual time for a fork, if @stephanr, who's made a lot of great progress, or anyone else is up for it. I'll be in to help spending my free time on this. Drop me an inbox message.

I can't stress enough how disappointed I am, if you can't handle a project, please do the right thing and hand it over.

stefan.r’s picture

Status: Needs work » Needs review

To be fair, the maintainer has been chiming in with feedback every couple of weeks :)

The issue mentioned by @hass was more about the output than the actual queries. They are unexpensive so even with 10000 actual circular redirects (which I still don't see how anyone even on the largest scale sites would have that many), this shouldnt time out, should it?

rwilson0429’s picture

Thanks stefan.r for calming the waters. You've done a fabulous job on this issue. Thank you and everyone else who is working us through this issue. Excuse me for getting off the real issue here, 'Fix and Prevent Circular Redirects' but, I am driven to say that the maintainer of this module is a huge and consistent contributor to the Drupal community for over 8 years (I believe). That's top-notch in my book. Thanks maintainer. I appreciate his hard work and efforts on this module and the many others that he maintains and gives freely to the community.

Alan D.’s picture

We maintain a few sites that do have content in the millions, but imho, we would never have an issue ourselves with the update script. (99.98% of this content is automated from feeds etc that would never have multiple aliases.)

travelvc’s picture

I am tracking this thread daily hoping for for an "official" update. I have tested the latest patches and they work fine for me and solve the redirect loop bug I am experiencing.

As a suggestion - since the development release is already better than the production release, why don't we set a date (for example, March 1) for everyone here to test and verify the latest patch, and commit to a release date. This can't drag on forever.

DamienMcKenna’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

All of Dave Reid's feedback has been resolved, there are no further items indicating problems with the patch, so I'm setting this to RTBC.

mxwright’s picture

Patch in #397 works for me too, though I still have to use it in conjunction with #1198028-13: Redirects do not work if $_SERVER['SCRIPT_NAME'] does not follow base_path

Edit: Second patch due to Drupal being in a sub directory on my server.

azinck’s picture

Status: Reviewed & tested by the community » Needs work

I'm very sad to move this back to "needs work". #397 calls redirect_change_status_multiple() which calls redirect_save() which calls drupal_write_record() which, according to the docs, isn't allowed to be called via an update hook due to the potential for an unreliable schema cache. Practically speaking, in my testing my redirects get correctly disabled when running the update hooks via update.php but they don't get disabled (the update hook is run, and the correct # of items are indicated as being disabled by the text returned, but the status does not actually get correctly set) when called via drush updb. This is due to a stale schema cache.

We could switch to calling db_update directly, but then none of the proper redirect-related hooks like hook_redirect_update would get called. Instead, I'd suggest queuing the redirects that need to be changed and processing the queue during cron (hook_cron_queue_info) so that we can call redirect_save() in a "safe" environment.

azinck’s picture

Status: Needs work » Needs review
FileSize
24.11 KB

In thinking about this a bit more I actually think we should go ahead and use db_update() here. I don't think it's important to invoke the redirect-related hooks as we're populating the value of the status field for the very first time.

Since we're no longer calling redirect_save() or drupal_write_record(), we no longer need the registry_rebuild() or entity cache clear or even to clear the schema cache so I've simplified the update hooks back down to a single hook.

azinck’s picture

FileSize
3.34 KB

Forgot to attach the interdiff. This is the diff between #397 and #418.

stefan.r’s picture

The code itself looks good, just wondering what "conflicting" means in "circular or conflicting redirect"? Perhaps we could clarify that wording?

+ * Add status field to redirect table. Mark circular or conflicting redirects as disabled.

Needs to be under 80 characters. How about:
* Add status field and mark circular or conflicting redirects as disabled.

azinck’s picture

Ah, oops! I didn't mean to leave in those changes to the comments. Those are leftover from some changes I was considering to disable redirects that match url aliases but that goes beyond the scope of this issue. I can reroll the patch when I get home.

azinck’s picture

Fixed the comments. Interdiff is with 418.

stefan.r’s picture

Thanks, patch looks ready again.

stefan.r’s picture

Issue summary: View changes
cmseasy’s picture

Hi

@azinck @stefan.r
Thanks for the fast patch on the latest issue (#417). Testing it now, looks ok at first sight.

@all followers
You are one of the 219 followers, I think a few are disappointed at the duration this isue exists, I am. But I know I dont't have the skills to help with the code and maybe a lot of you don't have php coder skills either. The coders / maintainers do a great job. And I am sure that they want to commit the solution of this issue as fast as possible. I do.
So, become an active follower: test the patch and give your feedback. It can give a boost to the next release of redirect. I am sure: you want that, together with 218 other followers!

cmseasy’s picture

Patch #422 broke one of my sites compleet (white screen on all pages) and on other sites only the edit page of redirect was a white screen: and in the logs:
PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'status' in 'where clause': SELECT redirect.rid AS rid FROM {redirect} redirect WHERE (status = :db_condition_placeholder_0) AND( (source LIKE :db_condition_placeholder_1 ESCAPE '\\') OR (source = :db_condition_placeholder_2) )AND (language IN (:db_condition_placeholder_3, :db_condition_placeholder_4)) ; Array ( [:db_condition_placeholder_0] => 1 [:db_condition_placeholder_1] => frontpage [:db_condition_placeholder_2] => [:db_condition_placeholder_3] => nl [:db_condition_placeholder_4] => und ) in redirect_fetch_rids_by_path() (regel 575 van /home/public_html/drupal-7/sites/all/modules/redirect/redirect.module).

The answer was in the database: the column 'status' was missing, update.php didn't help. Only re-install solved it, loosing the 'old' redirect rules. That is not wat we want on a live site.

The schema version of redirect for both, the updated version and the re-installed version, is 7100. My (live) sites with the old (not updated) redirect have schema version 7000.

Function redirect_update_7100() in redirect.install does nothing. Maybe missing a "create column 'status' " if not present in redirect.install?

nicholasruunu’s picture

@cmseasy, I'm guessing it's because #419 removed my cache_clear_all('schema', 'cache', TRUE);
I would expect update.php to clear all the cache itself after the batch, but it probably doesn't.

Is the problem fixed when clearing the cache?
Is this something we should do in the migration to prevent this or is the user expected to clear cache after db update?

cmseasy’s picture

I didn't know that clearing caches can create columns in the database :-)
The problem is that column 'status' does not exist in the database and update.php did not create this column because creating this column is not part of the redirect.install script. So the problem is not fixed with a cache clear.

Look at the redirect.install of redirect 7.x-1.0-rc1: there is no 'status' column created (both: hook_schema or redirect_update). The redirect.install in Redirect dev+patch#422 creates the status column with hook_schema not in an redirect_update. There are 12 columns created in 7.x-1.0-rc1, there are 13 columns in the patched version.

As far as I know, cache clear is defined in the update module and a proces of update.php (line 882?).

DamienMcKenna’s picture

@cmseasy: When you first applied the patch did update 7100 run?

cmseasy’s picture

When you first applied the patch did update 7100 run?

Yes, and it worked: the schema version of redirect changed from 7000 to 7100 in the database 'system' row-table.

There is no "add column status" script in function redirect_update_7100() (see redirect.install), so it does nothing with this column.
sorry there is.

stefan.r’s picture

@cmseasy you may want to do some further debugging, if redirect_update_7100() runs it should definitely create a "status" field on the redirect table...

azinck’s picture

cmseasy: I'm confused by you saying that the "Function redirect_update_7100() in redirect.install does nothing." Patch 422 does add the necessary column in that update hook. This is what it looks like after the patch:

/**
 * Add status field and disable redirects that could cause infinite loops.
 */
function redirect_update_7100() {
  $field = array(
    'type' => 'int',
    'unsigned' => TRUE,
    'not null' => TRUE,
    'default' => 1,
    'description' => 'Boolean indicating whether the redirect is enabled (visible to non-administrators).',
  );
  db_add_field('redirect', 'status', $field);
  db_drop_index('redirect', 'source_language');
  db_add_index('redirect', 'status_source_language', array(
    'status',
    'source',
    'language',
  ));

  $result = db_query(
    "SELECT r.rid, r.source, r.redirect
    FROM {redirect} r
      INNER JOIN {url_alias} u
        ON r.source = u.alias
        AND r.redirect = u.source
        AND r.language = u.language
        GROUP BY r.rid"
  );

  if ($result->rowCount() > 0) {
    $rids_to_disable = array();

    foreach ($result as $row) {
      $rids_to_disable[] = $row->rid;
    }

    // Disable redirects
    db_update('redirect')
      ->fields(array('status' => 0))
      ->condition('rid', $rids_to_disable)
      ->execute();

    $disabled_redirects_message = format_plural(count($rids_to_disable),
      '1 circular redirect causing infinite loop was disabled.',
      '@count circular redirects causing infinite loop were disabled.');

    return $disabled_redirects_message;
  }
  else {
    return t('No circular redirects were found that could cause infinite loops.');
  }
}

The part that makes this potentially tricky, and what I expect tripped you up, is that the dev version of this module does, in fact, define redirect_update_7100(). So if you install the dev version of this module and run update.php with that version before you patch with #422 then the redirect_update_7100() will never get called again and your database column won't get added.

Some tips for other folks looking to help test this:

  • You can't properly test these patches against a database that's already had a version of the patch in this issue run on it. You need to go back to a pre-patch database, before the status field was added.
  • If you've never installed a patch for this issue but are running a dev version of the module then you'll need to:
    1. Apply patch 422
    2. Manually set the schema_version for the redirect module to 7000
    3. Run update.php or drush updb

And yes, the schema cache is definitely cleared by update.php and updb when they're finished running. That explicit call was only needed before because drupal_write_record was being called to write to the status column in the same update script call as was creating the column. Since we're now using db_update we're no longer relying on the schema cache.

DamienMcKenna’s picture

FileSize
23.98 KB

@azinck: You should never repurpose an existing update script, you should always create a new one.

This new patch puts update_7100 back to the blank state it was previously, and moves the custom functionality into update_7101.

hass’s picture

We typically do not need to support DEV to DEV upgrades. As RC1 had no update 7100 we can change this function until next release.

azinck’s picture

Yes, exactly, what hass said. 7100 only existed in the dev version.

azinck’s picture

But maybe we need to make an exception here based on the sheer amount of time since a release of redirect and the likely relatively high percentage of folks using the dev version.

stefan.r’s picture

Well we can change 7100 but we must not necessarily. Maybe we can leave it up to the maintainer and stick to #433 for now as doing otherwise may trip up other reviewers?

hass’s picture

We could also switch to 7101 and remove 7100 as it is empty and does nothing. This way we support DEV to DEV upgrades what is good to do, but not a must.

stefan.r’s picture

FileSize
23.97 KB

OK let's do that. This patch removes update hook #7100.

DamienMcKenna’s picture

@hass / @stefan.r: Yeah, that works too :)

cmseasy’s picture

Thanks for the discussion and the result of that, I will test patch #439 updating against 7000 (alpha) and 7100 (dev).

cmseasy’s picture

Update against 7100 (patch422):

The following updates returned messages
redirect module
Update #7101

    Failed: DatabaseSchemaObjectExistsException: Cannot add field redirect.status: field already exists. in DatabaseSchema_mysql->addField() (regel 328 van /home/public_html/drupal-7/includes/database/mysql/schema.inc).
stefan.r’s picture

@cmeasy yes, that's to be expected. You can't run this patch against a previous patch...

heddn’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
3.36 KB

Here's an interdiff between the last RTBC (#397) and #439. Two small nits, not marking needs work, but good to pick up if we have to re-roll this again.

+++ b/redirect.install
@@ -264,10 +275,56 @@ function redirect_update_7101() {
+  $field = array(
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => 1,
+    'description' => 'Boolean indicating whether the redirect is enabled (visible to non-administrators).',
+  );
+  db_add_field('redirect', 'status', $field);

No need to duplicate code, call redirect_schema(), which will return the schema structure and use that as input to db_add_field().

+++ b/redirect.install
@@ -264,10 +275,56 @@ function redirect_update_7101(&$sandbox) {
+    db_update('redirect')
...
+    $disabled_redirects_message = format_plural(count($rids_to_disable),

The return from db_update is the count of records that are changed. Use it instead of calling count() on the $rids_to_disable array.

The $sandbox is now removed, this is OK since this is now using db_update and even on the slowest db installations, it should scale. This is ready for RTBC again.

hass’s picture

No need to duplicate code, call redirect_schema(), which will return the schema structure and use that as input to db_add_field().

No... *never* do this, please! If you change hook schema this will cause lots of troubles. Update hooks need to work in-depended or they may fail in future. Current patch is correct.

heddn’s picture

If hook schema changes in the future then repeating code is fine, in the future. Copy/paste into the hook_update at that point. But there isn't any need to repeat code, now. Coding for the future usually means additional complexity that is never utilized.

Regardless, now we are RTBC, let's get this thing committed!

azinck’s picture

Looks good to me. The rename to redirect_update_7101 is smart to help folks already on dev. Let's get this in.

hass’s picture

No, we write code once. It is absolutely correct to copy the field schema into the update function. If there comes hook 7201 and it changes the field again hook_schema will change and therefore 7101 will fail from this point. It is often missed that this happens. That is why project module 5.x and 6.x is seriously broken. You should make every update in-depended of every functions in the module or you run into troubles. Nobody will and like to test this upgrade paths in future properly. Do it once and do it right. Whenever possible do not depend on anything as it can and will change.

rooby’s picture

+1 for never directly calling the hook_schema() implementation from an update function.

cmseasy’s picture

Test results redirect with patch #439

I bumped against an url_alias condition. On admin/config/search/path/settings the Update action must manual set to: "Create a new alias. Delete the old alias". Redirect does not work without this setting.

The comment under Update actions "What should Pathauto do when updating an existing content item which already has an alias? The Redirect module settings affect whether a redirect is created when an alias is deleted." is not clear abouth this condition and not documented (far as I know).

Possible solution: change the checkbox text on the URL redirect settings page admin/config/search/redirect/settings:
"Automatically create redirects when URL aliases are changed."
to, for example:
"Automatically create redirects when URL aliases are changed. Set Update actions in url aliases on: Create a new alias. Delete the old alias!"

------

Test url alias with the update action options:

Update actions in url aliases set to Do nothing:
No new alias is made, the old one exists.
No redirect made.
Result: ok.

Update actions in url aliases set to Create a new alias. Leave the existing alias functioning. :
A new alias is created, next to the old alias.
No redirect is made.
Result: ok.

Update actions in url aliases set to Create a new alias. Delete the old alias.
A new alias is made, removing the old alias
A redirect is made.
Result ok, see remark above.

------

Testing url alias and redirect:

1) First title (node made):
url alias is made.
no redirect.
Result: ok.

2) Change title (url change):
A new alias is made, removing the old alias from (1).
A redirect is made from old (first title) alias to node/#.
Result: ok.

3) Change title back to 'first title' (url change):
A new alias is made, equals the 'first title' alias (1).
A redirect is made from old 'çhanged title' alias made in (2) to node/#.
The 'first title' redirect made in (2) changed status to Disabled.
Result: ok.

During the test I never seen an error or warning message.
My compliments for the developers.

stefan.r’s picture

@cmseasy good to hear your tests were successful, thanks!

candelas’s picture

@stefan.r isn't enough tested now to ask @Dave Reid to check and commit or say what to be changed?

It would be great to be able to go for other bugs that are in the cue :) Thanks all for your work.

stefan.r’s picture

Yes, I think it is. Keep bugging him so we can get this in! :)

candelas’s picture

@Dave Reid, first thanks for the thousands of hours that you have gave to the community :)

Could you, please, check this critical issue to be able to commit it? Thanks

candelas’s picture

@stefan.r and all the other :)

I was thinking how to make more attractive to the module maintainer to check this issue. What do you think if we check that it works with all the other patches tested and review by the community? There are 9.
https://www.drupal.org/project/issues/redirect?text=&status=14&prioritie...

This is the only classified as critical, so I would think that is the one that would have preference, but if we check, there will be more efficient to commit in one moment all that work well.

Next week I have to implement this module in a site that I am building, so I have time to check.

flyke’s picture

Patch #439 works for me.

Steps that were a prolem before:
- create and save a page with title 'Contact' (it auto generates alias '/contact')
- edit the page, change title to 'Contact us' (it auto generates alias '/contact-us' AND places a redirect from /contact to /contact-us)
- edit the page, change title to 'Contact' (it adds a redirect from /contact-us to /contact and thus infinite loop now)

With patch #439 -> problem solved.

travelvc’s picture

Patch #439 works for me! :)

1mundus’s picture

It seems that many people can confirm that the latest patch works. Please commit it, it's really critical for many of us. :)

cmseasy’s picture

On the main project page the maintainer Dave Reid is asking for a co-maintainer. I don't have the skills to maintain a module: who wants to help Dave committing this patch (maybe all 9 RVBC patches, see #457) to dev, rc# or even better to an official 7.x-1.0 release?

PQ’s picture

Just a thought but would there be any merit in appealing to the Drupal association to fund the maintainer's time to get this in? I appreciate that the D.A. more or less commit in advance to what they are going to do with members's membership fees, but as a paying individual member myself, I would want to see funds spent to protect and maintain Drupal's reputation.

Whilst redirect is not part of core or indeed on a stable release, it is the 45th most used project in the ecosystem (https://www.drupal.org/project/usage) which means that a large proportion of all Drupal sites have it installed. In my experience if no pre-emptive action is taken more than half of site owners flag up issues with redirects relating directly to this issue. Site owners then consider this to be a bug either with Drupal or the individuals / organisation that put their site together, and their faith in Drupal as a whole and/or their vendor takes a knock.

This is why I personally feel that this is highly critical that this gets resolved ASAP. It's not an edge-case... as-is a large proportion of Drupal sites are exhibiting behaviour that's broken and highly visible and disruptive.

I also understand that the Dave Reid is stretched thin and is actively seeking co-maintainers to help. He does a fantastic job within the community and has contributed a huge amount. I appreciate that he has a finite amount of time he can put into these issues especially on an unpaid basis, but in the absence of any co-maintainer, there is effectively a bottle-neck here where no-one else can get this fix committed. This is why I'm suggesting that a small amount of our D.A. membership fees goes towards sponsoring getting this resolved once and for all, for the entire community's sake.

Thoughts?

candelas’s picture

@PQ I agree completely with your arguments and your proposal. Which steps are needed to make what you propose?

PQ’s picture

@candelas, Not entirely sure to be honest, I guess I'd like to see if anyone else on the thread would support this course of action or can provide a reason why it's not a good way forward. If feedback is positive then I could put together an open email to the DA, flagging up this discussion.

Charles Belov’s picture

+1 for funding this issue. I don't feel comfortable implementing patches that have not been committed to at least dev, as it creates maintenance headaches.

ladybug_3777’s picture

WOW, I just read through this thread and I can't believe how long this issue has gone on! I just installed this module for the first time today and immediately saw the issue with the infinite loops. With all the talk back and forth and changes and upgrades etc I agree with Charles Belov that I don't feel comfortable applying a patch, to a dev version, on something I'd like to use on a production site. I can deal with installing a dev, but a patched dev on a module that is having trouble being maintained.... well that's a little different.

I would like to thank EVERYONE that has worked on this issue. This is a wonderful community effort, I just wish we could push it to the next level and get it into dev at the very least. I'm all for helping with that.

ladybug_3777’s picture

Is this issue, https://www.drupal.org/node/2395917, still an outstanding separate issue? I assume it is, but I saw fixes for workbench moderation were rolled into one of the patches, but then taken back out, and perhaps considered in again? I wasn't clear on it so I thought I'd ask the people that have worked closely with this fix. Thanks!

jimmynash’s picture

I added the patch in #439 to two of my sites and so far things have been fine.

I was running version 7.x-1.0-rc1 in both cases. The process was this:

  1. Update to 7.x-1.x-dev
  2. Patch module with patch from #439
  3. Run update.php

Testing consisted of:

  1. Create a page with title "Redirect Test
  2. Edit this new page and change title to "Redirect Test Changed"
  3. Edit the page again and change title back to "Redirect Test"

I also tried adding a manual alias in the pathauto settings for the node itself and manually adding a redirect to an existing one. In both of these cases everything handled as it should and no circular redirects were caused.

I have two active sites in production using this patch now. If I run across any issues I'll report back.

HUGE thanks to everyone for their work on this long standing issue.

capellic’s picture

Applied patch from latest dev (January 15, 2015) and it is working well!

betarobot’s picture

#439 worked pretty well for me at live project where owners are changing blog titles often and erratically, so that could lead to loops before.

candelas’s picture

Hello you all :)

I think we have to solve two problems:... by one side to find a maintainer, as @cmseasy points out, and another to get this patch to dev (one very interesting solution for this is the one that @PQ proposes).

Maintainer ... @stefan.r wouldn't you, please, adopt this project... ? :) as you see, there is not anybody with the knowledge to make it... for a while... to push to dev the https://www.drupal.org/project/issues/redirect?text=&status=14&prioritie...

@PQ how many people/time to start with the process that you propose?

Let's do it! :)

stefan.r’s picture

I'm currently not familiar enough with the redirect module to commit all those RTBC patches or to be fine with being a full-blown maintainer.

This patch however is widely tested, so if I were to be made "unofficial" maintainer I'd be OK to commit it, do a new release and deal with any issues that may come up as a consecuence :)

nicholasruunu’s picture

@stefan.r Why don't you step up as a co-maintaner and we just keep the searching for a co-maintainer label up?
Better we get something done than this just sitting and rot.

rcodina’s picture

+1 to comment #472

This issue is taking too much long.

candelas’s picture

+1 to comment #472 :)

Alan D.’s picture

@stefan.r
Even if you tackle this and #905914: Merge global redirect functions into Redirect module, you will have closed off the two biggest irritants of this otherwise amazing module and big thumbs up from 138,873 users :)

These are by far the two most commented issues

https://www.drupal.org/project/issues/redirect?text=&status=All&prioriti...

PQ’s picture

As per #461, I have sent the following open email to the Drupal Association:

Dear Drupal Association,

I was hoping to draw your attention to one particular issue on drupal.org, specifically “Fix and prevent circular redirects” (https://www.drupal.org/node/1796596) on the Redirect module issue queue.

To summarise, If a user changes a node’s path and then changes it back again on a site with redirect enabled, an infinite redirect loop is caused which breaks that page for anonymous visitors. This is compounded when Pathauto is configured as for example it could just require the administrator to change the node title and then change back again to cause the loop.

This is an major issue that has been active for nearly three years (although the underlying problem has probably existed for longer) and I feel that during that time it has been doing significant damage to Drupal’s and the community’s collective reputation.

Whilst redirect is not part of core or indeed on a stable release, it is the 45th most used project in the ecosystem (https://www.drupal.org/project/usage) which means that a large proportion of all Drupal sites have it installed. In my experience if no pre-emptive action is taken more than half of site owners flag up issues with redirects relating directly to this issue. Site owners then consider this to be a bug either with Drupal or the individuals / organisation that put their site together, and their faith in Drupal as a whole and/or their vendor takes a knock.

This is why I personally feel that this is highly critical that this gets resolved ASAP. It's not an edge-case... as-is a large proportion of Drupal sites are exhibiting behaviour that's broken and highly visible and disruptive.

Thanks to the sterling efforts of 130 contributors on the thread (most notably stefan.r who has been driving this forward over the last year), a patch has been created which has been marked a RTBC for over two months. Unfortunately, it has proven difficult to get this critical patch committed. The main issue would appear to be that the module maintainer Dave Reid does not have the availability to work on this at present.

This is by no means meant to be a criticism of Dave Reid. I understand that he is stretched thin and is actively seeking co-maintainers to help. He does a fantastic job within the community and has contributed a huge amount. I appreciate that he has a finite amount of time he can put into these issues especially on an unpaid basis, but in the absence of any co-maintainer, there is effectively a bottle-neck here where no-one else can get this fix committed.

This is why I'm contacting the D.A. on the behalf of the wider community to ask if it’s a possibility that the association could sponsor the maintainer’s time to get this commit in. I appreciate that the D.A. has to commit in advance to what they are going to do with members' membership fees, but as a paying individual member myself, I would want to see funds spent to protect and maintain Drupal's reputation.

I imagine this is not a line of action that the D.A. would normally take, but I hope you will consider making an exception in this case in order to help reduce any ongoing damage to Drupal’s reputation that this issue may cause.

Many thanks,
Paul Querol (PQ).

I'll update this thread with any response.

cmseasy’s picture

I support #476, thanks PQ

ciss’s picture

Related issues: +#2447731: Path_auto Patch
hass’s picture

@stefan.r: Have you field a issue to take over the project ownership or at least requested to become a co-maintainer?

stefan.r’s picture

@davereid is on parental leave, I caught him on IRC last week and he said he'd commit the issue when he could as it's been on his todo list for a while.

Reis Quarteu’s picture

Patch #249 works fine for me. Many thanks @stefan.r! :)

Dave Reid’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
21.74 KB
3.84 KB
3.62 KB

Reviewed this. Ended up being very confused at why the hook_redirect_enable|disable were left in, since we already have hook_redirect_update(), so I removed those, in addition to the entity cache clearing, since that's already handled by redirect_save() as well. Moved the watchdog calls to redirect_change_status_multiple() instead. Could we get some confirmation from some testers for this to be the final version?

Not going to comment on anything else in here that was said while I was on paternity leave with a new baby (and still am).

Status: Needs review » Needs work

The last submitted patch, 482: 1796596-prevent-circular-redirects-test-only.patch, failed testing.

Dave Reid’s picture

Status: Needs work » Needs review
FileSize
21.51 KB
1.84 KB

The update hook could have used some simplifications, I think the query was going to error on PostgreSQL due to not having all three fields included in the GROUP BY condition.

andresc75’s picture

Hello, This is my first post on drupal.org
I think there is a related issue you may have to consider when preventing infinite loops with redirect module on path alias update.

This is my case:
- A site with Path aliases created using PathAuto with "transliteration" option not checked resulting in Ex.:
http://[BaseURLSite]/articulos/biomedicalización-del-envejecimiento
or
http://[BaseURLSite]/articulos/biomedicalizaci%C3%B3n-del-envejecimiento
- Several "Page Not found" errors on my log regarding with not transliterated URLs
- Want to change aliases converting special chars for preventing 404 and for better readeable URLs.
- Use auto update option in redirect module to auto create Redirects on updated aliases.

To solve this:
- Enabled transliteration on PathAuto for better URL compatibility.
- Edit a node and enable auto generate for updating URL Alias with the new PathAuto config
This results in:
- A new redirect record with old (not transliterated) URL -> node/[NodeID]
- An updated alias on PathAuto with the new transliterated URL -> node/[NodeID]

Problem:
When I try access the updated node with any URL (node/id, transliterated or not transliterated) i get an infintite loop error message.

A workaround solution for me was changing PathAuto URL pattern for this content type: Ex adding a node taxonomy term chaging "articulos/[node:title]" to "articulos/[node:taxonomy-vocabulary-4:name]/[node:title]"
By doing this, the NOT and the transliterated URL has an extra difference not regarding only with an ASCII special Char.

Other "solution" was disabling "auto create redirects when an url changes" in Redirect config. This made both URLs accesible but will generate duplicate content (not SEO fiendly) in transilterated and NOT URLs. And do not solves my original problem in detecting many "page not found" errors with not transliterated URLs.

My guess is
- PathAuto is not detecting any difference between both URLs when accesing to node
- Redirect (or pathauto) detects the diff on pathauto update and adds a redirect record
- This causes a transliterated path alias URL and a not transliterated path Redirect URL being the same causing an infinite loop

I don't know if this is a Redirect Issue, a PathAuto issue, a database config, or all above.

This was tested on Dev and Production servers:
Dev; Ubuntu 14.10 // Apache/2.4.10 // Mysql Ver:14.14 Dis: 5.5.40
Prod: CentOS 7 // Apache 2.4.6 // MariaDB 5.5.41 Ver: 15.1
Drupal 7.36 // Redirect 7.x-1.0-rc1 (and +13-dev) // Pathauto 7.x-1.2

Sory for my English!

stefan.r’s picture

Those changes look good, however we are changing the update hook number to 7100 but a significant amount of people already run some version of this patch on production systems. Considering sometimes very basic questions come up, I'm quite sure a few of them may miss update hook #7101 if we start changing numbers around. Maybe we can add an empty 7100 update hook instead?

As to the implications of the actual changes:

1. The new query looks good, it doesn't have the GROUP BY anymore which should still be fine, it just means that the array of redirect IDs to update may have some duplicates, which should have no practical implications.

2. Removing hook_redirect_disable() and hook_redirect_disable() should be fine too, as we're already invoking presave/update hooks in redirect_save(), which is the only place where the disable/enable hooks were being invoked anyway.

The only implication is that we would miss the chance to react to a redirect being enabled/disabled (nor would anything appear in the log) if another module changes the status back to whatever it was in the presave/update hooks, but then it's not actually "truly" being enabled/disabled anyway, so I guess that's fine.

3. It seems the entity cache clear code was just copy-pasted from redirect_delete_multiple(), and I don't see anything happening that would require a cache clear between where that cache clear used to be and the existing cache clear in redirect_save(). So that's OK too.

4. Moving the watchdog messages to where they are now should be fine, however there is no more check that looks whether we are actually moving from disabled to enabled or viceversa, ie. it will still say "X has been disabled" even if X was already disabled, which is not a big deal either.

stefan.r’s picture

FileSize
575 bytes
21.67 KB
stefan.r’s picture

Ah, seems we already had the upgrade hook discussion over in comments #433 - #440, where we suggested to either remove 7100 and start with 7101, or to add an empty 7100 update hook.

Dave Reid’s picture

FileSize
21.62 KB
1.57 KB

Ok I see what happened with the update hook. Let's just leave the existing one alone. It was added for a reason and we don't just get rid of empty update hooks because we feel like it. That added unnecessary confusion.

This also makes redirect_change_status_multiple() a no-op for redirects that already match the status parameter, and make the {redirect}.status column a tiny int column instead of unsigned, which is unnecessary for the data it stores.

stefan.r’s picture

I'm not sure that the original update hook actually triggered a registry rebuild. I think that happens after a drush updatedb and not after every individual update hook? So the description may be misleading and I also don't remember why a registry rebuild & entity info refresh would be required. I think that was about an error with a new class from redirect dev not being found. Not really in scope of this issue but would still be good to fix.

Overall the patch looks ready to me now, maybe it just needs one or two further reviews now?

@Dave Reid can we please do a new release after this patch goes in? For any post-release followup specific to to this issue, I'll be happy to keep on top of this :)

kylebrowning’s picture

Not that I've been involved a lot in this ticket, but happy to co-maintain if needed.

patch #488 worked for me.

stefan.r’s picture

FileSize
5.73 KB

@kylebrowning (and others) please can you do a review of this interdiff between Dave Reid's latest patch (#489) and the latest RTBC patch (#439?)

Would also be good if anyone could do some manual testing.

stefan.r’s picture

stefan.r’s picture

FileSize
22.85 KB
653 bytes

Tested this manually.

Dave Reid's changes all were OK.

I did find a few other issues though:

1. Bulk operations in admin/config/search/redirect were broken, because of this:

    if (isset($operation['callback arguments'])) {
      $args = array_merge(array($rids), $operation['callback arguments']);
    }
    call_user_func_array($function, $args);
// before:
$rids = array(array(1,2,3));
$operation['callback arguments'] = 0;

// after:
$rids = array(1,2,3);
$operation['callback arguments'] = array(0);

2. The watchdog message was in plural if there was only 1 redirect.

3. The update hook combines two actions that shouldn't be combined. If we fail in the middle of this update hook, we can't re-run it because the field has already been added.

4. Added comment about no-op behavior when there's no change of status. Maybe people do write code that triggers on hook_save and wonder why it isn't running when a redirect goes from disabled to disabled.

stefan.r’s picture

FileSize
22.85 KB
571 bytes

And the thing in this interdiff shouldn't have been changed in that previous patch.

stefan.r’s picture

Reviewed the older comments and remembered what the empty update hook was about. Some other issue introduced that, on the assumption that it would trigger a registry rebuild. However, it doesn't, that only happens at the end of an update run. So we had changed it to:

 /**
- * Empty update to trigger registry and entity info rebuild.
+ * Registry and entity info rebuild.
 */
function redirect_update_7100() {
-  // Do nothing.
+  // This is needed because RedirectController has moved to
+  // redirect.controller.inc, and is used in redirect_update_7102.
+  registry_rebuild();
+  entity_info_cache_clear();
}

However, in #418 someone took it out, saying:

Since we're no longer calling redirect_save() or drupal_write_record(), we no longer need the registry_rebuild() or entity cache clear or even to clear the schema cache so I've simplified the update hooks back down to a single hook.

But we *did* call that, when we change the status of redirects in update hook #7102. So that should've stayed in there at that point.

stefan.r’s picture

FileSize
967 bytes
23.2 KB
3.43 KB

So I looked into this and in update hook 7102 we changed from redirect_save() to a database query directly, and we only call the entity controller anymore to do a reset of the redirect static entity cache.

I think the conservative thing to do here is to remove that call to the entity controller (as we may not know where its class is located yet before having done a registry rebuild), and just deal with the fact that the static entity cache may not know yet about the changed redirect status -- for now this has 0 practicaly implications.

However, the issue of the missing registry rebuild / entity info rebuild remains. But as the main priority here is getting this patch committed, and the lack of this doesn't currently break anything, I don't think we should hastily re-add these and potentially introduce further issues. Doing cache clears in the middle of an upgrade is always potentially dangerous (ie. when distribution upgrades change multiple modules at once).

The thing to do for now may be to comment this in code and add a @todo (which is what this patch does). Also filing a followup for the broken update hook #7100, which should be out of scope as far as this issue is concerned: #2485459: Update hook 7100 does not do a registry rebuild or entity info cache clear.

Attaching an interdiff with Dave's latest patch for his review.

stefan.r’s picture

FileSize
23.21 KB
630 bytes

80 cols fix

heddn’s picture

Status: Needs review » Needs work
  1. +++ b/redirect.admin.inc
    @@ -264,9 +264,14 @@
    +    watchdog('redirect', $message);
    

    Variables should be passed to watchdog, rather than concatenating them into the message.

heddn’s picture

+++ b/redirect.module
@@ -961,6 +961,9 @@
+        // We no-op if the redirect is not actually changing status.
+        // So if a disabled redirect is disabled, nor redirect_save() is
+        // triggered, nor do we log any message.

This should read:

So if a disabled redirect is disabled, no redirect_save() is...

stefan.r’s picture

Status: Needs work » Needs review
FileSize
23.28 KB
1.31 KB
heddn’s picture

Status: Needs review » Needs work
  1. +++ b/redirect.admin.inc
    @@ -524,10 +541,12 @@ function redirect_edit_form_validate($form, &$form_state) {
    +    // Find out if any (disabled or enabled) redirect with this source already exists.
    

    Nit: comment line length.

  2. +++ b/redirect.install
    @@ -264,10 +275,57 @@ function redirect_update_7000(&$sandbox) {
    +    $disabled_redirects_message = format_plural(count($rids),
    +      '1 circular redirect causing infinite loop was disabled.',
    +      '@count circular redirects causing infinite loop were disabled.');
    

    This was dropped from #444. The count is returned by the db_update. Use it instead.

  3. +++ b/redirect.module
    @@ -687,7 +728,7 @@ function redirect_validate($redirect, $form, &$form_state) {
    +      form_set_error('source', t('A redirect already exists for the source path %source. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => redirect_url($redirect->source, $redirect->source_options), '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid))));
    

    Build the URL for the edit page using url() and insert using variable substitution.

heddn’s picture

Issue summary: View changes

Updated issue summary with suggested commit message.

#503.3 can be safely ignored. It was already using the alternate method. Out of scope of fixing in this issue.

stefan.r’s picture

Status: Needs work » Needs review
FileSize
22.97 KB
3.04 KB

Fixed 1 and 2. Leaving 3 alone as it's merely inconvenient for translators, not an actual problem. We don't want to introduce new bugs and the l() documentation on drupal.org also uses t('<a href="@link">link<a/>') as an example.

Reverted changes to that watchdog call from #500 as it's out of scope for this patch. Formatting singular/plural strings right into watchdog isn't something that can easily be done, so it was the way it was for a reason. Ideally it would have said "redirect(s)", but that's introducing a new translatable string.

Instead, I fixed the watchdog call that _this_ patch introduced :)

stefan.r’s picture

FileSize
23.23 KB
1.19 KB
stefan.r’s picture

FileSize
1.19 KB

That was one last change I discussed with @heddn, we thought this should be stricter.

that previous interdiff was faulty, attached one is the correct one.

Status: Needs review » Needs work

The last submitted patch, 506: 1796596-504.patch, failed testing.

heddn queued 506: 1796596-504.patch for re-testing.

The last submitted patch, 506: 1796596-504.patch, failed testing.

heddn’s picture

This should be a fairly easy thing to troubleshoot. Follow https://www.drupal.org/node/30011, and add breakpoints in redirect_save() and redirect_change_status_multiple().

stefan.r’s picture

Status: Needs work » Needs review
FileSize
23.35 KB
1.61 KB

@heddn I looked into this and we may want to stick with the weak typing and allow status to be either an integer or a string. I think it's a string on the redirect object for instance.

stefan.r’s picture

FileSize
5.9 KB

Interdiff with Dave Reid's patch in #489

rroblik’s picture

Can we expect a commit in dev version ?

Thanks

stefan.r’s picture

Yes, at some point, but we probably first need to get this back to RTBC. Any further reviews will help toward getting this committed :)

stefan.r’s picture

FileSize
23.35 KB
564 bytes
klokie’s picture

Patch in #516 works for me (on a multilingual site). Would be great to get this RTBC as it affects all of my projects!

cmseasy’s picture

Patch #516 tested in a development environment.
During the test I never seen an error or warning message.
Overall test results: ok.

1) First title (node made):
url alias is made.
no redirect.
Result: ok.

2) Change title (url change):
A new alias is made, removing the old alias from (1).
A redirect is made from old (first title) alias to node/#.
Result: ok.

3) Change title back to 'first title' (url change):
A new alias is made, equals the 'first title' alias (1).
A redirect is made from old 'çhanged title' alias made in (2) to node/#.
The 'first title' redirect made in (2) changed status to Disabled.
Result: ok.

bdanin’s picture

Here is my testing on patch #516:

Update from 7.x-1.0-rc1 to 7.x-1.x-dev, and ran update.php
Then apply patch #516, run update.php

Removed infinite redirect (I only had one, and it also worked in an instance with 0), and everything appears to work.

The issue I've found is that if I take the latest 7.x-1.x-dev, apply patch from #516, and then push the code to the server, I get locked out of the site and there is an error screen. It's the same error screen as if you just apply #516, but the difference is that I cannot sign in and run update.php

The concern here is that if this gets merged into dev, then it will be really hard to update from 1.0-rc1 to dev on a live site.

Everything else seems to be working well though.

stefan.r’s picture

The issue I've found is that if waI take the latest 7.x-1.x-dev, apply patch from #516, and then push the code to the server, I get locked out of the site and there is an error screen. It's the same error screen as if you just apply #516, but the difference is that I cannot sign in and run update.php

The concern here is that if this gets merged into dev, then it will be really hard to update from 1.0-rc1 to dev on a live site.

It is unclear what you mean here, could you clarify?

Case 1: 1.0-rc1 -> 1.x-dev -> update.php -> patch -> update.php -> all is OK?
Case 2: 1.0-rc1 -> 1.x-dev+patch -> update.php -> gives an error?

Is there any way we can reproduce this on a new drupal install? Which error screen do you get when you go to update.php? Is the problem that you cannot login before running update.php? What if you are already logged in before pushing the new code, can you run update.php then?

bdanin’s picture

Let me try a more structured version of testing this (hoping I have time), I think the error is
PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'status' in 'where clause': SELECT redirect.rid AS rid FROM {redirect} redirect WHERE (status = :db_condition_placeholder_0) AND( (source LIKE :db_condition_placeholder_1 ESCAPE '\\') OR (source = :db_condition_placeholder_2) )AND (language IN (:db_condition_placeholder_3, :db_condition_placeholder_4)) ; Array ( [:db_condition_placeholder_0] => 1 [:db_condition_placeholder_1] => node/631 [:db_condition_placeholder_2] => [:db_condition_placeholder_3] => en [:db_condition_placeholder_4] => und ) in redirect_fetch_rids_by_path() (line 575 of /sites/all/modules/contrib/redirect/redirect.module).

stefan.r’s picture

Are you sure you get that error in update.php? That seems odd...

Wenever we're about to install a new version of a module we should assume our site may be broken until any update hooks have run, see also https://www.drupal.org/node/250790.

So steps would usually be:

put site in maintenance mode -> install new code -> run update hooks

So if what you were trying to do was:

install new code -> log in -> run update hooks

That may have need to be:

log in -> install new code -> run update hooks

bdanin’s picture

Yes, I agree, you must sign into the site before adding the updated code.

when trying to sign into the site at /user, here is the problem I encountered:

Case 1: 1.0-rc1 -> 1.x-dev -> update.php -> patch -> update.php -> all is OK
Case 2: 1.0-rc1 -> 1.x-dev+patch -> update.php -> if already signed-in, then you can run update.php

I'll note that going from 1.0-rc1 -> 1.x-dev => you can still sign into the site, and the site itself works but 1.0-rc1 -> 1.x-dev+patch => broken site with the error I posted above until updates are run (the error I posted was not from update.php but the error that breaks the site).

Overall, this isn't a major problem because like you said, you should already be signed into the site with maintenance mode turned on before adding new code to the server, and then you can go from 1.0-rc1 -> 1.x-dev+patch -> update.php, and I've now tested that this does work as it should for me.

klokie’s picture

Status: Needs review » Reviewed & tested by the community

In case it helps anyone, here's how I run the update from the command line (after already having tested it on a development site per #518, and also backing up the database). This worked successfully for 3 sites so far, taking less than 1 minute to run.

git fetch                                # check out "redirect" update
git diff origin/master                   # verify code update
drush variable-set maintenance_mode true # put site into maintenance mode
git merge origin/master                  # merge in the update
drush cc all && drush updb               # update the database 
drush variable-delete maintenance_mode   # take site out of maintenance mode

Thanks for the patch. Marking as RTBC, hope that's ok. Cheers!

stefan.r’s picture

#523: I think we can live with that, maybe put a note in the rc2 release notes just in case.

The update.php script has clear instructions re what we can do other than logging in -- we don't /have/ to be logged in to do an update.

Thanks klokie for the review.

valentin schmid’s picture

We're just testing patch #516 together with patch #195 from https://www.drupal.org/node/905914.
No problem so far. Just works great.
Thank you.

bdanin’s picture

#525, that would be great. Having this out there as an rc2 release would be very helpful, so I definitely support RTBC.

Just a quick note that a lot of production machines won't necessarily have drush, but I was still able to test upgrading to this patched version several times successfully without using drush.

GoZ’s picture

1/ Create node : title1 (node/1)
2/ Rename to title2 : create redirect title1 -> node/1 enable
3/ Create node : title1 (node/2) : disable redirect title1->node/1. node/2 is available at /title1
4/ Edit node/1 and enable redirect title1->node/1. title1 redirect now to node/1. node/2 is now not available with title1 path.
From data for this redirect is now node/2->node/1.

No message is displayed and it's possible to enable this previous redirect.
Are you agree with that ?

Another thing, we keep disabled redirect with same path. This could be deleted instead of being disabled.
1/ Create node : title1 (node/1)
2/ Rename to title2 : create redirect title1 -> node/1 enable
2/ Rename to title1 : create redirect title2 -> node/1 enable. Disable title1->node/1
I agree to keep disabled redirect when it could be useful, but in this case, it's not.

stefan.r’s picture

The first half of that comment I don't understand. How should this be fixed / what kind of message should be displayed in your opinion?

As to the second half, I think it's fine to keep the old redirect and disable it, just for consistency or in case it was manually created.

GoZ’s picture

@stefan.r Following first half steps, you can enable a redirect which make another node unavailable. I think we should not allow a redirect to be enable if another node has same path or at least display a message like "While redirect xxx is enabled, node 2 will not be accessible."

stefan.r’s picture

@GoZ just to reiterate what you were trying to say:

  1. Create a node (node/1) with alias 'cats'
  2. Rename the alias of this node to 'dogs'. A redirect will be automatically created ('cats' -> node/1)
  3. Create a node (node/2) with alias 'cats'. This will disable the previous redirect ('cats' -> node/1).
  4. If we now re-enable the recently disabled redirect, node/2 becomes inaccessible. This is because the meaning of the redirect changes ('cats' now stands for node/2 so the redirect 'cats' -> node/1 becomes node/2 -> node/1).

    It's logical for node/2 to be inaccessible though, as there's now a redirect away from it.

So while this may be odd behavior, in a way it's expected.

As to your suggestions, I'm not sure disallowing the enabling of that redirects that have the alias of an existing node in the "from" field is the right solution. We may restrict legitimate use cases there, and I'm worried about the complexity it would add to this patch.

I agree we may want to warn users with an error message when there's possibly odd behavior, but the problem with the error message you suggest is that it only seems to add to the confusion ("While redirect node/2 -> node/1 is enabled, node 2 will not be accessible.")

Maybe this should be a non-critical followup?

heddn’s picture

All the feedback from the issue summary is addressed and a review of the feedback back through the past few hundred comments. So I'll +1 on the RTBC.

GoZ’s picture

@stefan.r I agree with you this is not critical and could be improved in another issue. So this one could be commited.
+1 for RTBC (Yeah!)

hanoii’s picture

A little bit (completly) off-topic, but wanted to share an offspring of this issue.

Keeping up with it was driving me crazy drupal.org-navigation-wise, so I contributed this for dreditor:

https://github.com/dreditor/dreditor/pull/255

Leeteq’s picture

Agreeing with Stefan.r and others here, it would be very practical if this was released as RC2 immediately after -dev is updated, so we have a clear before/after point.

Dustin@PI’s picture

@stefan.r says in comment 531 "So while this may be odd behavior, in a way it's expected." ... I agree It is slightly odd behavior but it's useful, one of my sites relies on it. Having a warning message in that scenario is useful, but I feel that should be a separate feature request. I will not get to try the patch until tomorrow, but don't let that stop the RTBC from being switched to fixed.

razgriz.one’s picture

I have applied patch from #516.

Tested:
1) Old posts with infinite loop warnings before update
Result: After updating, infinite loop was removed by disabling URLs responsible for infinite loops
2) Added new content after update, then renamed content to create infinite loop
Result: Patch prevented redirect from creating infinite loop by disabling URL redirects by default

As far as I can tell, the patch is working acceptably.

blacklabel_tom’s picture

Hi,

I applied the patch in #516 and it working as expected.

Cheers

Tom

Elijah Lynn’s picture

LGTFM!

Dustin@PI’s picture

I have tested on SimplyTest.me on a fresh install, and one of my teammates tested on our the dev version of our complicated site and both worked well.

Any idea when there might be a new Beta (or even a dev release)?

hanoii’s picture

Only a quick bump/reminder not to lose the momentum that the issue got lately, it seemed close to be accepted.

JackProbst’s picture

I've tested and applied the patch in #516. It works as intended. +1 ready to be committed.

stefan.r’s picture

Yes this has been RTBC long enough now, let's get this in.

I think only @Dave Reid can commit this, he recently became a dad so it may take a while still :)

  • Dave Reid committed eb95006 on 7.x-1.x authored by stefan.r
    Issue #1796596 by stefan.r, Eric_A, Steven Jones, Dave Reid,...
Dave Reid’s picture

Status: Reviewed & tested by the community » Fixed

Committed #516 to 7.x-1.x. Thanks everyone for your patience and diligence. I'm evaluating adding a co-maintainer this week so situations like this do not happen in the future.

stefan.r’s picture

Great stuff! Will a new release be tagged soon?

/edit: Looks like yes per #2505299: Is it time to publish another release?, thanks!

Dave Reid’s picture

Yes, prepping a RC2 release for today after reviewing the issue queue. Next release targeted is 1.0.

hanoii’s picture

I wonder if @stefan.r would like to be one? He has proven to be the key promoter (and patcher) of this issue and I expect him to be very familiar with the module, but not sure if he has the time or anything else might prevent him to properly be one.

ar-jan’s picture

Thanks Dave Reid, stefan.r and everyone else who worked on fixing this!

John Bickar’s picture

+1. Bravo, everyone. I have been holding off on updating the Redirect module on over 1,200 D7 sites due to this issue. Now I can update.

Thank you!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.