Problem/Motivation

In some cases, there is a need for redirects supporting wildcards, so we can achieve some scenarios that could be hard to get to non-programmers without them. For a programmer, it could be easy to create a route with a parameter and do the magic inside a custom module, but editors cannot follow that path, they need a user-friendly tool for doing such things.

Making redirect module supporting wildcards is a great way of achieving it.

Proposed resolution

Make Redirect module support wildcards:

  • Admins will be able to add a redirect with an asterisk (*) in it, that could be replaced by any string. For example: /user/* will match for /user/1, /user/2, /user/whathever ... and so on
  • Admins will be able to put a wilcard also in the destination path, which will be replaced by the part of the source that matched the wildcard. For example: a redirect from /user/* to /user/profile/* will redirect /user/1 to /user/profile/1

Performance impact

The developed solution has a performance impact of o(n), with n as the number of wildcard redirects configured (i.e. those that have a * in its source path)

It has been tested within a demo site, using a simple controller in a custom module, and the Apache Bench tool doing 500 request, one at a time.

  • With wildcard support disabled, the demo site serves a request each 12.796 ms.
  • With wildcard enabled and 1000 wildcard redirects, the demo site serves a request each 15.708 ms.
  • More measures have been made to ensure the impact is in linear correlation with the number of wildcard redirects, so we have an impact of about 3ms for each 1000 wildcard redirects.
  • The number of non-wildcard redirects does NOT impact the performance.

Profiling
There could be a little more impact if redirects have a querystring, as it has to be unserialized, but that has not been measured.

Remaining tasks

  • [X] - Support for wildcards in both source and destination
  • [X] - Make the behavior configurable (turn on / turn off)
  • [X] - Profile the solution, as it has a performance impact.
  • [X] - Add tests

User interface changes

A new configuration option is added on Redirect config page:

Configuration before:
Configuration before

Configuration after:
Configuration after

Add redirect form before:
Add redirect form before

Add redirect form after:
Add redirect form after

Original report by @awasson

I would like to make a request for support for wildcard redirects.

For instance if I have a lot of requests for a legacy Drupal 6 path like /members/calendar/2014-03 and I would like to redirect them to the Drupal 8 calendar which has an entirely different path like /events/calendar-of-events/2014-03

Ideally, I could use tokens so that the actual date part is useful but at this point, I'd settle for just redirecting them to a static path like: /events/calendar-of-events/month

The From field would be: /members/calendar/*
The to field would be: /events/calendar-of-events/month

Thanks.

CommentFileSizeAuthor
#160 support_for_wildcards-2831605-160.patch10.57 KBo_timoshchuk
#154 support_for_wildcards-2831605-154.patch11.42 KBruslan piskarov
#150 support_for_wildcards-2831605-150.patch17.59 KBbernardopaulino
#147 2831605-profiling.png17.86 KBvidorado
#147 2831605-add-after.png44.54 KBvidorado
#147 2831605-add-before.png37.71 KBvidorado
#147 2831605-after.png86.47 KBvidorado
#147 2831605-before.png75.3 KBvidorado
#138 support-for-wildcard-2831605-138.patch30.41 KBchizh273
#130 support-for-wildcard-2831605-130.patch12.17 KBvidorado
#129 support-for-wildcard-2831605-129.patch12.45 KBceithamer728
#123 patchdiff-114_123.txt2.41 KBjeffschuler
#123 support-for-wildcard-2831605-123.patch12.82 KBjeffschuler
#120 support-for-wildcard-2831605-120.patch11.74 KBsidgrafix
#114 support-for-wildcard-2831605-113.patch12.94 KBricardofaria
#104 support-for-wildcard-2831605-104.patch5.75 KBvidorado
#103 support-for-wildcard-2831605-103.patch5.9 KBvidorado
#101 support-for-wildcard-2831605-101.patch5.55 KBvidorado
#101 support-for-wildcard-2831605-101.patch5.55 KBvidorado
#99 support-for-wildcard-2831605-99.patch5.47 KBvidorado
#98 support-for-wildcard-2831605-98.patch3.94 KBvidorado
#97 support-for-wildcard-2831605-97.patch5.29 KBvidorado
#95 interdiff_83_95.txt691 bytesvidorado
#95 support-for-wildcard-2831605-95.patch5.28 KBvidorado
#83 support-for-wildcard-2831605-83.patch5.28 KBfabsgugu
#82 support-for-wildcard-2831605-82.patch5.17 KBfabsgugu
#81 support-for-wildcard-2831605-81.patch5.33 KBfabsgugu
#79 support-for-wildcard-2831605-79.patch5.32 KBzeip
#77 interdiff_72_77.txt2.15 KBzeip
#77 support-for-wildcard-2831605-77.patch5.32 KBzeip
#75 interdiff_72_74.txt865 byteszeip
#75 support-for-wildcard-2831605-74.patch4.93 KBzeip
#73 interdiff_72_73.txt1.31 KBzeip
#73 support-for-wildcard-2831605-73.patch5 KBzeip
#72 interdiff_66_72.txt613 byteszeip
#72 support-for-wildcard-2831605-72.patch4.5 KBzeip
#68 interdiff_56_66.txt1.55 KBdevad
#66 support-for-wildcard-2831605-66.patch4.5 KBsahil432
#57 interdiff_53_56.txt1.54 KBdevad
#56 support-for-wildcards-2831605-56.patch3.95 KBdevad
#53 support-for-wildcards-2831605-53.patch4.12 KBdmitrii puiandaikin
#52 interdiff_48-52.txt900 bytesdmitrii puiandaikin
#52 support-for-wildcards-2831605-52.patch4.51 KBdmitrii puiandaikin
#48 support-for-wildcards-2831605-48.patch3.96 KBrahul.nahar001
#45 interdiff_40_44.txt1.45 KBrahul.nahar001
#44 support-for-wildcards-2831605-44.patch3.9 KBrahul.nahar001
#43 interdiff_42_43.txt734 bytesrahul.nahar001
#43 support-for-wildcards-2831605-43.patch3.95 KBrahul.nahar001
#42 interdiff_40_42.txt1.47 KBrahul.nahar001
#42 support-for-wildcards-2831605-42.patch3.92 KBrahul.nahar001
#40 interdiff_35_40.txt532 bytesrahul.nahar001
#40 support-for-wildcards-2831605-40.patch2.35 KBrahul.nahar001
#36 interdiff_31-35.txt1.19 KBrahul.nahar001
#36 support-for-wildcards-2831605-35.patch2.33 KBrahul.nahar001
#34 support-for-wildcards-2831605-34.patch2.35 KBrahul.nahar001
#34 interdiff_31-34.txt804 bytesrahul.nahar001
#31 redirect-n2831605-31.patch1.92 KBdamienmckenna
#31 redirect-n2831605-31.interdiff.txt1.9 KBdamienmckenna
#22 redirect-wildcard-support-2831605-22.patch1.84 KBahebrank
#19 redirect-wildcard-support-w-aliases-2831605-18.patch2.55 KBstevieegee
#17 redirect-wildcard-support-w-aliases-2831605.patch2.41 KBstevieegee
#16 redirect-wildcard-support-2831605.patch1.86 KBkenorb
#13 redirect-2831605-support-for-wildcard-redirect-13.patch3.33 KBAnonymous (not verified)
#11 support_for_wildcards-2831605-11.patch3.34 KBjose_guisado
#10 support_for_wildcards-2831605-7.patch3.34 KBomitsis
#6 support_for_wildcards-2831605-6.patch3.33 KBl0ke

Issue fork redirect-2831605

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

awasson created an issue. See original summary.

awasson’s picture

Issue summary: View changes
berdir’s picture

We could maybe add this based on #2833457: Allow redirect between domains, but it's impossible for normal redirects as they use a hash for quick idenficiation of matching redirects.

awasson’s picture

Thanks. I managed to work around the problem with RedirectMatch 301 rules in my .htaccess but was hoping to be able to do something within the Redirect module. It would be so much easier to manage the redirects in the module and updating core wouldn't affect the redirects since they wouldn't be in .htaccess.

My rules were something like:

RedirectMatch 301 /members/calendar/(.*)-(.*)-(.*) http://www.example.com/calendar-of-events/day/$1$2$3
RedirectMatch 301 /members/calendar/(.*)-W(.*) http://www.example.com/calendar-of-events/week/$1$2
RedirectMatch 301 /members/calendar/(.*)-(.*) http://www.example.com/calendar-of-events/month/$1$2  
markie’s picture

Can I get some clarification on this. Using the Redirect Domain UI, I want to use a wildcard on the same domain so

"example.com/path/to/all/* => example.com/new/path"

for example.com we get a redirect to www.example.com, does that matter in the table entry?

thanks for your help.

l0ke’s picture

StatusFileSize
new3.33 KB

While it is not the perfect way to handle wildcards, let's start with this.

Proposed resolution

The given patch adds suitable wildcards for given $source_path to $lookup_paths and then generates hash for each of them. Matching performed with this "extended" set of hashes.

Remaining tasks

More specific redirect should be selected over others. Example:

  • Two redirects exist from blog/* and from blog/abc.
  • In case user access path blog/abc second redirect should be selected as matching.

So we need some kind of specificity index to handle this properly.

didebru’s picture

#6 worked for me thanks!!

wim leers’s picture

Status: Active » Needs work
Issue tags: +needs profiling, +Performance

#6 has massive performance implications though. This will need profiling I think.

maaty388’s picture

#6 Works for me thanks

omitsis’s picture

StatusFileSize
new3.34 KB

#6 works for me too, but I need to edit to match it to the latest dev version.
Here's the patched patch

jose_guisado’s picture

Status: Needs work » Needs review
StatusFileSize
new3.34 KB

#6 fine for me but I need edit it to the current module version.

l0ke’s picture

Thanks everyone for re-rolls, I see that this feature gathers some interest.
@Wim Leers I agree, performance is a weak spot of this solution and I understood it while working on the patch. However I don't see better solution now.
I will think what can be done to improve it, and maybe move this feature to an experimental submodule.

Anonymous’s picture

Patch for latest dev version

laravz’s picture

D8.6 is not the latest dev version, I'll run the test for D8.7. Also, we might still need to figure something out for the performance issue.

+++ b/src/Plugin/Field/FieldFormatter/RedirectSourceFormatter.php
@@ -26,7 +26,7 @@ class RedirectSourceFormatter extends FormatterBase {
-        '#markup' => urldecode($item->getUrl()->toString()),
+        '#markup' => str_replace('/%2A', '/*', $item->getUrl()->toString()),

It's not clear to me why the urldecode has been removed (it was still there in the patch of #11). The urldecode was specifically added in a bug report in issue #2869055.

kenorb’s picture

I've tested the patch from #13, works for source like "/foo/*", but it seems it doesn't support multiple wildcard characters.
E.g. source from "/foo/*/bar/*".

Also, based on the logic, I think it doesn't support wildcards in the middle.

kenorb’s picture

StatusFileSize
new1.86 KB

Uploading patch written from scratch. It supports multiple wildcards by using PHP's fnmatch().
The performance should be less issue, as it only checks for wildcard patterns, when the original pattern was not find. When no redirects with wildcards are present in the redirect table, there won't be any checks.

stevieegee’s picture

Using patch from #16 I have added support for aliases. It was a quick fix for what I needed so any feedback would be appreciated.

Status: Needs review » Needs work

The last submitted patch, 17: redirect-wildcard-support-w-aliases-2831605.patch, failed testing. View results

stevieegee’s picture

Try that again. The reason for this patch is when combined with pathauto you can redirect all paths of a certain content type.

annetts62’s picture

I am trying to do a url redirect of https:\\sitename\pw\* to https:\\sitename\public-works\* so that url reqests such as https:\\sitename\pw\somepage will go to https:\\sitename\public-works\somepage.

Will this patch work for that?

mglaman’s picture

  1. +++ b/src/RedirectRepository.php
    @@ -64,6 +64,11 @@ class RedirectRepository {
    +	
    +    // If there is a path alias get it. ¶
    

    Whitespace.

  2. +++ b/src/RedirectRepository.php
    @@ -64,6 +64,11 @@ class RedirectRepository {
    +    $alias_source_path = \Drupal::service('path.alias_manager')->getAliasByPath('/' . $source_path);
    +    $alias_source_path = ltrim($alias_source_path, '/');
    
    @@ -80,6 +85,17 @@ class RedirectRepository {
    +      foreach ($patterns as $rule) {
    +        if (fnmatch($rule->pattern, $source_path) || fnmatch($rule->pattern, $alias_source_path)) {
    

    You're loading a service outside of dependency injection, and replacing it with the string value.

    The alias technically does not need to be loaded unless there are any patterns containing %*%

ahebrank’s picture

StatusFileSize
new1.84 KB

Reroll of #16 for 1.x / 1.4.

craneswalker’s picture

I have applied #22 and it works works

druplr’s picture

#22 works for me too. Thank you @ahebrank!

loisovervoorde’s picture

I got a redirect loop when trying to redirect all nodes in a particular language to one page using #11. I guess this is to be expected, but I was wondering if there could be protection against this?

jwwj’s picture

Got the same question as annetts62 in #20. It seems this patch only uses the wildcard for the source, not the destination? I want to redirect all routes under mydrupalinstance.com/catalogue/* to anotherdomain.com/catalogue/*, but if I try to add a redirect with

path:

/catalogue/*

and

to:

https://anotherdomain.com/catalogue/*

Upon redirect it doesn't replace the * in the "to" field with the corresponding value in the "path" field. Should this patch enable such a use case, or would I need some other patch/module to enable this use case?

ravipjoshi’s picture

I've applied #22, but as above, I can't seem to hand off the wildcard to the new domain... i.e. /blog/any-blog-title -> yadayada.ca/analysis/any-blog-title

Thank you!

kbeck303’s picture

I am running Drupal Core 8.7.11.

I have tested the patch in comment #22, I was able to apply the patch to both the 1.x-dev and 1.5, but when I saved the redirect the destination wildcard * is output as %2A.

This seems like it might be the result mentioned in comment #14.

klonos’s picture

I see that this feature is now being considered and being worked on for 8.x. Would it then make sense to re-open #2562455: Wildcard redirecting, and backport whatever solution come out of this into 7.x?

joewhitsitt’s picture

In testing #22, I see a single text field for source but the help text suggests a long text (Enter one per line). I imagine changing the field type would be a substantial change. For now, maybe the help text could be altered.

damienmckenna’s picture

Rerolled, wrapped the huge query statement, and I removed the "Enter one path per line" part of the description as it's a textfield not a textarea.

damienmckenna’s picture

Status: Needs work » Needs review
joewhitsitt’s picture

Thank you for updating the help text. #31 is working for me and the 100+ wildcard redirects we are about to go live with.

rahul.nahar001’s picture

StatusFileSize
new804 bytes
new2.35 KB

I have updated the patch #31 to provide the support for #26.

path:
/catalogue/*
to:
https://xzy.com/catalogue/*

Status: Needs review » Needs work

The last submitted patch, 34: support-for-wildcards-2831605-34.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

rahul.nahar001’s picture

StatusFileSize
new2.33 KB
new1.19 KB

Updated patch #34

rahul.nahar001’s picture

Status: Needs work » Needs review
biguzis’s picture

#36 doesn't do simple wildcard redirects from /something/* to <front>. Using #31 and works fine.

jcmartinez’s picture

#36 doesn't work with /user/* to /better-user-page

#31 does work.

The error I got with #36 is:

Notice: Undefined variable: wildcard_path in Drupal\redirect\RedirectRepository->findMatchingRedirect() (line 116 of /modules/contrib/redirect/src/RedirectRepository.php)
rahul.nahar001’s picture

StatusFileSize
new2.35 KB
new532 bytes

Fixed above mentioned scenarios. #38, #39

Below are tested scenarios:

/user/* to /better-user-page
/user/* to /better-user-page/*
/user/* to <front>
/user/* to https://www.example.com
/user/* to https://www.example.com/*
vijaycs85’s picture

Component: Miscellaneous » Code
Status: Needs review » Needs work
Issue tags: +Needs tests

Let's add some test coverage so that we can be sure not to break existing cases.

rahul.nahar001’s picture

StatusFileSize
new3.92 KB
new1.47 KB

Added test coverage for wildcards source and destination.

rahul.nahar001’s picture

StatusFileSize
new3.95 KB
new734 bytes

Updated test coverage for wildcards source and destination.

rahul.nahar001’s picture

StatusFileSize
new3.9 KB
rahul.nahar001’s picture

Status: Needs work » Needs review
StatusFileSize
new1.45 KB

Updated test coverage for wildcard's source and destination.

Chandru Ranganathan’s picture

Patch #44 worked for me.

Thanks.

dermario’s picture

Status: Needs review » Needs work

#44 doesn't work for me. Wildcard redirects to an (internal) entity are failing.

From: "jobs/*"
To: An entity on my system (107)

Fails with redirect to .../entity%3Anode/107

$ curl -Ik https://myhost.local/en/jobs/abcd
HTTP/2 302
server: openresty/1.15.8.2
date: Fri, 26 Jun 2020 13:05:38 GMT
content-type: text/html; charset=UTF-8
x-powered-by: PHP/7.3.11
x-redirect-id: 23
cache-control: must-revalidate, no-cache, private
location: https://myhost/entity%3Anode/107
rahul.nahar001’s picture

StatusFileSize
new3.96 KB

Please find an updated patch. Thanks

rahul.nahar001’s picture

Status: Needs work » Needs review
baptisten’s picture

#48 works for me!

r_h-l’s picture

#48 works great for me

dmitrii puiandaikin’s picture

StatusFileSize
new4.51 KB
new900 bytes

If absolute path has a file name with spaces, Redirect::isValid() return FALSE and "internal:/" added to url.

InvalidArgumentException: The internal path component 'https://stage.ccsf.edu/sites/default/files/2020/inline-media/Virtual Counter Icon.jpg' is external. You are not allowed to specify an external URL together with internal:/. in Drupal\Core\Url::fromInternalUri() (line 410 of /mnt/www/html/ccsfdev/docroot/core/lib/Drupal/Core/Url.php).

dmitrii puiandaikin’s picture

StatusFileSize
new4.12 KB
devad’s picture

Status: Needs review » Reviewed & tested by the community

Patch #53 works perfect for me. It applies cleanly to both 8.x-1.6 and latest dev.

I have tested it with D9.0.6. PHP7.3

It works not only with user/* format of paths but with more specific as well like: user/abc*.

I like it. This is a huge step forward.

Thank you @letjeng and all the others here.

No more need for D8/9 port of Match Redirect module which we used for similar D7 redirects. We just need commit here... od course.

Marking RTBC.

devad’s picture

I have tested patch #53 a bit further and wildcards work nice with ignore list also.

We can prevent all common /wp-**** attempts from entering ever more our "Fix 404 pages" table with single ignore rule:

/wp-*

What a relief. :)

However, "?" wildcard (single character wildcard) is not working and this patch has no code for it to work.

Removing "?" wildcard from patch is probably the best option for now... unless somebody would like to code "?" wildcard to work... with tests for it included.

"?" is rarely used in comparison to "*", so there is no big harm in removing it for now... I suppose.

devad’s picture

Title: Support for wildcards » Support for * wildcard
Status: Reviewed & tested by the community » Needs review
StatusFileSize
new3.95 KB

Traces of "?" wildcard removed from #53 patch.

We can add support for "?" wildcard later if there will be a feature request for it.

devad’s picture

Interdiff.

Sorry for temporary title change... I just realized that there is no real need for that.

jhnnsbstnbch’s picture

#53 works for me.

One question please:

Right now, with wildcards, I can redirect this

/aggregator/categories/1?page=25

to this:

/music-news/1?page=25

My question: how do I eliminate the extraneous /1?page=25 from the new path so that it is just

/music-news

Thank you.

devad’s picture

Status: Needs review » Reviewed & tested by the community

Switching back to RTBC as per #58.

jhnnsbstnbch’s picture

Something is not working... not sure if it is operator error or not.

I want this path

/taxonomy/term/*/feed

To redirect to the front page.

It will not work, no matter what I try.

Any clues? Thank you.

devad’s picture

Re: 60

The patch supports only wildcards at the end of the path.

jhnnsbstnbch’s picture

Ah, ok, thank you!

Any idea how to fix #58?

I am not a coder. Thanks for your help. Regards.

devad’s picture

Re: #62

You can try to deselect the "Retain query string through redirect." checkbox in redirect settings:

/admin/config/search/redirect/settings

However, it is a global option and it will affect all of your redirects.

jhnnsbstnbch’s picture

Thanks very much, greatly appreciated.

sahil432’s picture

Status: Reviewed & tested by the community » Needs work

@devad

Reopening this ticket as this patch is not working correctly with multilingual approach

Scenario:-

First Redirect :-
Path: /en/article/*
To :- /news

Second Redirect:-
Path: /af/article/*
To :- /about-us

if you try to access "en/article/123" it will redirect to en/news and if you access "af/article/123" than it still redirect to "af/news" which is wrong it should redirect to af/about-us.

sahil432’s picture

StatusFileSize
new4.5 KB

Updating patch based on above scenerios.

sahil432’s picture

Status: Needs work » Needs review
devad’s picture

StatusFileSize
new1.55 KB

Adding interdiff for better review.

ac’s picture

#66 works nicely

tjtj’s picture

I moved my domain from /drupal into a subdomain, blogs in Drupal 9.0.
I want to redirect every page to my new subdomain.
I applied patch 66 and for example, when I access mysite/drupal/alias, I want to get blogs.mysite/alias, but this does not seem to work (error processing directive).
in the domain redirect page, I have mysite , /drupal/* and blogs.mysite/* as entries.
What am I doing wrong, or is this impossible?

does the old /drupal directory have to exist? What must be in it?

devad’s picture

Re: @tjtj

For this purpose better are .htaccess tweaks like:

https://ubiq.co/tech-blog/redirect-subfolder-subdomain/

zeip’s picture

StatusFileSize
new4.5 KB
new613 bytes

I stumbled upon a case where $wildcard_path was an empty string, which caused the wildcard not to be replaced. Attached is a new patch to fix this.

zeip’s picture

In my project we needed to change the wildcards so that a ”folder redirect” covers with a single redirect both the ”folder” and all paths under it, eg. example.org/test/* would redirect all of example.org/test, example.org/test/ and example.org/test/asdf (but not example.org/testitwell). I did this by changing the interpretation of /* to work as a ”folder wildcard” which also matches a path without the slash.

I'd suggest using this approach for the whole wildcard feature, but decided to make it a separate patch from the smaller bug fix in #72 in case you disagree. Attached is a patch and interdiff for the folder wildcards.

Status: Needs review » Needs work

The last submitted patch, 73: support-for-wildcard-2831605-73.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

zeip’s picture

StatusFileSize
new4.93 KB
new865 bytes

As the testing revealed, there was an extra change in the patch causing an extra slash. Here's a fixed patch version that should work and an interdiff bypassing the faulty version.

zeip’s picture

Status: Needs work » Needs review
zeip’s picture

StatusFileSize
new5.32 KB
new2.15 KB

Fixed one more bug in the folder feature and added a few more test cases.

Status: Needs review » Needs work

The last submitted patch, 77: support-for-wildcard-2831605-77.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

zeip’s picture

Status: Needs work » Needs review
StatusFileSize
new5.32 KB

The test output is a bit weird (actual vs. expected is contradictory per my logic), but here's a try to fix the expected test result.

Status: Needs review » Needs work

The last submitted patch, 79: support-for-wildcard-2831605-79.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

fabsgugu’s picture

StatusFileSize
new5.33 KB

Hello,
I think it is better to only consider children:

test or test/ should not match with test / *

fabsgugu’s picture

StatusFileSize
new5.17 KB

Patch without unused test ( for test and test/)

fabsgugu’s picture

StatusFileSize
new5.28 KB

Patches with bugfix.

Before (redirect for /b-path/* -> /* ) :
/b-path/a-path -> //a-path

now :
/b-path/a-path -> /a-path

fabsgugu’s picture

Status: Needs work » Needs review
andy-blum’s picture

#83 is working well for me.

brianperry’s picture

#83 is also working nicely for me.

r81d3r’s picture

#83 working for me except if url contains digits

fabsgugu’s picture

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

Status: Reviewed & tested by the community » Needs work
fabsgugu’s picture

Assigned: Unassigned » fabsgugu
fabsgugu’s picture

Assigned: fabsgugu » Unassigned
Status: Needs work » Needs review

r81d3r,
I tried with digits and saw no problems. Can you give me an example?

If not, can someone else confirm their problem?

mjmorley’s picture

I am not able to get this to work. I have tried to used it alongside patch #104 from this issue https://www.drupal.org/project/redirect/issues/2879648.

I have a path alias for content type x that changes the path to /x/[node:title]. But I want all these nodes to redirect to the same page /listing. So I tried setting a redirect for /x/* -> /listing and it isn't working. Is this a bug or a misconfiguration on my part perhaps?

toddses’s picture

#83 worked for me. I also tested a few scenarios with digits and had no issues.

trenttati’s picture

The patch from #83 applied cleanly, but it is not redirecting if the URL contains digits in my environment: PHP 7.4, Drupal 8.9.16, Redirect 8.x-1.6.

I tested by setting up a redirect for existing aliases, some of which contain digits, some of which do not. All without digits redirect as expected. Those with digits do not redirect. If I remove the digits from the URL (and thus point to a non-existing alias) the redirect works. Pattern in use is /oldalias*, example of not working URL is https://www.mysite.com/oldalias7ate9.

Caches cleared after patching with drush cr && drush cr.

vidorado’s picture

StatusFileSize
new5.28 KB
new691 bytes

It works ok for me, even with digits. I see code is using fnmatch() function, so it should not matter if there are digits or not... i haven't managed to reproduce that error.

Anyway, I think it makes sense to apply before the most specific rules, so i think the sorting must be done by redirect_source__path and not by redirect_source__query (maybe both, but not only by query) and ASCENDING.

Now works okay for me at all! ;)

devad’s picture

Regarding the performance concerns...

It would be nice to have the wildcard support turned off by default.

And we need a new "Wildcard support" checkbox inside Redirect module settings page - to be able to turn this feature on/off.

In that way, the default performance would stay the same, the backwards compatibility would be preserved in full, and users who need the feature could be warned in description below the checkbox that the performance could be an issue.

I don't see any other way for this nice feature to make it's way into the module officially.

vidorado’s picture

StatusFileSize
new5.29 KB

I agree with you with the performance concerns,

Meanwhile i'm posting a little fix in the redirect behavior when the chunk before the wildcard appears also after the wildcard.

with "invitations/*" => "new_invitations/*"

URL "/invitations/my_invitations" was being redirected to "/new_invitations/my_"

vidorado’s picture

StatusFileSize
new3.94 KB

A safer method to do it, sorry.

vidorado’s picture

StatusFileSize
new5.47 KB

Re-adding wildcard tests forgotten in the new patch.

CheckeredFlag’s picture

ZeiP brings up a good idea in #73 to reduce clutter such that /foo/bar and /foo/bar/* could be written as /foo/bar/*.

However, it is not safe to assume that /foo/bar is also to get redirected, as stated by Fabsgugu.

What if we were to support a specific format to declare this behavior explicitly? Therefore, I'd like to propose this syntax:

/foo/bar//*

This would include: /foo/bar, /foo/bar/, and /foo/bar/*,
but ignore: /foo/barred.

This provides a clear and concise way to "redirect a directory and all its children".

Thoughts?

vidorado’s picture

StatusFileSize
new5.55 KB
new5.55 KB

Updated patch from #99 to make pattern redirects case-insensitive

vidorado’s picture

Hidding file, uploaded twice by mistake

vidorado’s picture

StatusFileSize
new5.9 KB

Added support for querystring to wildcard redirects

vidorado’s picture

StatusFileSize
new5.75 KB

Corrected a mistake with querystrings comparison.

The request query string must match the querystring set in the rule, but it can have other querystring parameters

vidorado’s picture

Hidding old patch file

azinck’s picture

Status: Needs review » Needs work

There are a lot of divergent ideas going on here and the current set of tests does not adequately capture everything going on.

A couple of thoughts: I strongly agree with CheckeredFlag in #100 that we should not be interpreting /foo/bar/* as targeting both /foo/bar and /foo/bar/*. But I'm hesitant to adopt the proposed /foo/bar//* convention since I don't think that's a standard anywhere. In particular, in terms of getting to an MVP of this patch, I'd argue for keeping it simpler. A new convention can always be added later.

I'm also unclear as to what the expectations are around the behavior with querystrings. They're mentioned in 103 and 104, but there are no tests for them and no documentation on how they're meant to work.

Earlier in the thread there are mentions of issues with how this interacts with multilingual sites that store the language in the path portion of the URL and we have no tests to exercise that.

Another test that really should be added is to test the interaction with paths that have similarities to the redirected path. For instance, patch 101 introduced a significant bug with this code: $rule->pattern = mb_strtolower(str_replace('/*', '*', $rule->pattern)); -- that causes redirect loops if you create a redirect like:
/foo/* => /foobar/*
That's due to the fact that /foo/* pattern is now really being interpreted as /foo*...which is just not correct. But the tests didn't catch that error.

SebaZ made their first commit to this issue’s fork.

sebaz’s picture

In my opinion wildcard definition analyzed in #100 and #106 should work like wildcards in blocks visibility.
/foo/bar and /foo/bar/* separately.

Not always /foo/bar has to redirect but path descendants should do it what /foo/bar/* will cover.

laura.gates’s picture

noting that #104 works well with 9.3.9

I changed

/foo/bar/[page-alias] to /foo/[page-alias] successfully.

dqd’s picture

Agreed with #107 and #108. And yes, the way how Blocks are doing it IS THE Drupal way and this is what should be done when wild cards for paths get introduced anywhere. If this is the best way to redirect groups of paths with the same subpath in redirect is another question. I am not 100% sure that we can achieve it that way. More secure would be maybe to add a bulk editing functionality like in views_bulk_operations to bulk redirect many paths having the same sub path pattern. In this editing request the wildcard would make sense since the wildcard only exist in the editing request. But later all paths are redirected with their proper last part of the path instead of "*". I think this is how redirect works. Look at the resulting redirect. They are links. How can our wildcarded result be a proper link.

#109: Are you sure you tested carefully? When I try to create a redirect like /abc/* to /def/* it looks saved like this: /abc/* -> /def/%2A underlined as link which indicates that we possibly have a bigger problem all in all with wildcards as a concept like I described above.

kevinquillen’s picture

/foo/bar//*

Keep it simple - most people would expect normal URL entry. If you want to redirect /foo/bar and then any path under that, I would expect to enter both '/foo/bar' and '/foo/bar/*'

rkelbel48’s picture

I 100% agree with #108 and #111, keeping it simple and in line with the block placement functionality is exactly how I'd expect to walk into this module and use it without having to check the documentation.

I think that is the right way to go and hopefully helps move this solution through to a release.

ricardofaria’s picture

#104 works for me with 9.4.x, for my use case.

However, I agree with #108 and #111.
We should be able to add /test and /test/* separately.

ricardofaria’s picture

StatusFileSize
new12.94 KB

Created a patch with a little difference from the previous one. Will need to review this later, but for now its working for me.
you can redirect from:
test > /test2
test/* > /test2/*
test/path2 > /another-path

scotwith1t’s picture

+1 for #104, working great!

ronaldmulero’s picture

#104 worked great for our needs with Drupal 9.4.9.

In our case, though, we also needed the wildcard redirects to allow redirects to external files like MustRemainMixedCaseFilenameWillNeverBeFileManagedByDrupal.pdf so we created a follow-up patch locally to remove the two mb_strtolower() in src/RedirectRepository.php and it works fine.

Maybe patch #104 can be improved by removing the two mb_strtolower() functions?

Just a thought.

vvs’s picture

@ricardofaria thanks for #114 it's working for my conditions:
/recipe/* -> /recipes/*

With one file rejected:

--- src/RedirectRepository.php
+++ src/RedirectRepository.php
@@ -188,7 +243,6 @@ class RedirectRepository {
    */
   public function findBySourcePath($source_path) {
     $ids = $this->manager->getStorage('redirect')->getQuery()
-      ->accessCheck(TRUE)
       ->condition('redirect_source.path', $source_path, 'LIKE')
       ->execute();
     return $this->manager->getStorage('redirect')->loadMultiple($ids);
@@ -206,7 +260,6 @@ class RedirectRepository {
   public function findByDestinationUri(array $destination_uri) {
     $storage = $this->manager->getStorage('redirect');
     $ids = $storage->getQuery()
-      ->accessCheck(TRUE)
       ->condition('redirect_redirect.uri', $destination_uri, 'IN')
       ->execute();
     return $storage->loadMultiple($ids);

but worked in redirect 1.7.

It's rejected because code is:

  public function findBySourcePath($source_path) {
    $ids = $this->manager->getStorage('redirect')->getQuery()
      ->condition('redirect_source.path', $source_path, 'LIKE')
      ->execute();
    return $this->manager->getStorage('redirect')->loadMultiple($ids);
  }
bgilhome’s picture

@vidorado there's an undefined variable $query in your patch in #104 - line 165 of RedirectRepository.php.

chike’s picture

#114 is working for me in Drupal 9.5.10.

I could redirect /properties-location/* to /properties/* while leaving /properties-location untouched.

sidgrafix’s picture

StatusFileSize
new11.74 KB

Patch broke on latest release of redirect so here is an updated patch (same as #114, applied to latest version of redirect).

msypes’s picture

Update: This entire comment can be ignored. I only just learned that this module does not function for "From URLs" that represent real endpoints, like an actual node; it only operates on "empty URLs."

Tested patch from #120 with core 10.1.5 and redirect 1.9.
With a "From" /foo/bar/* no redirect occurred from either /foo/bar/baz nor /foo/bar/baz/bing. Was hoping for something similar to #119, using a "To" of /foo/bar. Looks like that creates a redirect loop, according to the logs, which isn't surprising, but incompatible with my use case.
I also note however, that editing the "To" to /foo didn't result in any redirects either, which is surprising.
I can't say that this patch works at all.

Additional information:
By the time RedirectRepository::findMatchingRedirect() is hit, the page's $source_path has already been converted from an alias (e.g., /foo/bar/baz/bing) to /node/###, so it longer matches with the above wildcard. Is this then related to Redirects from aliased paths aren't triggered?

chike’s picture

Sure, patch #114 broke on the latest release and #120 applied successfully.

jeffschuler’s picture

StatusFileSize
new12.82 KB
new2.41 KB

@sidgrafix: it doesn't appear your patch is a simple reroll ("the same as #114, applied to latest version"). There are a number of material differences. Are the other changes intentional (to get this working) or an accident?

Here is #114 rerolled for the current 8.x-1.x, with the one rejected hunk fixed, (plus a diff of these two patches for verification).

Leaving this as Needs Work, as @azinck's recommendations in #106 are still not addressed.

sidgrafix’s picture

@jeffschuler - All I did was reroll patch from comment 114 listed as support-for-wildcard-2831605-113.patch (if you compare that to mine they are identicle.

Per "ricardofaria" post 114 he said "Created a patch with a little difference from the previous one. Will need to review this later, but for now its working for me."

- Any changes you mention (if they diverged from the patch prior to support-for-wildcard-2831605-113.patch) were done there in support-for-wildcard-2831605-113.patch before I rerolled.. I assumed that patch was the previous current working if it is not "my bad", apologies for any confusion. Tho the patch has been working fine for me!

jeffschuler’s picture

@sidgrafix your patch in #120 and the patch in #114 are not identical.

For example, the patch in #114 has this change:

-    $this->assertSession()->statusCodeEquals(404);
+    $this->assertResponse(404, 'Deleted redirect properly clears the internal page cache.');

while your patch does not.

The #114 patch removes this line:

-    $this->assertSession()->pageTextContains('No URL redirects available.');

but yours doesn't:

     $this->assertSession()->pageTextContains('No URL redirects available.');

The #114 patch calls drupalPostForm:

+    $this->drupalPostForm('node/' . $node->id() . '/edit', ['path[0][alias]' => '/node_test_alias_updated'], t('Save'));

and yours calls drupalPostFormForm:

+    $this->drupalPostFormForm('node/' . $node->id() . '/edit', ['path[0][alias]' => '/node_test_alias_updated'], t('Save'));

...

sidgrafix’s picture

@jeffschuler - I honestly not entirely sure being 5 months ago, there is a chance I patched a non-dev version then by accident and the slight differences in the lines you mention would have been changed in accordance with whatever version of redirect I was patching.

Looking at the composer.json for the site I used patch 120 on I patched redirect 8.x-1.9 (which was for a D9.5 sight, since updated to 10.2.2) again apologies for any confusion this may have caused..

jeffschuler’s picture

No prob; just want to see this issue continue forward!

vidorado’s picture

I can confirm what Azinck says in #106

Another test that really should be added is to test the interaction with paths that have similarities to the redirected path. For instance, patch 101 introduced a significant bug with this code: $rule->pattern = mb_strtolower(str_replace('/*', '*', $rule->pattern)); -- that causes redirect loops if you create a redirect like:
/foo/* => /foobar/*
That's due to the fact that /foo/* pattern is now really being interpreted as /foo*...which is just not correct. But the tests didn't catch that error.

A redirect /r/* has become /r* which is a CRITICAL bug

The patch from #123 has the problematic line commented:
// $rule->pattern = mb_strtolower(str_replace('/*', '*', $rule->pattern));

And now that bug has gone.

Anybody remembers what was the purpose of that str_replace()? It does not make sense for me right now...

ceithamer728’s picture

StatusFileSize
new12.45 KB

Patch #123 was broken for us in Drupal 10.2.5 as it was removing the now required accessCheck(TRUE) calls from RedirectRepository class.

My patch just adds those two lines of code back.

vidorado’s picture

StatusFileSize
new12.17 KB

The patch in #129 commented the line

// $rule->pattern = mb_strtolower(str_replace('/*', '*', $rule->pattern));

And two thing where stripped out:

- The replacing of '/*' to '*'.
- But ALSO making the rule case insensitive.

I attach a new patch to restore the latter.

Matthijs made their first commit to this issue’s fork.

matthijs’s picture

We noticed a performance impact on our project due to this patch (the query took often 300-500ms) and decided to remove it.
As a safety (in case it ever gets merged) I converted the patch from #130 into an MR and added a config option to enable wildcard support.

I did not update any tests, these might still need changes.

ameymudras made their first commit to this issue’s fork.

jelle_s’s picture

I've not been able to test yet, but I was wondering what this patch/MR would do if there are these wildcard redirects:

a-path/* -> redirect1.com
a-path/b-path/* -> redirect2.com

and you would navigate to a-path/b-path/c-path, which redirect would it use?

vidorado’s picture

@Jelle_S It will select the second one, as it is the more specific one (determined by being the longer string)

vidorado’s picture

Issue tags: -Needs tests
chizh273’s picture

StatusFileSize
new30.41 KB

I can't update the MR on the GitLab so here's a new version with resolved merge conflict.

vidorado’s picture

Status: Needs work » Needs review
gaëlg’s picture

@chizh273 If you click on the green button "Get push access" above, you should be able to update the MR. Anyway, @vidorado dit it.

vidorado’s picture

Thanks for the review @gaelg! It is very useful to polish the feature.

I will get into it tomorrow 😊

vidorado’s picture

I've made some changes to address the issues identified by @gaelg, but I can't resolve the threads on git.drupalcode.org; I don't see any button to do so...

prudloff’s picture

I think only the creator of the MR, the person who started the thread or the maintainers of the module can mark threads as resolved.

gaëlg’s picture

Thank you for the work @vidorado! I cannot resolve threads either. @Matthijs?

matthijs’s picture

@vidorado @gaëlg I resolved them all.

nikolay shapovalov’s picture

Status: Needs review » Needs work

Thanks for update MR, please check my feedback.

vidorado’s picture

Issue summary: View changes
StatusFileSize
new75.3 KB
new86.47 KB
new37.71 KB
new44.54 KB
new17.86 KB
vidorado’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -needs profiling

Tests added! :)

Thanks for the review @nikolay

nikolay shapovalov’s picture

Thanks for removing all unrelated changes, and MR update.
It's much easier to review now.
I left my feedback.
But it would be great someone else also check MR.

bernardopaulino’s picture

StatusFileSize
new17.59 KB

I’ve created a patch based on the latest version of merge request !97, introducing a few adjustments to ensure that redirect patterns are matched against URL aliases rather than just internal paths like /node/[nid]. This change supports a specific client requirement where redirects should be defined using node aliases, e.g., example-node-alias/*.

We should review this patch to verify its compatibility across different use cases and evaluate its potential impact on performance.

ronraney’s picture

I tried MR97. I added as a patch, ran composer and so forth. I tried doing something like this:

From: /county-news/*
To: /news/*

It is not working for me. Is this the correct usage of a wildcard?

P.S. Sorry, I didn't know that you had to enable it in configurations. It works!

ronraney’s picture

I found something interesting, however.

It appears that whatever the wildcard represents cannot contain at least a hyphen (-). For example,

If I do this...
From: /depts/voting-and-elections/polling-places/*
To: /county-clerk/voting-and-elections/where-to-vote

This string won't work...
/depts/voting-and-elections/polling-places/precinct-2

But this one will
/depts/voting-and-elections/polling-places/2025 (no hyphen in the wildcard string)

Is this a known issue or something that won't be resolvable? Of course, it would need to be verified. I'm concerned that anything having any valid URL punctuation like hypen - or / backward slash will not be resolvable using wildcards.

P.S. A feature that would be really helpful is the ability to put wildcards before a string, like this:
From: /*apple-touch-icon*
To: /location-of-apple-touch-icon.png

cmd87 made their first commit to this issue’s fork.

ruslan piskarov’s picture

StatusFileSize
new11.42 KB

Attaching patch from merge request 97.

3cwebdev’s picture

@ruslan - thank you for the patch. It applied to v1.12, but the wildcards are no longer working. I've verified that the "Enable wildcard support" checkbox is enabled and have cleared the caches, but wildcard paths are now ignored (were working on 1.11 with the previous patch). Do you have any ideas on where the issue might be?

ronraney’s picture

Hello, I tried applying patch 2831605-150. Composer says I cannot apply the patch. Is this being considered for merge? I have redirects on my site that stopped working. At some point, I think the patches in this issue cannot be applied. Please point me in the right direction if I'm wrong.

P.S. I used the "97.diff" and it installed correctly.

It still doesn't work if part of the path used by the wildcard has hypens.

scotwith1t’s picture

We're seeing an odd side-effect of what I can only assume is this patch...when we add a redirect like this:

from: /docs/*
to: https://www.example.com/docs/*

the redirect works one time and the first click is for, say, https://www.example.com/docs/doc12345.pdf BUT the redirect rule is then updated (like it is resaved somewhere along the line and completely changed) to have the "to" be the specific redirect that was successful (https://www.example.com/docs/doc12345.pdf) so that every subsequent click on any other /docs/* link takes you to that single pdf instead of redirecting appropriately. This definitely USED to work, so I'm not sure what changed, so just reporting for now. We are replacing this with a custom rule in our .htaccess file for now, but it would be ideal if our admin users could continue to maintain wildcard rules like this. Thanks!

alphex’s picture

The patch from #154 does not apply to the dev release at this time.

o_timoshchuk’s picture

Assigned: Unassigned » o_timoshchuk
Status: Needs review » Needs work
o_timoshchuk’s picture

Assigned: o_timoshchuk » Unassigned
Status: Needs work » Needs review
StatusFileSize
new10.57 KB

I have prepared a patch for version 1.13.0

alphex’s picture

I can verify the patch from #160 works against drupal/redirect:^1.13 on Drupal 11.3.8 using a simple pattern like:

"/foo/*" --> "/a-real-page"

thanks!

fabsgugu’s picture

Status: Needs review » Reviewed & tested by the community

Be careful, I don't think it's a good idea to use the patch as is.

If the patch doesn't integrate into the module's core, we might encounter problems with the next addition to the module's .install file, which simply won't be updated.

Ideally, we should wait until it is added to the module to use the functionality.

----

The patch works well, I have the desired redirections.

Exemple 1 :
/test/* => /redirect/*

/test/test => /redirect/test
/test => /test

Exemple 2 :
/test/* => /*

/test/test => /test
/test => /test