Quick links: Content - sections - sub sections
EN FR
Jelix 1.9.0-dev

Section: urls.xml file

^ jUrl: automatic urls
Switch to language: FR

The configuration principle of this engine is to indicate in a app/system/urls.xml file all the possible url forms in the application, and the actions that will be associated to them. Here is an example of file:


<urls xmlns="http://jelix.org/ns/urls/1.0">
    <entrypoint name="index" default="true">
        <url pathinfo="/" module="main" action="default:index" />
        <url module="main" />

        <url pathinfo="/news/:annee/:mois/:id-:title" module="news" action="view">
           <param name="annee" type="year"/>
           <param name="mois"  type="month" />
           <param name="id"   type="number" />
           <param name="title"   escape="true" />
        </url>

        <url pathinfo="/articles/:rubrique/:id_art" module="cms" action="show">
           <param name="id_art" regexp="\d+"/>
        </url>

    </entrypoint>
    <entrypoint name="shop" type="classic">
       <url pathinfo="/:category/:product" module="unittest" action="url2">
          <param name="product"  regexp="\d{2}" />
          <static name="mystatic" value="valeur statique" />
        </url>
    </entrypoint>
    <classicentrypoint name="foo/bar">
       <url handler="urlsig" module="unittest" action="url4" />
    </classicentrypoint>

    <classicentrypoint name="news">
        <url module="news" />
    </classicentrypoint>
    <entrypoint name="xmlrpc" default="true" type="xmlrpc"/>
    <entrypoint name="jsonrpc" default="true" type="jsonrpc"/>
</urls>

Entrypoint tags

The urls root tag contains the same number of entrypoint tags as entrypoints available in your application.

Each of these tags has a name attribute specifying the name of the entry point (without the .php extension), or, precisely, its relative path to the basePath indicated into the main configuration. And you could set eventually a default attribute specifying if this entry point is the default one for the actual type of request.

An entrypoint tag should also have a type attribute which indicates the type of the entry point : classic, xmlrpc, soap etc. If this attribute is not present, the default value is classic.

For compatibility reasons with older version of jelix, you can use other name for entrypoint elements. Their exact name gives the type of request they are affected to:

  • classicentrypoint for classical request,
  • xmlrpcentrypoint for xmlrpc
  • etc...

However this kind of names are now deprecated.

Url tag

Each module is accessible through a specific entry point, so you should declare urls of modules in the corresponding entry point element.

Specify the default action, the homepage

Every application has an homepage at the / url.

So you have at least one url to define on each "classic" entry points:


<urls xmlns="http://jelix.org/ns/urls/1.0">
    <entrypoint name="index" default="true">
        <url pathinfo="/" module="main" action="default:index" />
    </entrypoint>
</url>

So here the url index.php/ correspond to the method index of the controller default in the module main.

Before Jelix 1.7, the home page was defined in the startModule and startAction parameters in the configuration of each entry points.

Automatic urls for modules

As you will see below, you can define url for each action. But it is not required.

By default, urls are like <entrypoint>.php/<module>/<controller>/<method>.

You have at least to indicate on which entry point the module is accessible, and so to specify the url tag for module: a <url> indicating only a module name.


    <entrypoint name="index" default="true">
        <url pathinfo="/" module="main" action="default:index" />
        <url module="main" />
    </entrypoint>
    <entrypoint name="blog">
        <url module="news" />
    </entrypoint>

Here all urls of the news module are like blog.php/blog/<controller>/<method>.

You can also indicate a pathinfo: it then replaces the module name into the url.

With this example:


    <entrypoint name="index" default="true">
        <url pathinfo="/" module="main" action="default:index" />
        <url pathinfo="/awesome/informations/" module="news" />
    </entrypoint>

The urls of the news module are like index.php/awesome/informations/<controller>/<method>.

If there isn't an url tag for module for a specific module, only actions of this module that are defined by an url tag for action, will be accessible from the web.

In the previous example, there is only one action for the main module that is defined, in an url tag, with the pathinfo /, so only the default:index will be accessible from the web. For any other actions of the module main, you should also declare an url tag, or you can define <url module="main" /> to automatically declare automatic url for other actions.

If there isn't no url tag that targets a module, the actions of this module will be accessible by the default entry point.

Automatic urls for controllers

You can define a pathinfo for a controller, and then the method name is the last component of the path.

On an url tag, you have to indicate the module, the pathinfo, and the controller in a controller attribute.


    <entrypoint name="index" default="true">
        <!-- ... -->
        <url pathinfo="/cool" module="main" controller="system" />
    </entrypoint>

Urls of all actions of the controller system, will be like index.php/cool/<method>.

Specifying url for actions

According to a pathinfo

You want to indicate the module and the action to execute for a particular url. You will then indicate a pathinfo attribute, specifying the model of the pathinfo part the url must be like, and the module and the action in the module and action attributes.

The pathinfo attribute must then contain the value of a pathinfo. In this example, every url with "/foo/bar" pathinfo will correspond to the indicated module and action .


   <url pathinfo="/foo/bar" module="hello" action="world" />

You can set the attribute optionalTrailingSlash="true" if you want that if there is a trailing slash or not, it correspond to the same action, ie, /foo/bar/ and /foo/bar are the same action (by default, it is not the case). You can set this attribute on the entrypoint element, so all it is activated for all defined urls.

According to a pathinfo with undefined parts

It is possible to indicate "dynamic" parts in the pathinfo. They are defined as two points (:) followed by a name. The retrieved value will then be placed in a parameter with the same name for the controller. In the following example, the pathinfo contains two dynamical parts: chapter and id_art.


   <url pathinfo="/articles/:chapter/:id_art" module="cms" action="default:show" />

If we use the "/articles/planes/544" url, then the chapter and id_art parameters will be created and will have the "planes" and "544" value respectively.

Be careful: to avoid the confusion with other urls, at least one static part is needed (here "/articles/" in the url to distinguish it from the others).

The typed and formatted undefined parts

Another way to avoid resemblances is to specify the format or the type of each parameter. By default, the type is a classical string.

For this, you have to indicate <param> tags for each parameter which you want to specify the type/format of. They will have to contain a name attribute indicating the parameter, and either a type attribute or a regexp attribute, containing a regular expression of the format (without delimiter). In our example, we want to specify that chapter is a string, and a regular expression for id_art:


   <url pathinfo="/articles/:chapter/:id_art" module="cms" action="show">
      <param name="chapter" type="string" />
      <param name="id_art" regexp="\d+" />
   </url>

if the regular expression of the attribute regexp contains parentheses, it's necessary to say that this one should not be catched. Example:


    <param name="type" regexp="(?:0|1|2){1}" />

When the parameter is of string type, it is not mandatory to specify a param tag. The available types are:

string a string
letter one letter alone
number an integer number,equivalent to 'int' et 'integer' too
digit a digit
date a date according to the AAAA-MM-JJ format
year a year on four digits
month a month on two digits
day a day on two digits
path a sub-path of the url. this parameter must be the last one in the URL
lang lang code (2 or 3 letters)
locale locale code (lang code + country code)

Notice: you must of course indicate the values of these parameters when calling jUrl.

Static parameters

It can be sometimes necessary to add "static" parameters, awaited by the action (which can be attributed to several different urls), but not present in the url. For this you have to add <static> elements, with value and name like in this example:


   <url pathinfo="/:category/:product" module="shop" action="view">
      <param name="product"  regexp="\d{2}" />
      <static name="details" value="0" />
   </url>
   <url pathinfo="/:category/:product/details" module="shop" action="view">
      <param name="product"  regexp="\d{2}" />
      <static name="details" value="1" />
   </url>

Here, we use the same action for two different urls. The processing will be partly different according to the details parameter. In this case, we would display the product of a catalogue, with general characteristics, and in the other its general and detailed characteristics. This avoids to create two different actions for so small differences.

We can use this mechanism to support language, for instance:


   <url pathinfo="/articles/en/:page" module="cms" action="page:view">
      <param name="page"/>
      <static name="lang" value="en_US" />
   </url>
   <url pathinfo="/articles/fr/:page" module="cms" action="page:view">
      <param name="page"/>
      <static name="lang" value="fr_FR" />
   </url>

You should not forget to give the lang parameter to jUrl.


jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>jApp::config()->locale));
// or
jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>"en_US"));

<a href="{jurl 'cms~page:view', array('page'=>'foo', 'lang'=>$j_locale}">my link</a>
<a href="{jurl 'cms~page:view', array('page'=>'foo', 'lang'=>'fr_FR'}">my link</a>

Automatic parameters for language

with static parameters

We saw that we could declare static parameters containing the code of the language, to have different URLs pointing to the same action. However, it causes some issues:

  1. you have to give the parameter to jUrl::get(), and in most of case, you use the current language code (jApp::config()->locale).
  2. in the action you have to set yourself the language code in the configuration, with the given value in the parameter.

The url engine brings some improvements about these issues. You just have to add the attribute type="locale" or type="lang", and then, everything is automatic. You can define for example:


   <url pathinfo="/articles/english/:page" module="cms" action="page:view">
      <param name="page"/>
      <static name="lang" value="en_US" type="locale"/>
   </url>
   <url pathinfo="/articles/francais/:page" module="cms" action="page:view">
      <param name="page"/>
      <static name="lang" value="fr_FR" type="locale" />
   </url>

If you call simply jUrl::get('cms~page:view', array('page'=>'foo')), then jUrl will generate /articles/english/foo if the current language is "en_US", or /articles/francais/foo if it is "fr_FR". You don't have to indicate the language code as before: jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>jApp::config()->locale)). However, you can still give it if you want to force the language code (one code different from the default one).

During the call of the page /articles/english/foo or /articles/francais/foo, Jelix will configure automatically the locale. So when the user call the page /articles/english/foo, the current language will be "en_US".

You can also use type="lang". In this case, you just have to indicate the lang code ('en', 'fr'...), either in the attribute value, or to jUrl::get().


    <static name="lang" value="fr" type="lang" />

With dynamic parameters

If there are more than 2 or 3 language, the use of static parameters for language could be tedious, since you have to define an url for each language.

Happily, you can have parameters with the type "locale" or "lang".


   <param name="lg" type="lang"/>
   <!-- or -->
   <param name="lg" type="locale"/>

It allows you to have a language code into the "pathinfo" of the URL, without indicating it to jUrl::get(), It also allows to configure automatically the current language during the execution of the action.

Then, the previous example:


   <url pathinfo="/articles/en/:page" module="cms" action="page:view">
      <param name="page"/>
      <static name="lang" value="en_US" />
   </url>
   <url pathinfo="/articles/fr/:page" module="cms" action="page:view">
      <param name="page"/>
      <static name="lang" value="fr_FR" />
   </url>

becomes:


   <url pathinfo="/articles/:lang/:page" module="cms" action="page:view">
      <param name="page"/>
      <param name="lang" type="lang" />
   </url>

If you just call jUrl::get('cms~page:view', array('page'=>'foo')), then jUrl will generate /articles/en/foo if the locale is "en_US", or /articles/fr/foo if the locale is "fr_FR".

And if a browser calls /articles/en/foo, the current locale will be automatically "en_US".

Same principle with type="locale", except that jUrl will generate /articles/en_US/foo.

Using an handler

We saw how to use a system to analyse the content of the pathinfo. However, sometimes it is not enough, and we need a more complex parsing. For example, parts of a pathinfo can be some data stored in a database, and we need to search in the database this parts to get some ids or whatever..

So in some case, we want to create our own parser, called an "handler".

To create an handler, you create a specific class:



class myHandlerUrlsHandler implements jIUrlSignificantHandler {

    function parse($url) {       
        if (preg_match("/^\/(.*)$/",$url->pathInfo,$match)) {
            // the pathinfo is the expected one, we can process it 
            $urlact = new jUrlAction($url->params);
            $urlact->setParam('page',jUrl::unescape($match[1]));
            return $urlact;
        }
        else {
            // the pathinfo does not correspond to what we expect
            return false;
        }
    }

    function create($urlact, $url){

        $p=jUrl::escape($url->getParam('page'));

        $url->pathInfo = "/$p";
        $url->delParam('page');
    }
}

The class name should end by UrlsHandler, and the name of the handler is the prefix (here myHandler). Then the class should be stored in the classes/ directory of a module. The file should be named like <prefix>.urlhandler.php. In our example, it should be myHandler.urlhandler.php.

The method parse() should parse the url given as argument (a jUrl object). If your handler recognize the url, it should return a jUrlAction object, else it should return false.

The method create() is called each time the application ask the url corresponding to an action. This method receive a jUrlAction object and a jUrl object. $urlaction contains the parameters of the action. This parameters are already stored in the $url object. Then you should modify the $url object in order to generate the corresponding true url. So in fact, you should generate the pathinfo, and/or delete some parameters...

Note that you can use jUrl::escape() and jUrl::unescape(), to cleanup strings (by removing some special characters for example).

Then, in the urls.xml file, you have to specify the handler:


   <entrypoint name="wiki">
       <url handler="myHandler" module="unittest" action="url4" />
    </entrypoint>

Of course, the handler can be stored in any module:


   <entrypoint name="wiki">
       <url handler="othermodule~myHandler" module="unittest" action="url4" />
    </entrypoint>

The same URL for several possible actions

Let's imagine that we have a url of the following pattern, /article/54-title, and this displays the article number 54 with a view action associated for example:


   <url pathinfo="/article/:id_art-:title" module="cms" action="view" />

We want to able to indicate other actions in some case without changing the url, with an action parameter:

  • /article/54-title?action=edit
  • /article/54-title?action=delete

Notice: we could also do /article/54-title/edit or /article/54-title/delete, with thus several <url> tags, which would avoid what follows. But this would not be very good when the url is called by a form for example.

To specify the authorized alternative actions, we add an actionoverride attribute, containing the list of the actions separated by a space or a comma:


   <url pathinfo="/article/:id_art-:title" module="cms" action="view" actionoverride="edit,delete" />

Specify some secured urls (https) for specific actions

For some actions, you could want to access them through an https access. So you should add a https attribute (with "true" as a value) on the <url> elements you want. If you want to specify https on all actions of an entrypoint, then you can put the https attribute on the entrypoint element instead of each url element.

Including urls files of modules

You can define URLs in separate files, in each module, and then declare those files into the main urls.xml file. It avoid to have a big main urls.xml file, and more important, to avoid to declare or rewrite all url of a module in this file, when you install a module made by someone.

To do it, you should create a file, urls.xml (or with an other name), in the directory of the module. This file should have a suburls element as root element, instead of urls, and have all needed url element to declare all urls mapped to each action of the module. You should not use entrypoint elements of course. Here is the urls.xml file of the jauth module:


<?xml version="1.0" encoding="utf-8"?>
<suburls xmlns="http://jelix.org/ns/suburls/1.0">
    <url pathinfo="/dologin" action="login:in" />
    <url pathinfo="/dologout" action="login:out" />
    <url pathinfo="/login" action="login:form"/>
</suburls>

And in the main urls.xml file, in one of your entrypoint element, you indicate:


  <entrypoint name="index">
    ...
     <url pathinfo="/auth" module="jauth" include="urls.xml" />
  </entrypoint>

It means: urls declared in the urls.xml file of the module jauth should be included, and should be prefixed by the given pathinfo "/auth/". So the url of the "jauth~login:in" action will be "/auth/dologin".

You notice that URLS defined in a module file are not "full" URL. It allows the developer to choose the first part of URLs, and it avoid to have conflict with other URLS declared in other modules.

In module files, you can declare URL with a static pathinfo, or with a pathinfo with parameters, or with an handler, or with static values. However you have to declare an url for all actions. And you cannot of course use the module attribute.

You have also to create a url file for each type of entry point, if needed.

Removing the entry point name from urls

Perhaps you would like to remove the name of the entry point in the urls generated and parsed by Jelix. But to do it, you have to configure correctly your web server.

For example, you want to have:


   http://monsite.com/news/2017-02-08-il-neige-a-lille.html

instead of:


   http://monsite.com/index.php/news/2017-02-08-il-neige-a-lille.html

First, in the urls.xml file, put the attribute noentrypoint="true" on the <url> element or on the <entrypoint> element.

Second you must configure your web server.

For Nginx, you can create a configuration like this:


server {
    
    location / {
        try_files $uri $uri/ @mysite;
    }

    location @mysite {
            fastcgi_split_path_info ^()(/.*)$;
            set $path_info $fastcgi_path_info;
            include fastcgi_params;

            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME "$document_root/index.php";
            fastcgi_param PATH_INFO       $fastcgi_path_info;
            fastcgi_param PATH_TRANSLATED "$document_root/index.php";
            fastcgi_param SCRIPT_NAME "/index.php";
            fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    }
}

For Apache, you can use the rewrite module, with a simple configuration: the path will be added after the index.php. You should create a .htaccess file in the www/ directory, or a virtualhost section in the Apache configuration, and both solution should contains this rules:


<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.php/$1 [L,QSA]
</IfModule>
  • second line: you don't need it if you write these instructions into an .htaccess in the www directory of your app. However, in an other .htaccess or in the virtual host configuration, you have to write it, and you must indicate the right path to the www directory: / if the documentRoot is the www directory, or the path to access this www directory from the documentRoot of the website (basePath). Example: RewriteBase /myapp/www/ if you access to your application with http://localhost.local/myapp/www/index.php
  • third and forth lines: url will be rewritten only if doesn't correspond to a real file or a real directory. (for css files, images etc..).
  • fifth line: we get all the content of the url and put it after index.php/, so the url become the pathinfo of the new url.

In some apache configuration, you could have an apache error "Error: No input file specified" (this is the case for example when PHP is executed in cgi mode and cgi.fix_pathinfo=0 in php.ini).

There is a solution. You should change the last rule to:


    RewriteRule ^(.*)$ index.php?jpathinfo=/$1 [L,QSA]

So the url is not put as the pathinfo, but as a query parameters. Of course, you should inform jelix of this parameter. You have to add the pathInfoInQueryParameter parameter in the urlengine section in the jelix configuration file. pathInfoInQueryParameter should contains the name of the parameter (here in the example, jpathinfo):


 [urlengine]
 ...
 pathInfoInQueryParameter = jpathinfo