Section: Generating HTML5 content
^ Responses: generating content | Generating plain text » |
− Table of content
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 templatefalse
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:
- an absolute path, starting with
/
, or a full URL (starting withhttp://
) - a path relative to the base path (so it doesn't start with
/
) - a path starting with
$theme/
, where$theme
will be replaced by a path like<basepath>/themes/<current_theme>/
- a path starting with
$jelix/
, where$jelix
will be replaced by the path of the jelix-www directory. - a selector of a module action:
<module>~<controller>:<method>
. This action should return a JS or CSS content. - a module name, followed by
:
and then by a path. This path should correspond to a file inside thewww/
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:
$this->declareGlobalWebAssets()
if you want to modify the global configurationdeclareWebAssets()
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
.
Using specifics JS and CSS links ¶
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 valueassetsRevision
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.