And now to my favourite topic, mass URL aliasing. See the attached patch. It modifies drupal_get_normal_path() and drupal_get_path_alias() to call a user defined function (probably defined in conf.php, thus the name) to return an alias for the URL.

Some possible scenarios, where you would use it:

Mass URL translation

Since you have the ability to translate the site interface via locale.module, you should have the ability to translate the URL interface to your own language. Here is a simple example for Hungarian:

function conf_url_rewrite($path, $mode = 'incoming') {
  if ($mode == 'incoming') {
    return preg_replace("!^olvasas/(\\d+)$!", "node/view/\\1", $path);
  }
  else {
    return preg_replace("!^node/view/(\\d+)$!", "olvasas/\\1", $path);
  }
}

Mass URL shortening

You would like to provide short aliases for RSS feed links, since you would like to include the links on all pages, and let people share the URL with each other, and avoid the URL breaking in emails, etc. While you can add an alias with path.module to every taxonomy term, this is future proof, since if you remove a taxonomy term, the alias 'gets removed'.

function conf_url_rewrite($path, $mode = 'incoming') {
  if ($mode == 'incoming') {
    return preg_replace("!^rss/(\\d+)$!", "taxonomy/feed/\\1", $path);
  }
  else {
    return preg_replace("!^taxonomy/feed/(\\d+)$!", "rss/\\1", $path);
  }
}

URL fakeing

Lets say I need to have a new overview page for the forums, but without modifying the forum module. Of course I would not like to serve that feature under a different URL, but would like to make people think that it is under /forum (which also makes it possible to use default breadcrumbs, block restrictions, and the like). Now what I can do is to fake the URLs of taxonomy.module below the URLs of forum.module.

function conf_url_rewrite($path, $mode = 'incoming') {
  if ($mode == 'incoming') {
    return preg_replace("!^forum/quickview/(\\d+)$!", "taxonomy/page/\\1", $path);
  }
  else {
    return preg_replace("!^taxonomy/page/(\\d+)$!", "forum/quickview/\\1", $path);
  }
}

More notes

I use mass URL aliasing for all the above problems successfully. I needed to have a 'news' URL space on my site, which needed to be called Hungarian ('hirek'). I faked all the story type nodes in the 'news' vocabulary to have an URL under this 'hirek' URL space, plus I have faked an overview page (from taxonomy module) to be located under this URL space, and I have added my own taxonomy terms overview page from my own module faked under this URL space. Now I can restrict blocks to only display under my news section, and I have short Hungarian URLs named after what they mean and not after what they do.

Note that one can handle the incoming URL problem without custom mass URL aliasing. For the above example, you can add a module to Drupal and assign a menu() to handle 'forum/quickview' and in that handler function you can call the handler of 'taxonomy/page' properly, thus faking the URL. You can also handle incoming translated URLs with adding menu() calls for the translated URLs, but there is no solution to alias the outgoing URL massively, and therefore it is not much that you can implement the incoming side without my patch...

I strongly beleive that this small patch gives enourmous power to people using Drupal, apart from easing my life, so I need not patch common.inc with every Drupal update :)

Comments

gábor hojtsy’s picture

Ups, my last example was a bit shortsighted in that it translates all outgoing taxonomy/page URLs to forum/quickview pages :) That is not right... In my own implementation I have precached node type + vocabulary and taxonomy vocabulary relation arrays, so I can decide on what URL alias to use...

But that would be a bit too complicated example for such a simple patch :) for a simpler example, let's assume you would like to provide your special overview of the hottest topics per forum category, and that said, you need your own module, which you would like to fake under the forum URL space:

function conf_url_rewrite($path, $mode = 'incoming') {
  if ($mode == 'incoming') {
    return preg_replace("!^forum/hottest/(\\d+)$!", "hottest/\\1", $path);
  }
  else {
    return preg_replace("!^hottest/(\\d+)$!", "forum/hottest/\\1", $path);
  }
}

This will work without interfering with something else...

dries’s picture

Cool.

  • conf_get_path_map() is not defined anywhere?
  • If you want this to get accepted, your explanation should be part of either the path or the system module's documentation.
  • Translating a Drupal site's URL scheme this way is not particularly performant/practical; one has to setup many expensive regular expressions and search and replace operations. There might be better ways to do this but until these get implemented, this is probably our best shot.
gábor hojtsy’s picture

StatusFileSize
new1.14 KB

Wups, I have incorrectly used conf_get_path_map() here, which is essentially the same as the built-in drupal_get_path_map(). Here is a corrected patch...

dries’s picture

Could you please submit a patch that includes documentation updates. This ought to be documented.

gábor hojtsy’s picture

StatusFileSize
new7.34 KB

Extended patch with documentation updates (to path.module). And taken url() as an example for if-elseif-else formatting, so the new code conforms better to already used code.

dries’s picture

I'm not entirely happy yet with the approach. Moreover, I wonder why the 'url rewrite' function should be defined in conf.php? It would make perfect sense if people could provide a module with a url_rewrite() function of some sort (e.g. a 'hungarian URLs module' or a URL rewrite module with administration UI).

gábor hojtsy’s picture

I am not entirely happy with you asking me to document something which will not be accepted afterwards... I have provided extensive explanation as well as the code for consideration without the added documentation so I need not put effort into writing documentation for something that will morf into a really different thing later after some discussion...

I would however be happy with enabling people to implement this as a module_url_alias() hook or something, but I am not sure that it would always be available to be called, since I am not familiar with the inner workings of module_invoke() now that we have bootstrapping, and throttling, which might influence what is loaded and executed.

gábor hojtsy’s picture

BTW if we implement this as a new hook, then there is no reason to document it in path.module, since it becomes 'module developer documentation', and it should go into the hooks docs.

dries’s picture

I committed the common.inc part of your patch. I will or will not commit the documentation part of your patch after a consensus has been made as to whether this needs to be a hook or not. I'm worried that such a hook would be too expensive, though conf_rewrite_url() could also be added to a module, without being a hook (as long you don't have two modules implementing this function).

If the thought of making this a hook occurred to me earlier, I would have told you so. Sorry about the frustration caused.

gábor hojtsy’s picture

Thanks for committing. If you would decide to go on the hook route, a foreach() loop could be implemented to call all module_url_alias() functions, similarly to the filtering code.

As I have investigated, in bootstrap mode the original request URI is used to query the cache, so there is no need for aliasing. Otherwise a module file is included if it is not affected by throttle. An intelligent administrator should not set a module providing core URL aliasing features to be throttle controlled. So theoretically the code of modules defining such hooks should be in memory. Then only the question of performance remains, whether having a foreach and several module_invoke() calls worth the added benefit of allowing any module to add mass URL aliases...

gábor hojtsy’s picture

Since the semantics of the outgoing mode was changed back to not returning anything if an alias does not exist, a corrected example would be:

function conf_url_rewrite($path, $mode = 'incoming') {
  if ($mode == 'incoming') {
    return preg_replace("!^dagboek/(\\d+)$!", "book/view/\\1", $path);
  }
  else {
    $aliased = preg_replace("!^book/view/(\\d+)$!", "dagboek/\\1", $path);
    if ($aliased != $path) { return $aliased; }
  }
}

This shuold be reflected in any example that might go into the documentation.

gábor hojtsy’s picture

Title: Allow mass URL aliasing » Fix misleading help in path.module
StatusFileSize
new2.55 KB

My patch on path.module seems to be accepted (as opposed to what Dries stated here). Since it has some minor backslash escaping problems and it refers to the semantics of the aliasing I tried to push into Drupal, this fixes the help text to reflect how the mass URL aliasing function should be written. Also added a note on the need of deep knowlegde about Drupal and regular expressions to make use of advanced mass URL aliasing.

gábor hojtsy’s picture

StatusFileSize
new2.55 KB

Updated patch, which also fixes the .po extraction problems of dollar signs not escaped... This should be committed instead of the former patch...

dries’s picture

Committed to HEAD. Thanks.

Anonymous’s picture