Content structure
Create site19 by copying site18.
- /cms
- ...
- site18
- site19
In this chapter, we are going to program how the content of the site is managed and displayed.
To test the result online, enter http://www.frasq.org/cms/site19/en/node/1 in the address bar of your navigator. The site displays the content of node 1 in English. Enter http://www.frasq.org/cms/site19/fr/noeud/1 to display its translation in French.
A node has a title, a name, an abstract and a cloud of words associated to it. A node has a content, in this case, a text in HTML.
Display the source code of the page. Notice that the tag <meta name="description" />
contains the abstact of the node and that the tag <meta name="keywords" />
contains the cloud of words.
This information is normally exploited by search engines.
The name of a node will be used to designate it in a URL. Its title is generally displayed at the beginning of a page.
Start the command processor of MySQL and enter the site's DB:
$ mysql -u root -p
mysql> use frasqdb2;
NOTE: Use phpMyAdmin for more comfort.
Add the table node
then the table node_locale
to the DB of the site:
node_id INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
user_id INT( 10 ) UNSIGNED NOT NULL ,
created datetime NOT NULL ,
modified datetime NOT NULL ,
PRIMARY KEY ( node_id )
) ENGINE = MYISAM DEFAULT CHARSET = utf8;
node
contains the properties of a node which are in common for all languages: its identifier, the identifier of the user who has modified it for the last time, its creation and modification dates.
The primary key of a node
is its identifier.
node_id INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
locale enum( 'fr', 'en' ) NOT NULL DEFAULT 'fr',
name VARCHAR( 100 ) NOT NULL ,
title VARCHAR( 100 ) NOT NULL ,
abstract text,
cloud text,
PRIMARY KEY ( node_id , locale )
) ENGINE = MYISAM DEFAULT CHARSET = utf8;
A node_locale
contains the properties of a node which depend on a language: the designation of the language of the content, the name of the node, its title, the abstract and the list of words associated to the node.
The identifier of a node_locale
matches the identifier of a node
.
The designations of the languages must match the languages listed by the configuration parameter $supported_languages
defined in the file includes/config.inc.
The primary key of a node_locale
is its identifier and the designation of the language of its content.
content_id INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
locale enum( 'fr', 'en' ) NOT NULL DEFAULT 'fr',
text text,
PRIMARY KEY ( content_id , locale )
) ENGINE = MYISAM DEFAULT CHARSET = utf8;
A content_text
contains a content of type text
for a given language, usually in HTML.
The primary key of a content_text
is its identifier and the designation of the language of its content.
The link between a node and its contents is done by the table node_content
:
node_id INT( 10 ) UNSIGNED NOT NULL ,
content_id INT( 10 ) UNSIGNED NOT NULL ,
content_type enum( 'text' ) CHARACTER SET ascii NOT NULL DEFAULT 'text',
`number` INT( 3 ) UNSIGNED NOT NULL ,
PRIMARY KEY ( node_id , content_id , content_type )
) ENGINE = MYISAM DEFAULT CHARSET = utf8;
A node_content
lists and orders the contents of a node.
The identifier node_id
matches the identifier of a node
.
content_id
associated to a content_type
matches the identifier of a content in content_text
.
The primary key of a node_content
is the identifier of a node, the identifier of a content and its type.
Create a first node:
(1, 1, NOW(), NOW());
Node 1 is created by the user 1, the administrator of the site. Check in the table user
.
Add its properties in English:
(1, 'en', 'legal-information', 'Legal information', 'Author''s liability - Rights.', 'copyright liability author rights distribution');
Add the version in French:
(1, 'fr', 'informations-legales', 'Informations légales', 'Responsabilité de l''auteur - Droits.', 'copyright responsabilité auteur droits diffusion');
Add a content in English:
(1, 'en', '<p>The information, the techniques and the software ("CONTENT")\r\nprovided by this web site are for educational purposes only\r\nand should not be construed as a commitment by their author ("AUTHOR").</p>\r\n<p>The CONTENT is provided "AS IS", without warranty of any kind, express or implied,\r\nincluding but not limited to the warranties of merchantability,\r\nfitness for a particular purpose and noninfringment of third party rights.\r\nIn no event shall the AUTHOR be liable for any claim,\r\nor any special indirect or consequential damages,\r\nor any damages whatsoever resulting from loss of use, data or profits,\r\nwhether in an action of contract, negligence or other tortious action,\r\narising out of or in connection with the use or performance of the CONTENT.</p>');
Add its translation in French:
(1, 'fr', '<p>Les informations, les techniques et les logiciels ("CONTENU")\r\nfournis par ce site ont un but purement éducatif\r\net ne doivent pas être interprétés comme un engagement par leur auteur ("AUTEUR").</p>\r\n<p>Le CONTENU est fourni "EN L''ÉTAT" sans garantie d''aucune sorte, expresse ou implicite,\r\ny compris, mais sans limitation, les garanties de qualité marchande,\r\nd''adéquation à un usage particulier et de non-violation des droits de tierces parties.\r\nL''AUTEUR ne saurait en aucun cas être tenu responsable de toute réclamation\r\nou dommage indirect ou consécutif ou de tout autre dommage\r\nlié à la perte d''utilisation, de données ou de bénéfices,\r\nque ce soit dans le cadre d''un contrat, d''une négligence ou d''une autre action préjudiciable,\r\ndus ou liés à l''utilisation ou aux performances du CONTENU.</p>');
Note that the identifiers of the two versions are identical but that the language differ.
Create the association between the node and its content:
('1', '1', 'text', '1');
Associates node 1 to content 1 of type text and places it at position 1.
Try a few requests on the DB:
Checks that node 1 is actually created.
FROM node n
JOIN node_locale nloc ON nloc.node_id=n.node_id AND nloc.locale='fr'
WHERE n.node_id=1
LIMIT 1;
Returns all the attributes of the French version of node 1.
FROM node_content nc
JOIN content_text ct ON nc.content_type='text' AND ct.content_id=nc.content_id AND ct.locale='fr'
WHERE nc.node_id=1
ORDER BY `nc.number`;
Returns the content in French of node 1.
Add the file node.inc in the folder models with the following content:
- function node_id($node) {
- if (!is_numeric($node)) {
- return false;
- }
- $tabnode=db_prefix_table('node');
- $sql="SELECT node_id FROM $tabnode WHERE node_id=$node LIMIT 1";
- $r = db_query($sql);
- return $r ? $r[0]['node_id'] : false;
- }
node_id
returns the identifier of the node whose identifier is $node
, or false
if the node $node
doesn't exist.
- function node_get($lang, $node_id) {
- $sqllang=db_sql_arg($lang, false);
- $tabnode=db_prefix_table('node');
- $tabnodelocale=db_prefix_table('node_locale');
- $sql="SELECT nloc.name AS node_name, nloc.title AS node_title, nloc.abstract AS node_abstract, nloc.cloud AS node_cloud, n.user_id AS node_user, UNIX_TIMESTAMP(n.created) AS node_created, UNIX_TIMESTAMP(n.modified) AS node_modified FROM $tabnode n JOIN $tabnodelocale nloc ON nloc.node_id=n.node_id AND nloc.locale=$sqllang WHERE n.node_id=$node_id LIMIT 1";
- $r = db_query($sql);
- return $r ? $r[0] : false;
- }
node_get
returns all the attributes for the language $lang
of the node whose identifier is $node_id
, or false
in case of error.
Notice that the fields node_created
and node_modified
are converted to a Unix time.
- function node_get_contents($lang, $node_id) {
- $sqllang=db_sql_arg($lang, false);
- $tabnodecontent=db_prefix_table('node_content');
- $tabcontenttext=db_prefix_table('content_text');
- $sql="SELECT nc.content_id AS content_id, nc.content_type AS content_type, nc.number AS content_number, ct.text AS content_text FROM $tabnodecontent nc JOIN $tabcontenttext ct ON nc.content_type='text' AND ct.content_id=nc.content_id AND ct.locale=$sqllang WHERE nc.node_id=$node_id ORDER BY nc.number";
- $r = db_query($sql);
- return $r;
- }
node_get_contents
returns the content of the node $node_id
for the language $lang
, or false
in case of error.
Create the action node
by adding the file node.php in the folder actions with the following content:
- /cms/site19
- actions
- node.php
- actions
- require_once 'models/node.inc';
- function node($lang, $arglist=false) {
- $node=false;
- if (is_array($arglist)) {
- if (isset($arglist[0])) {
- $node=$arglist[0];
- }
- }
- if (!$node) {
- return run('error/notfound', $lang);
- }
- $node_id = node_id($node);
- if (!$node_id) {
- return run('error/notfound', $lang);
- }
- $r = node_get($lang, $node_id);
- if (!$r) {
- return run('error/notfound', $lang);
- }
- extract($r); /* node_name node_title node_abstract node_cloud */
- head('title', $node_id);
- head('description', $node_abstract);
- head('keywords', $node_cloud);
- head('robots', 'noindex, nofollow');
- $banner = build('banner', $lang);
- $node_contents = build('nodecontent', $lang, $node_id);
- $content = view('node', $lang, compact('node_name', 'node_title', 'node_abstract', 'node_cloud', 'node_contents'));
- $output = layout('standard', compact('banner', 'content'));
- return $output;
- }
The action node
returns a page displaying the content of a node built by the block nodecontent
.
node
takes one argument, a node number.
If the node number is missing or invalid, node
sends back an HTTP 404 Not Found error.
To give access to the node
action, add an alias for each language in the file includes/aliases.inc:
- 'node' => 'node',
- 'noeud' => 'node',
Add the view of a node in the folders views/en for the English version and views/fr for the version in French:
- /cms/site19
- views
- en
- node.phtml
- fr
- node.phtml
- en
- views
- <h3><?php echo htmlspecialchars($node_title, ENT_COMPAT, 'UTF-8'); ?></h3>
- <?php if ($node_abstract): ?>
- <h5>Abstract</h5>
- <p><?php echo htmlspecialchars($node_abstract, ENT_COMPAT, 'UTF-8'); ?></p>
- <?php endif; ?>
- <?php if ($node_cloud): ?>
- <h6>Cloud</h6>
- <p class="smaller"><?php echo htmlspecialchars($node_cloud, ENT_COMPAT, 'UTF-8'); ?></p>
- <?php endif; ?>
- <?php if ($node_contents): ?>
- <h4>Contents</h4>
- <?php echo $node_contents; ?>
- <?php endif; ?>
- <h3><?php echo htmlspecialchars($node_title, ENT_COMPAT, 'UTF-8'); ?></h3>
- <?php if ($node_abstract): ?>
- <h5>Extrait</h5>
- <p><?php echo htmlspecialchars($node_abstract, ENT_COMPAT, 'UTF-8'); ?></p>
- <?php endif; ?>
- <?php if ($node_cloud): ?>
- <h6>Nuage</h6>
- <p class="smaller"><?php echo htmlspecialchars($node_cloud, ENT_COMPAT, 'UTF-8'); ?></p>
- <?php endif; ?>
- <?php if ($node_contents): ?>
- <h4>Contenu</h4>
- <?php echo $node_contents; ?>
- <?php endif; ?>
Add the function nodecontent
which returns the content of a node:
- /cms/site19
- blocks
- nodecontent.php
- blocks
- require_once 'models/node.inc';
- function nodecontent($lang, $node_id) {
- $contents = array();
- $r = node_get_contents($lang, $node_id);
- if ($r) {
- foreach ($r as $c) { /* content_id content_number content_type content_text */
- $type=$c['content_type'];
- switch($type) {
- case 'text':
- $s=$c['content_text'];
- if (!empty($s)) {
- $text = $s;
- $contents[] = compact('type', 'text');
- }
- break;
- default:
- break;
- }
- }
- }
- $output = view('nodecontent', false, compact('contents'));
- return $output;
- }
Since the view of the block doesn't depend on the language, add it directly to the views folder:
- /cms/site19
- views
- nodecontent.phtml
- views
- <?php
- foreach ($contents as $c) {
- switch($c['type']) {
- case 'text':
- $text_content = $c['text'];
- echo $text_content, PHP_EOL;
- break;
- default:
- break;
- }
- }
- ?>
Enter http://localhost/cms/site19/en/node/1 then http://localhost/cms/site19/fr/noeud/1 in the address bar of your navigator.
Add a second content to the page:
(2, 'fr', '<p><i>13 mai 2010</i></p>\r\n<p><b>frasq.org</b></p>'),
(2, 'en', '<p><i>May 13, 2010</i></p>\r\n<p><b>frasq.org</b></p>');
('1', '2', 'text', '2');
Reload the pages Legal information and Informations légales.
Comments