Dear %username%,

I would like to share with you my first-hand experience in creating a Website on CppCMS (library-template engine on C++). It can also be named as “help for beginners on CppCMS”.

Why Would You Write a Website with C++

There are different pros and cons against this solution. So in order not to provoke a “language war”, I will draw an analogy with cars: “I bought one. I like it and don’t want to sell it!”. As an additional argument I will use the fact that this language is filed-specific for my job.

High Time to Write Something

But before that

Before we write a website, CppCMS should be installed on the working machine. The library requires Boost c++, pcre, crypt, python, icu for its operation. Despite the cross-platform it’s easier to set everything up under *nix systems. Building is quite trite:

mkdir build cd build cmake .. make make install

There should be no problems with it. Everything is built automatically and it has never let me down.

Looking ahead, I would like to say that development should desirably be able to execute user building steps and have a handily adjusted syntax analyzer. I use QtCreator. I am going to describe all my further steps with reference to the environment mentioned above, as the building by using the command line is well described at the library’s website. I would also like to mention that some building actions will be automated by bash scripts (though it would be enough to write a “user step” at the setup stage).

It’s better to add the syntax highlight for QtCreator before we begin. It will help to identify special *.tmpl files which are used as templates. The given file tmpl.xml (slightly modified HTML highlight) should be allocated in the config folder “qtcreator/generic-highlighter/tmpl.xml”:

<?xml version="1.0" encoding="UTF-8"?> <!ENTITY entref "&(#[0-9]+|#[xX][0-9A-Fa-f]+|&name;);"> ]> <language name="TMPL" version="1" kateversion="2.4" section="Markup" extensions="*.tmpl" mimetype="text/tmpl" author="Wilbert Berendsen ( original HTML author)(wilbert@kde.nl)" license="LGPL" priority="10"> <highlighting> <contexts> <context name="Start" attribute="Normal Text" lineEndContext="#stay"> <IncludeRules context="FindHTML" /> </context> <context name="FindHTML" attribute="Normal Text" lineEndContext="#stay"> <DetectSpaces/> <DetectIdentifier/> <StringDetect attribute="Comment" context="Comment" String="<!--" beginRegion="comment" /> <StringDetect attribute="Commenttmpl" context="Commenttmpl" String="<%" beginRegion="commenttmpl" /> <StringDetect attribute="CDATA" context="CDATA" String="<![CDATA[" beginRegion="cdata" /> <RegExpr attribute="Doctype" context="Doctype" String="<!DOCTYPE\s+" beginRegion="doctype" /> <RegExpr attribute="Processing Instruction" context="PI" String="<\?[\w:-]*" beginRegion="pi" /> <RegExpr attribute="Element" context="CSS" String="<style\b" insensitive="TRUE" beginRegion="style" /> <RegExpr attribute="Element" context="JS" String="<script\b" insensitive="TRUE" beginRegion="script" /> <RegExpr attribute="Element" context="El Open" String="<pre\b" insensitive="TRUE" beginRegion="pre" /> <RegExpr attribute="Element" context="El Open" String="<div\b" insensitive="TRUE" beginRegion="div" /> <RegExpr attribute="Element" context="El Open" String="<table\b" insensitive="TRUE" beginRegion="table" /> <RegExpr attribute="Element" context="El Open" String="<ul\b" insensitive="TRUE" beginRegion="ul" /> <RegExpr attribute="Element" context="El Open" String="<ol\b" insensitive="TRUE" beginRegion="ol" /> <RegExpr attribute="Element" context="El Open" String="<dl\b" insensitive="TRUE" beginRegion="dl" /> <RegExpr attribute="Element" context="El Open" String="<&name;" /> <RegExpr attribute="Element" context="El Close" String="</pre\b" insensitive="TRUE" endRegion="pre" /> <RegExpr attribute="Element" context="El Close" String="</div\b" insensitive="TRUE" endRegion="div" /> <RegExpr attribute="Element" context="El Close" String="</table\b" insensitive="TRUE" endRegion="table" /> <RegExpr attribute="Element" context="El Close" String="</ul\b" insensitive="TRUE" endRegion="ul" /> <RegExpr attribute="Element" context="El Close" String="</ol\b" insensitive="TRUE" endRegion="ol" /> <RegExpr attribute="Element" context="El Close" String="</dl\b" insensitive="TRUE" endRegion="dl" /> <RegExpr attribute="Element" context="El Close" String="</&name;" /> <!-- as long as kde gives DTDs the text/html mimetype--><IncludeRules context="FindDTDRules" /> <IncludeRules context="FindEntityRefs" /> </context> <context name="FindEntityRefs" attribute="Other Text" lineEndContext="#stay"> <StringDetect attribute="Commenttmpl" context="Commenttmpl" String="<%" beginRegion="commenttmpl" /> <RegExpr attribute="EntityRef" context="#stay" String="&entref;" /> <AnyChar attribute="Error" context="#stay" String="&<" /> </context> <context name="FindPEntityRefs" attribute="Other Text" lineEndContext="#stay"> <RegExpr attribute="EntityRef" context="#stay" String="&entref;" /> <RegExpr attribute="PEntityRef" context="#stay" String="%&name;;" /> <AnyChar attribute="Error" context="#stay" String="&%" /> </context> <context name="FindAttributes" attribute="Other Text" lineEndContext="#stay"> <RegExpr attribute="Attribute" context="#stay" String="&name;" column="0"/> <RegExpr attribute="Attribute" context="#stay" String="\s+&name;" /> <DetectChar attribute="Attribute" context="Value" char="=" /> </context> <context name="FindDTDRules" attribute="Other Text" lineEndContext="#stay"> <RegExpr attribute="Doctype" context="Doctype Markupdecl" String="<!(ELEMENT|ENTITY|ATTLIST|NOTATION)\b" /> </context> <context name="Comment" attribute="Comment" lineEndContext="#stay"> <DetectSpaces/> <IncludeRules context="##Alerts" /> <DetectIdentifier/> <StringDetect attribute="Comment" context="#pop" String="-->" endRegion="comment" /> <RegExpr attribute="Error" context="#stay" String="-(-(?!->))+" /> </context> <context name="Commenttmpl" attribute="Commenttmpl" lineEndContext="#stay"> <DetectSpaces/> <DetectIdentifier/> <StringDetect attribute="Commenttmpl" context="#pop" String="%>" endRegion="commenttmpl" /> </context> <context name="CDATA" attribute="Other Text" lineEndContext="#stay"> <DetectSpaces/> <DetectIdentifier/> <StringDetect attribute="CDATA" context="#pop" String="]]>" endRegion="cdata" /> <StringDetect attribute="EntityRef" context="#stay" String="]]>" /> </context> <context name="PI" attribute="Other Text" lineEndContext="#stay"> <Detect2Chars attribute="Processing Instruction" context="#pop" char="?" char1=">" endRegion="pi" /> </context> <context name="Doctype" attribute="Other Text" lineEndContext="#stay"> <DetectChar attribute="Doctype" context="#pop" char=">" endRegion="doctype" /> <DetectChar attribute="Doctype" context="Doctype Internal Subset" char="[" beginRegion="int_subset" /> </context> <context name="Doctype Internal Subset" attribute="Other Text" lineEndContext="#stay"> <DetectChar attribute="Doctype" context="#pop" char="]" endRegion="int_subset" /> <IncludeRules context="FindDTDRules" /> <StringDetect attribute="Comment" context="Comment" String="<!--" beginRegion="comment" /> <RegExpr attribute="Processing Instruction" context="PI" String="<\?[\w:-]*" beginRegion="pi" /> <IncludeRules context="FindPEntityRefs" /> </context> <context name="Doctype Markupdecl" attribute="Other Text" lineEndContext="#stay"> <DetectChar attribute="Doctype" context="#pop" char=">" /> <DetectChar attribute="Value" context="Doctype Markupdecl DQ" char=""" /> <DetectChar attribute="Value" context="Doctype Markupdecl SQ" char="'" /> </context> <context name="Doctype Markupdecl DQ" attribute="Value" lineEndContext="#stay"> <DetectChar attribute="Value" context="#pop" char=""" /> <IncludeRules context="FindPEntityRefs" /> </context> <context name="Doctype Markupdecl SQ" attribute="Value" lineEndContext="#stay"> <DetectChar attribute="Value" context="#pop" char="'" /> <IncludeRules context="FindPEntityRefs" /> </context> <context name="El Open" attribute="Other Text" lineEndContext="#stay"> <Detect2Chars attribute="Element" context="#pop" char="/" char1=">" /> <DetectChar attribute="Element" context="#pop" char=">" /> <IncludeRules context="FindAttributes" /> <RegExpr attribute="Error" context="#stay" String="\S" /> </context> <context name="El Close" attribute="Other Text" lineEndContext="#stay"> <DetectChar attribute="Element" context="#pop" char=">" /> <RegExpr attribute="Error" context="#stay" String="\S" /> </context> <context name="El Close 2" attribute="Other Text" lineEndContext="#stay"> <DetectChar attribute="Element" context="#pop#pop#pop" char=">" /> <RegExpr attribute="Error" context="#stay" String="\S" /> </context> <context name="El Close 3" attribute="Other Text" lineEndContext="#stay"> <DetectChar attribute="Element" context="#pop#pop#pop#pop" char=">" /> <RegExpr attribute="Error" context="#stay" String="\S" /> </context> <context name="CSS" attribute="Other Text" lineEndContext="#stay"> <Detect2Chars attribute="Element" context="#pop" char="/" char1=">" endRegion="style" /> <DetectChar attribute="Element" context="CSS content" char=">" /> <IncludeRules context="FindAttributes" /> <RegExpr attribute="Error" context="#stay" String="\S" /> </context> <context name="CSS content" attribute="Other Text" lineEndContext="#stay"> <RegExpr attribute="Element" context="El Close 2" String="</style\b" insensitive="TRUE" endRegion="style" /> <IncludeRules context="##CSS" includeAttrib="true"/> </context> <context name="JS" attribute="Other Text" lineEndContext="#stay"> <Detect2Chars attribute="Element" context="#pop" char="/" char1=">" endRegion="script" /> <DetectChar attribute="Element" context="JS content" char=">" /> <IncludeRules context="FindAttributes" /> <RegExpr attribute="Error" context="#stay" String="\S" /> </context> <context name="JS content" attribute="Other Text" lineEndContext="#stay"> <RegExpr attribute="Element" context="El Close 2" String="</script\b" insensitive="TRUE" endRegion="script" /> <RegExpr attribute="Comment" context="JS comment close" String="//(?=.*</script\b)" insensitive="TRUE" /> <IncludeRules context="##JavaScript" includeAttrib="true"/> </context> <context name="JS comment close" attribute="Comment" lineEndContext="#pop"> <RegExpr attribute="Element" context="El Close 3" String="</script\b" insensitive="TRUE" endRegion="script" /> <IncludeRules context="##Alerts" /> </context> <context name="Value" attribute="Other Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="Value NQ"> <DetectChar attribute="Value" context="Value DQ" char=""" /> <DetectChar attribute="Value" context="Value SQ" char="'" /> <DetectSpaces /> </context> <context name="Value NQ" attribute="Other Text" lineEndContext="#pop#pop" fallthrough="true" fallthroughContext="#pop#pop"> <IncludeRules context="FindEntityRefs" /> <RegExpr attribute="Value" context="#stay" String="/(?!>)" /> <RegExpr attribute="Value" context="#stay" String="[^/><"'\s]" /> </context> <context name="Value DQ" attribute="Value" lineEndContext="#stay"> <DetectChar attribute="Value" context="#pop#pop" char=""" /> <IncludeRules context="FindEntityRefs" /> </context> <context name="Value SQ" attribute="Value" lineEndContext="#stay"> <DetectChar attribute="Value" context="#pop#pop" char="'" /> <IncludeRules context="FindEntityRefs" /> </context> </contexts> <itemDatas> <itemData name="Normal Text" defStyleNum="dsNormal" /> <itemData name="Other Text" defStyleNum="dsNormal" spellChecking="false" /> <itemData name="Comment" defStyleNum="dsComment" /> <itemData name="Commenttmpl" defStyleNum="dsComment" color="#66f" /> <itemData name="CDATA" defStyleNum="dsBaseN" bold="1" spellChecking="false" /> <itemData name="Processing Instruction" defStyleNum="dsKeyword" spellChecking="false" /> <itemData name="Doctype" defStyleNum="dsDataType" bold="1" spellChecking="false" /> <itemData name="Element" defStyleNum="dsKeyword" spellChecking="false" /> <itemData name="Attribute" defStyleNum="dsOthers" spellChecking="false" /> <itemData name="Value" defStyleNum="dsString" color="#a00" spellChecking="false" /> <itemData name="EntityRef" defStyleNum="dsDecVal" spellChecking="false" /> <itemData name="PEntityRef" defStyleNum="dsDecVal" spellChecking="false" /> <itemData name="Error" defStyleNum="dsError" spellChecking="false" /> </itemDatas> </highlighting> <general> <comments> <comment name="multiLine" start="<!--" end="-->" /> </comments> </general> </language>

Now Let’s Start

Depending on the built, the following should be added:

LIBS += -L/usr/local/lib/ -lbooster -lcppcms INCLUDEPATH += /usr/local/include DEPENDPATH += /usr/local/include

Create main.cpp file and fill it with the following content:

#include <cppcms/applications_pool.h> #include <cppcms/url_dispatcher.h> #include <cppcms/http_response.h> #include <cppcms/application.h> #include <cppcms/url_mapper.h> #include <cppcms/service.h> //------------------------------------------------------------------------------------- // Dsc: Our class for page rendering, when some address is requested by the user // First of all he will get here //------------------------------------------------------------------------------------- class WebSite : public cppcms::application{ public: //------------------------------------------------------------------------------------- // Constructor. Entry point. //------------------------------------------------------------------------------------- WebSite(cppcms::service &s) : cppcms::application(s) {} //------------------------------------------------------------------------------------- // Dsc: The function we’ll get into if it’s not indicated otherwise in the constructor. // (more about it later) //------------------------------------------------------------------------------------- virtual void main(std::string path) { response().out() << "Hello!"; } }; //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- int main(int argc,char **argv) { try { // create the service cppcms::service srv(argc,argv); // define the root srv.applications_pool().mount(cppcms::applications_factory<WebSite>()); // run srv.run(); } catch(std::exception const &e) { std::cerr << "Failed: " << e.what() << std::endl; std::cerr << booster::trace(e) << std::endl; return 1; } return 0; }

If you have already tried to run it, you most likely failed. Configuration file for the server is lacking for full-fledged operation. Its location should be passed at start to the binary file. WebApp.bin -c config.json Here is an example of the config file:

{ "WebSite" : { "root" : "", "host" : "localhost:8080", "locdomain" : "localhost", }, "service" : { "ip" : "0.0.0.0", "api" : "http", "port" : 8080 }, "http" : { "script" : "/mb.fcgi" , "rewrite" : [ { "regex" : ".*" , "pattern" : "/mb.fcgi$0" } ], } }

This should be enough. You could also write startup options in “configs of the programming environment”. So add it, then start.

Impressed? No? Of course you aren’t as we haven’t used template mechanism for this example. We just put the string out. But the example lets us make sure that everything works.

Let’s use templates

Let’s write the first template which will be “transformed” by the template engine of the library into *.cpp file.

First of all, we need to add a header file which contains the structure of dynamic data (template data). They will be located in the data folder within the project by default. data/tmpl_master.h

#ifndef TMPL_MASTER_H #define TMPL_MASTER_H #include <cppcms/view.h> namespace Data { //------------------------------------------------------------------------------------- // Dsc: Basic page information //------------------------------------------------------------------------------------- struct infoPage { std::string title; // page title std::string description; // page description std::string keywords; // page key words std::map<std::string,std::string> menuList; // list of menu items output (url,desc) //------------------------------------------------------------------------------------- // Dsc: Constructor //------------------------------------------------------------------------------------- infoPage() : title (""), description(""), keywords (""), menuList ( ) {} //------------------------------------------------------------------------------------- // Dsc: Destructor, doing nothing //------------------------------------------------------------------------------------- ~infoPage(){} }; //------------------------------------------------------------------------------------- // Dsc: Basic content which exists on every page //------------------------------------------------------------------------------------- struct Master :public cppcms::base_content { infoPage page; //------------------------------------------------------------------------------------- // Dsc: Page's constructor //------------------------------------------------------------------------------------- Master() : page() {} //------------------------------------------------------------------------------------- // Dsc: Lazy destructor //------------------------------------------------------------------------------------- ~Master(){} }; } #endif

As a rule, the content of the given file isn’t notable for some smart code. They’re just containers for description of the variables, which are used in templates.

Let’s describe the template, then create templates folder and master.tmpl in it with the following content:

<% c++ #include "data/tmpl_master.h" %> <% skin defskin %> <% view Master uses Data::Master %> <% template page_main() %>MAIN TEMPLATE<% end %> <% template page_footer() %>Все права защищены<% end %> <% template page_left_sidebar() %>Левая панелька<% end %> <% template render() %> <html> <head> <meta charset="utf-8" /> <title><%= page.title %></title> <meta name="keywords" content="<%= page.keywords %>" /> <meta name="description" content="<%= page.description %>" /> <link href="/media/css/style.css" rel="stylesheet"> </head> <body> <div class="wrapper"> <header class="header"> <div class="nav" > <% foreach menuItem in page.menuList %> <ul> <% item %> <li><a href="<%= menuItem.first %>"><%= menuItem.second %></a></li> <% end %> </ul> <% end %> </div> </header> <div class="middle"> <div class="container"> <main class="content"><% include page_main() %></main> </div> <aside class="left-sidebar"> <div> <% include page_left_sidebar() %> </div> </aside> </div> </div> <footer class="footer"><% include page_footer() %></footer> </body> </html> <% end template %> <% end view %> <% end skin %>

So what is written here?

In the very first line <% c++ #include «data/tmpl_master.h» %> the header file is written. Data structures will be declared in it.

<% skin defskin %> line defines the current skin name, so you can have different page skins.

<% view Master uses Data::Master %> line defines the current template name as “Master” (it will be further indicated for page filling mechanism) and also creates Data::Master structure inside the wrapper class. In C++ it will look like “Data::Master context;” (if you are interested in details, you can always look at the generated file).

<% template page_main() %>MAIN TEMPLATE<% end %> <% template page_footer() %>All rights reserved<% end %> <% template page_left_sidebar() %>Left panel<% end %> lines define default values which will be derived to the user unless we override them (i.e. they are virtual const char* page_main(){ return "MAIN TEMPLATE"; } )

Now let’s try to build. Of course C++ compiler won’t handle the entire tmpl file. That’s why we need a utility which will, together with the library, process the template to the needed state. We’ll create “make_templates.sh” within the project. Inside the file the necessary operations will be located. The given file can be easily replaced either by manual call of this utility or by registering it at the “executed part” of the environment:

#!/bin/bash INPUT="" OUTPUT="" while getopts ":i:o:" opt; do case $opt in i) INPUT=$OPTARG ;; o) OUTPUT=$OPTARG ;; \?) echo "Invalid option: -$OPTARG" >&2 exit 1 ;; :) echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esac done # copy the configuration file to the build folder cp $INPUT/config.json $OUTPUT # write all templates here TEMPLATES="$INPUT/templates/master.tmpl" # process templates to срр cppcms_tmpl_cc $TEMPLATES -o $INPUT/all_tmpl.cpp # collect templates to the library g++ -shared -fPIC $INPUT/all_tmpl.cpp -o $OUTPUT/libcpp_defskin.so -lcppcms -lbooster

Now we should add a “user step” in the settings of QtCreator project Command: “./make_templates.sh” Working dir: “%{sourceDir}” Command arguments: “-i %{sourceDir} -o %{buildDir}” Don’t forget to add “execution” to the file (chmod +x make_template.sh)

If the build went well, libcpp_defskin.so library will appear at the building directory. The library can be built either statically or dynamically. I made it dynamically and wouldn’t recommend you to do statically as TMPL files should be often changed. Recompiling the project because of that is quite a thankless task.

config.json file should be altered in order to tie templates to the project

{ "WebSite" : { "root" : "", "host" : "localhost:8080", "locdomain" : "localhost", }, "service" : { "ip" : "0.0.0.0", "api" : "http", "port" : 8080 }, "http" : { "script" : "/mb.fcgi" , "rewrite" : [ { "regex" : "/media(/.+)", "pattern" : "$1" }, { "regex" : ".*" , "pattern" : "/mb.fcgi$0" } ], }, "views" : { "default_skin" : "defskin" , "paths" : [ "./" ], "skins" : [ "cpp_defskin" ], }, }

Appropriate changes should be made in main.cpp:

#include "data/tmpl_master.h" ... WebSite::main(std::string path) { Data::Master tmpl; tmpl.page.title = path; tmpl.page.description = "description"; tmpl.page.keywords = "keywords"; tmpl.page.menuList.insert(std::pair<std::string,std::string>("/","MAIN")); tmpl.page.menuList.insert(std::pair<std::string,std::string>("/else","ELSE")); render("Master",tmpl); }

We should see the template output when we start the project. Oops, I forgot to say about css and images. Add one more point to config.json

"file_server" : { "enable" : true, "listing" : true, "document_root" : "./media" },

I should explain that by the given point we allow the binary file to see the file system. The rules according to which it performs it are described in http{ «regex»: “/media(/.+)”, «pattern»: “$1” } section. So any request beginning with /media/ should be addressed to the “file server”. Let’s create a «media» folder in our project and also add an appropriate change to make_templates.sh:

# copy the media data to the build folder cp -R $INPUT/media $OUTPUT

Inside the «media» folder (in the project’s source codes directory) create css subfolder and style.css file in it.

/* Eric Meyer's CSS Reset */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } /* End of Eric Meyer's CSS Reset */ html { height: 100%; } article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } body { font: 12px/18px Arial, sans-serif; width: 100%; height: 100%; } .wrapper { width: 800px; margin: 0 auto; min-height: 100%; height: auto !important; height: 100%; } /* Header -----------------------------------------------------------------------------*/ .header { height: 50px; background: #FFE680; } /* Middle -----------------------------------------------------------------------------*/ .middle { width: 100%; padding: 0 0 50px; position: relative; } .middle:after { display: table; clear: both; content: ''; } .container { width: 100%; float: left; overflow: hidden; } .content { padding: 0 270px 0 270px; } /* Left Sidebar -----------------------------------------------------------------------------*/ .left-sidebar { float: left; width: 250px; margin-left: -100%; position: relative; background: #B5E3FF; } /* Footer -----------------------------------------------------------------------------*/ .footer { width: 800px; margin: -50px auto 0; height: 50px; background: #BFF08E; position: relative; }

Let’s try to build it one more time. Now, when the website looks like a first work of a beginner – we can proceed to the most important part.

Template Inheritance

Template inheritance mechanism is quite simple. Define from which template we inherit and add redefining of the content output function. Create tmpl_news.h file in data folder.

#ifndef TMPL_NEWS_H #define TMPL_NEWS_H #include "tmpl_master.h" namespace Data { //------------------------------------------------------------------------------------- // Dsc: News content //------------------------------------------------------------------------------------- struct News :public Master{ //------------------------------------------------------------------------------------- // Dsc: Main News //------------------------------------------------------------------------------------- std::string mainNews; //------------------------------------------------------------------------------------- // Dsc: Page constructor //------------------------------------------------------------------------------------- News() : Master() {} //------------------------------------------------------------------------------------- // Dsc: Lazy destructor //------------------------------------------------------------------------------------- ~News(){} }; } #endif // TMPL_NEWS_H

Also add news.tmpl file to templates folder.

<% c++ #include "data/tmpl_news.h" %> <% skin defskin %> <% view News uses Data::News extends Master %> <% template page_main() %><%= mainNews %><% end %> <% end view %> <% end skin %>

Add a path to the file in the build script:

TEMPLATES="$INPUT/templates/master.tmpl" TEMPLATES="$TEMPLATES $INPUT/templates/news.tmpl"

Change main.cpp file

#include <cppcms/applications_pool.h> #include <cppcms/url_dispatcher.h> #include <cppcms/http_response.h> #include <cppcms/application.h> #include <cppcms/url_mapper.h> #include <cppcms/service.h> #include "data/tmpl_master.h" #include "data/tmpl_news.h" //------------------------------------------------------------------------------------- // Dsc: Our class for page rendering, when some address is requested by the user // First of all he will get here //------------------------------------------------------------------------------------- class WebSite : public cppcms::application{ public: //------------------------------------------------------------------------------------- // Dsc: Constructor. Entry point. //------------------------------------------------------------------------------------- WebSite(cppcms::service &s) : cppcms::application(s) { dispatcher().assign("/news(.*)",&WebSite::news,this,1); mapper().assign("news","/news"); dispatcher().assign("(/?)",&WebSite::master,this,1); mapper().assign("master","/"); } //------------------------------------------------------------------------------------- // Dsc: The function we’ll get into if it’s not indicated otherwise in the constructor. // (more about it later) //------------------------------------------------------------------------------------- virtual void main(std::string path) { cppcms::application::main(path); } //------------------------------------------------------------------------------------- // Dsc: Basic content rendering //------------------------------------------------------------------------------------- virtual void master(std::string path) { Data::Master tmpl; tmpl.page.title = path; tmpl.page.description = "description"; tmpl.page.keywords = "keywords"; tmpl.page.menuList.insert(std::pair<std::string,std::string>("/","MASTER")); tmpl.page.menuList.insert(std::pair<std::string,std::string>("/news","NEWS")); render("Master",tmpl); } //------------------------------------------------------------------------------------- // Dsc: News rendering //------------------------------------------------------------------------------------- virtual void news(std::string path) { Data::News tmpl; tmpl.page.title = path; tmpl.page.description = "description"; tmpl.page.keywords = "keywords"; tmpl.page.menuList.insert(std::pair<std::string,std::string>("/","MASTER")); tmpl.page.menuList.insert(std::pair<std::string,std::string>("/news","NEWS")); tmpl.mainNews = "Sensation! Nothing happened at our website!"; render("News",tmpl); } }; //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- int main(int argc,char **argv) { try { // create the service cppcms::service srv(argc,argv); // define the root srv.applications_pool().mount(cppcms::applications_factory<WebSite>()); // start srv.run(); } catch(std::exception const &e) { std::cerr << "Failed: " << e.what() << std::endl; std::cerr << booster::trace(e) << std::endl; return 1; } return 0; } }

Major changes of the file occurred in constructor, where we had indicated which function is in charge of which page. Now these pages can display different templates. A crucial pint is the order of files list delivery to the template engine (children files should go after parents, otherwise errors will occur.

That’s where I am going to end the first part. Comments are appreciated.