Quick links: Content - sections - sub sections
EN FR

To generate HTML/XHTML, you indicate the alias "html" or "basichtml" to the getResponse() method in your controller's action. Then you retrieve respectively an instance of jResponseHtml or jResponseBasicHtml. Here is an example:


   $resp = $this->getResponse('html')

HTML5 ou HTML4

jResponseHtml generates an HTML5 header instead of an HTML4 header. However, you may want to have HTML4. You should then use the class jResponseHtml4.

So, if you have a personnalized response (this is the case by default in new applications), you should inherit from jResponseHtml4 instead of jResponseHtml:


require_once (JELIX_LIB_CORE_PATH.'response/jResponseHtml4.class.php');

class myHtmlResponse extends jResponseHtml4 {

}

Note: jResponseHtml4 inherits from jResponseHtml, so, in following parts of the manual, all features described for jResponseHtml are available for jResponseHtml4.

XTHML or HTML

jResponseHtml and jResponseHtml4 classes have an XHTML mode. Anyway, jResponseHtml4 generates XHTML headers.

The setXhtmlOutput() of this classes allows to generate the right DOCTYPE and HTTP headers according to your preferences:


// jResponseHtml
$rep->setXhtmlOutput(true);  // XHTML 5
$rep->setXhtmlOutput(false); // HTML 5 (by default)

// jResponseHtml4
$rep->setXhtmlOutput(true);  // XHTML 1.0 (by default)
$rep->setXhtmlOutput(false); // HTML 4

// Fetch the setting value :
$outputXhtml = $this->isXhtml();

Going further, you can specify your own DOCTYPE by overloading outputDocType() (using jResponseHtml, not jResponseBasicHtml). This method should echo a valid DOCTYPE.

The XHTML mode is also available with jResponseBasicHtml, but it will influence only HTTP headers. You should modify your template to indicate the right doctype.

Returning the content of a static page

jResponseBasicHtml allows to return simply a static page stored in a file. This file should be a simple PHP file, and not a template for jTpl. It should contain all the HTML code of a page, and three instructions to display these variables:

  • $HEADTOP : after the opening tag <head>
  • $HEADBOTTOM : before the end tag </head>
  • $BODYTOP : after the opening tag <body>
  • $BODYBOTTOM : before the end tag </body>

These variables contain HTML code given by the call of methods addHeadContent() and addContent() of the object jResponseBasicHtml. These calls may be made by some components like controllers, classes, plugins... So you really should include these instructions.

There is also an other variable, $BASEPATH, containing the base path of the application, and that could be useful for your style sheets or other static resources.

Here is an example of a such static file:


<!DOCTYPE html>
<html lang="en_US">
  <head>
     <?php echo $HEADTOP; ?>
     <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
      <title>Hello From Jelix !</title>
      <link type="text/css" href="<?php echo $BASEPATH; ?>mystyles.css" rel="stylesheet" />
      <?php echo $HEADBOTTOM; ?>
  </head>
  <body>
      <?php echo $BODYTOP; ?>
     <h1>Hello YOU !</h1>
     <p>This is a sample html page generated by Jelix</p>
      <?php echo $BODYBOTTOM; ?>
  </body>
</html>

In your controller, you must indicate where can jResponseBasicHtml find this file, by giving its full path in the property $htmlFile. Note that the object jCoordinator owns a method allowing to retrieve the path of a module. So it is easy to give the path of a file stored in a module.


   $rep = $this->getResponse('basichtml');
   $rep->htmlFile = jApp::coord()->getModulePath('unModule'). 'static/hello.php';

Generating dynamic contents

To return dynamic content, you have to use the class jResponseHtml, corresponding to the response type "html". This class implements additional methods and properties to manipulate the HTML/XHTML content. So it is more flexible for all consumers components (zones, templates, controllers..).

The source of an HTML page is split into two parts: the <head> part, and the <body> part.

jResponseHTML generates itself the content of the <head> element, from data you give to the jResponseHTML via its dedicated methods. However, you are responsible of the generation of the content of the <body> element. Let's see all of these things.

Generating (X)HTML headers

jResponseHtml provides features to change many things into the <head> of the document: title, favicon, links to style sheets or JS scripts, meta tags...

Features tour:



$resp->title = 'Document Title';

// generate a script tag
$resp->addJSLink( '/lib.js' );

// generate a script tag with inline code :
$resp->addJSCode( 'alert( "Hello world" ) )' );

// generate a <link>
$resp->addCSSLink('/style.css');

// generate a <style>
$resp->addStyle('span', 'font-weight:bold;');

// Add a description meta tag:
$resp->addMetaDescription( 'description' );

// Add a keywords meta tag:
$resp->addMetaKeywords( 'jelix php framework' );

// Add the author:
$resp->addMetaAuthor('Laurent');

// change the meta generator name
$resp->addMetaGenerator('My Super App 1.0');

// set the compatibility mode for IE
$resp->IECompatibilityMode = 'IE=Edge'; // default value

// set the viewport 
$resp->metaViewport = 'width=device-width, initial-scale=1.0';

// add a meta element
$resp->addMeta(array('name'=>'apple-touch-fullscreen', 'content'=>'yes'));

If you want to add custom content in the <head> element, use addHeadContent():


$resp->addHeadContent('<link rel="alternate" type="application/rss+xml" title="Recent Changes" href="/feed.php" />')

It happens to require removal of parts of the generated headers (if you refuse to take the hand on third-party-module). You should use method clearHtmlHeader() for this:


// remove links to stylesheets done with addCSSLink(), and style tags from addStyle()
$resp->clearHtmlHeader( array( 'CSSLink', 'Styles' ) );

Acceptable array values are strings 'CSSLink', 'Styles', 'JSLink', 'JSCode', 'Other'.

Generate (X)HTML body

jResponseHtml generates the (X)HTML <body> tag, but you must control its contents and attributes.

To control its attributes:


$resp->bodyTagAttributes = array( 'onload'=>'bodyonload( )',
                                   'class'=>'maincontent');

Control its contents with either:

  • a template
  • method addContent( )

Using a template

jResponseHtml provides a couple of properties:

  • $bodyTpl, which value should be the template selector;
  • body, which contains a jTpl instance allowing control over template "settings".

For example:


$resp->bodyTpl = 'myapp~main';
$resp->body->assign( 'person','Guybrush Treepwood');

Contents generated by the template engine will be integrated in the (X)HTML body tags automatically.

To know more about templates, read chapter about templates.

It happens to require appending some arbitrary contents in addition to the (X)HTML body code generated with method addContent():

  • first argument is the string value to append to the (X)HTML body contents,
  • second argument is a bool about the order :
    • true if the first argument's string value should be placed before the template
    • false if it should be placed after the template.

Note that it's set to false by default.

Example:


$resp->addContent( 'This text will be placed after the template' );
$resp->addContent( 'This text will be placed before the template', true);

It's possible to use this method to append contents from "zones", for example:


$resp->addContent( jZone::get( 'aModule~aZone'));

Using a main template and some "sub-templates"

We often have a common template for all pages, and only few things change inside this common template, depending of the page. So you will have a first template for the common things, and all pages will define an other "sub-template" to generate specific things for the page. The result of this sub-template will be inserted into the main template.

You can do this work directly into all your controllers, with zones.

But there is a more convenient way to do it: you can define an object which inherits from jResponseHtml. In this object, you will define all common things. And then you declare this object as the default HTML view for all your actions.

Read the section: how to create a custom common response.

Not using templates

Let bodyTpl empty if you don't want to use a template for the (X)HTML body of the response, and use addContents() as described in the previous paragraph of the documentation.


$rep->addContent('<p>content for my body</p>');

Adding CSS style sheets, javascript files, images...

Using webassets

You could declare CSS and JS links into your response object, into your controllers or into your templates (see below, "adding js and css links"). But you have an other solution: you can declare list of css and JS links into the configuration, into "groups" of assets, and then you declare into your response object or elsewhere, the group of assets to use.

It avoids to declare same CSS and JS links in several files, but it allows too to change CSS and JS links without to modify the code of modules. It is particulary useful when you are using vendor modules you cannot modify or you don't want to modify.

In the webassets_common section (or any other webassets_* sections, see webassets collections), you can define groups of assets.

A group is often corresponding to a list of CSS and JS links used by a specific component. There are existing following groups:

  jquery, jquery_ui, jforms_html, jforms_html_light, jforms_datepicker_default,
  jforms_datetimepicker_default, jforms_htmleditor_default, jforms_wikieditor_default,
  swjs

To declare JS files of a group, you have to use the name of the group, following by .js. To declare CSS files, use the suffix .css.

For example, to declare CSS and JS links for the group jforms_html_light:


jforms_html_light.js= "/jelix/js/jforms_light.js"
jforms_html_light.css= "/jelix/design/jform.css"

Here the group jforms_html_light contain two assets: /jelix/js/jforms_light.js and /jelix/design/jform.css.

If you have several assets, you can list them by separating them by a comma, or by using the array notation of the ini format:


mygroup1.js= "js/foo.js, js/bar.js"

mygroup2.js[]= "js/bla.js"
mygroup2.js[]= "js/baz.js"

The path you indicate for JS and CSS links, can be:

  1. an absolute path, starting with /, or a full URL (starting with http:// )
  2. a path relative to the base path (so it doesn't start with /)
  3. a path starting with $theme/, where $theme will be replaced by a path like <basepath>/themes/<current_theme>/
  4. a path starting with $jelix/, where $jelix will be replaced by the path of the jelix-www directory.
  5. a selector of a module action: <module>~<controller>:<method>. This action should return a JS or CSS content.
  6. a module name, followed by : and then by a path. This path should correspond to a file inside the www/ directory of the indicated module.

Path like 1) to 4) can contain also some variables, $locale or $lang, that will be replaced respectively by the current locale code (xx_YY) and the current language code (xx).

Example:


example.js[]= "/absolute/path.js"
example.js[]= "http://my.site/absolute/path.js"
example.js[]= related/to/basepath
example.js[]= "module:path/to/file.js, module~ctrl:meth"
example.js[]= "$theme/path/to/file.js, path/$lang/machin.js, /$locale/truc.js"

You can also indicate some attributes which will be added to the script or link element, like defer or media attributes:


example.js[]= "myscript.js|defer"
example.js[]= "https://popular.com/script.js.js|defer|integrity=sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K|crossorigin=anonymous"
example.js[]= "mymodule.mjs|type=module"
example.css[]= "mystyle.css|media=screen and (max-width: 600px)"
example.css[]= "fancy.css|rel=alternate stylesheet|title=Fancy"

Sometimes you may need to include a group when using an other group. For example, the assets group "jforms_html" needs to include also the group "jquery". You can indicate this dependency into the configuration instead of indicating the use of the group jquery in your PHP code each time you use the jforms_html group.

You have to kind of dependencies: require and include. And you indicate the dependencies by using the group name followed by the type of dependency:


example1.js = ex1.js
example2.js = ex2.js
example2.css = ex2.css
example3.js = ex3.js
example3.css = ex3.css

example.require = example1, example2
example.include = example3
example.js = foo.js
example.css = foo.css

.require allows to include given groups before the declaration of JS and CSS files of the group into the HTML. Whereas .include allows to include given groups after the assets of the group.

In the previous example, the list of <script> and <links elements into the HTML will be:


<link type="text/css" href="/ex2.css" rel="stylesheet" />
<link type="text/css" href="/foo.css" rel="stylesheet" />
<link type="text/css" href="/ex3.css" rel="stylesheet" />

<script src="/ex1.js"/>
<script src="/ex2.js"/>
<script src="/foo.js"/>
<script src="/ex3.js"/>

API to add webassets

Now that groups are declared, you have to indicate them into your controllers, templates or html response objects.

You have the method jResponseHtml::addAssets(), to which you give the name of the group:


$rep = $this->getResponse('html');
$rep->addAssets('example');

In a template, you will use the assets parameter to meta_html:


{meta_html assets 'example'}

In your installation scripts, you can declare assets groups, with these methods:

  1. $this->declareGlobalWebAssets() if you want to modify the global configuration
  2. declareWebAssets() on the entry point object, to declare the group on the entry point configuration.

$this->declareGlobalWebAssets(
    $groupName, 
    array( // assets
        'js'=>array('foo/bar.js', 'mymodule:baz.js'),
        'css'=> ..
        'require' => ...
        'include' => ...
    ),
    'common', // collection name
    false, // true to delete existing values
);

Webassets collection

A collection contains all webassets groups dedicated to an environment or a theme for example. For instance, if you debug an application, the list of assets will probably be different than the list of assets for the production environment, because you may not concatenate or compress JS files etc..

There is a common collection, containing all assets groups that are common to all collections. You can redefine assets and their groups into each collections.

Only one collection is usable at the same time, and its name is indicated in the useCollection parameter of the section webassets. The default value is common.

Each collection is defined in a section webassets_<name>. For the collection common, it is the section webassets_common.

You probably saw above an example about the use of addJSLink() and addCSSLink(). These methods allow you to link some CSS style sheets and javascript files to your HTML page. Even if the use of webassets is highly recommended, these methods are useful for JS/CSS added in a particular context.

addJSLink() and addCSSLink() accept as first argument the URL of a JS file or CSS file. Avoid to indicate relative URL, because it will be relative to URL of the page, and since you cannot guess the URL of the page, the resulting URL of the JS/CSS may not be the expected one. So you should indicate an absolute URL (most of time without the domain name).

For example, if you execute $rep->addCSSLink('css/styles.css') in the controller of the page http://exemple.com/foo/bar/, the URL calculated by the browser will be http://exemple.com/foo/bar/css/styles.css, whereas for the page http://exemple.com/foo/bar (note the missing slash) or http://exemple.com/foo/, the result will be http://exemple.com/foo/css/styles.css.

If you give an absolute URL, like $rep->addCSSLink('/css/styles.css') (see the leading slash), the browser will understand http://exemple.com/css/styles.css, whatever the URL of the current page.

So it is recommended to indicate an absolute URL. However, you should take care about the base path. It is important if you don't know the production environment, or if you create a module that can be used by several applications. You retrieve the base path with jApp::urlBasePath(). So your code should be:


  $resp->addCSSLink(jApp::urlBasePath().'css/mystyles.css');

instead of


  $resp->addCSSLink('/css/mystyles.css');

If you want a link to a CSS/JS file of the lib/jelix-www/ directory, you can use the jApp::urlJelixWWWPath() method:


  $resp->addJSLink(jApp::urlJelixWWWPath().'js/tooltip.js');

If the CSS/JS file is stored into a module, then you should use addJSLinkModule() or addCSSLinkModule(). Arguments to these methods are the module name, the path of the resource inside the www/ directory of the module.

For example, if you want a link to the file mymodule/www/css/mystyles.css:


  $resp->addCSSLinkModule('mymodule', 'css/mystyles.css');

Note that addCSSLink(), addJSLink(), addJSLinkModule() and addCSSLinkModule() can use an associative array argument specifying custom (X)HTML tag properties, for example:


$resp->addCSSLink( 
    '/stylesheet.css', 
    array( 
        'title' => 'blue design',
        'rel'   => 'alternate stylesheet',
    )
);

All previous example about the retrieval of URLs of CSS files, work with JS files, images files and any other ressources stored into the www/ directory of an application, a module or into the jelix-www directory.

You can also use same principle directly in a template:


  {* in the www/ of the application *}
  {meta_html css '/css/mystyles.css'}
  {meta_html css $j_basepath.'css/mystyles.css'}
  
  {* in the jelix-www/ *}
  {meta_html js  $j_jelixwww.'js/tooltip.js'}

  {* in the www/ of a module *}
  {meta_htmlmodule css 'mymodule', 'css/mystyles.css'}

  {* link to an image into myapp/mymodule/www/img/mypicture.png *}  
  <img src="{jurl 'jelix~www:getfile', array('targetmodule=>'mymodule', 'file'=>'img/mypicture.png')}" />

See chapter about templates to know more about these features.

Forcing update of web assets into browsers

For performance reasons, browsers have agressive cache policy with JS and CSS files. Even if files are updated, browser doesn't reload them in all case. So the user has to force the browser to reload files with shift+reload button for example. This is not really user friendly, and they often don't know this feature.

A modern solution is to use a Service Worker, which allows the developer to implement a cache policy for the assets of his application.

An other solution, more classical and which doesn't require Javascript, is to have a different URL for each version of the JS/CSS file. This can be a file renamed at each version. It could be done "easily" if the files are generated by a tool like WebPack. But the new filename should be set correctly in the webassets configuration of Jelix, at the same time.

The URL change could be done by appending a random query parameter, which should be different at least each time the file change. This is the solution that Jelix propose since version 1.7.11.

So, for example, when we add the url /js/foo.js (via webassets or with addJsLink() etc), the result will be <script src="/js/foo.js?_r=20230311144752"/>. It adds a "revision" number. The value of _r depends on the configuration.

The configuration parameter assetsRevision from the section [urlengine] allows to control the adding of this url parameter (which is _r by default) and its value.

  • assetsRevision="": the value is empty, and then no parameter _r will be added. You should manage the browser cache yourself, if you desire it, with an other solution, like thos described above.
  • assetsRevision=autoconfig: the revision value of _r will be regenerated during each compilation of the application configuration, (so after each cleaning of temp/) ans will be the datetime of the compilation.
  • assetsRevision has any other value: this will be the value of @@_r@. So this is a value you've choosen (or choose by a build script or else). Modifying the value assetsRevision is then your responsability, and should be done after each deployment for example.

If you want to change the parameter name, indicate the new name into assetsRevisionParameter.

Example:


[urlengine]
assetsRevision=autoconfig
assetsRevisionParameter=v

Note that the revision query parameter is not added on full url, so on url starting with http:// or https://.

If you don't want to add the revision parameter to some specific url, the only way is to declare the corresponding <link> or <script> with the method addHeadContent() of jResponseHtml.

Other response parameters

It's possible to control HTTP headers since jResponseHtml and jResponseBasicHtml inherit from jResponse, for the status code as well as for miscellaneous properties.


$resp->setHttpStatus( '404', 'Not Found' );
$resp->addHttpHeader( 'Date-modified', '...' );

If jResponseHtml's property $xhtmlContentType is true: HTTP response's "Content-Type" will be application/xhtml+xml. Note that the client-browser must support xHTML, or Content-Type text/html is sent.


$resp->xhtmlContentType = true ;

Using plugins

jResponseHtml, jResponseHtml4 and jResponseBasicHtml have a plugin system. These plugins allow to add content or modify the response content automatically, on all HTML pages generated with these classes. Thus there is such plugins to show a debug bar, or to "minify" css style sheets or javascript files.

These plugins have the plugin type "htmlresponse", so they have to be stored in a htmlresponse/ directory inside a plugins repository. To activate them, you need to specify their name in the option plugins in the section jResponseHtml of the main configuration. Example:


[jResponseHtml]
plugins = debugbar, minify

Minifying css and javascript files

Nowadays, concatenation and minification of CSS/JS files are made with external tools like WebPack, Grunt.. In this case, you have to indicate the path of the resulting files into webassets or to methods add*Link().

But probably you would prefer to do these processes on the fly. Jelix propose a module dedicated for it, using the library Minify, that you have to install into your application. This is the module jMinify. You can install it with Composer, by using the package name "jelix/minify-module". See the documentation on Github.

Note: the features of the jMinify module were integrated into Jelix 1.6 and lower.