Quick links: Content - sections - sub sections
EN FR

Here is how you can configure the significant engine for the url system of Jelix.

The configuration principle of this engine is to indicate in a var/config/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="/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 entry point defines one or more possible url forms, knowing that those that are not defined will be accepted on an entry point defined as the default one.

Specify a complete url form

According to a pathinfo

You want to indicate the module and the action to execute for a particular url form. 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 module and the action indicated.


   <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. In the following example, the pathinfo contains two dynamical parts: chapter and id_art.


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

If we use the "/articles/planes/544" url, then the chapter and id_art parameters will be created and will be 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

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 to different actions for so small difference.

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.

Jelix 1.4 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

Since Jelix 1.4, 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(), but alos 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".

Contrary to Jelix 1.3 and lower, you then don't have to duplicate URL declaration for each lang.

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" (an other solution is to create a new url engine but it is more complicated).

To create an handler, you create a specific class:



class myHandlerUrlsHandler implements jIUrlSignificantHandler {

    function parse($url){
        
        if(preg_match("/^\/(.*)$/",$url->pathInfo,$match)){
            $urlact = new jUrlAction($url->params);
            $urlact->setParam('page',jUrl::unescape($match[1]));
            return $urlact;
        }else
            return false;
    }

    function create($urlact, $url){

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

        $url->pathInfo = "/$f";
        $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:


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

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


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

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 that an entry point is dedicated to a particular module

You not always want to indicate a significant url for the actions of particular module. However, you have created an entry point dedicated to this module. All these actions will pass through this entry point. You just then have to do it like this:


    <classicentrypoint name="news">
        <url module="news" />
    </classicentrypoint>

You can specify several module like that on a same entry point.

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 shoud 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 allow 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, with a pathinfo with parameters, with an handler, 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.

Mod_rewrite and just removing the name of the entry point in the url

Perhaps you would prefer that the parsing of the urls will be done by the web server, by using for example the rewrite mod in Apache.

Full use of mod_rewrite

You can use mod_rewrite to parse the urls and so to rewrite urls to simple urls (index.php?module=...&action=...).

For that, you should inform jelix that it doesn't have to parse the urls:


 [urlengine]
  ..
  enableParser = off

However you should create a urls.xml file, so jelix can generate significant urls when your application need it.

In your .htaccess file, you should write all rewrite rules (see the apache manual).

If you want to define urls which don't contain the entry point (index.php for example), you should tell to Jelix that it musn't insert the entry point into generated urls. So you you have to add noentrypoint="true" on the <url> element or on the <entrypoint> element.

Just remove the entry point

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 use and configure the mod_rewrite module of apache.

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 should create a .htaccess file in the www/ directory, which 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