Home  /  RSS  /  RSS Comments  /  Project Site  /  Enter

Tutorial: Message Board.

Now we learn to write slightly more complicated application: a simple message board and learn:

  1. How to use nice URLs support.
  2. How to use integration with CgiCC library.
  3. How to write more complex templates.
  4. How to use DbiXX SQL library

Requirements: Read "Hello World With Templates" tutorial, install dbixx component of this framework.

Notes:

  1. You can use any other SQL library, this framework does not limit you to DbiXX nor gives special advantages using it. However it is recommended that the library you use would provide automatic escaping of input parameters in order prevent any kind of SQL injection.
  2. The code from this example is avialible from this location. It is recommended to read it together with explanations.

Prepare Templates

First we write a three basic templates:

  1. master.tmpl – the frame that outlines all content with title, document type and all other properties shared between all pages.
  2. main.tmpl – the template that displays a list of latest topics.
  3. topic.tmpl – the template for discussion of certain topic.

Master template has a statement:

<% include ref master_ref %>

that tells it to include content of template that defined by variable master_ref. This allows as to simplify the process of creation of general templates.

In main template we iterate over topics list. If the list is empty the appropriate message is displayed.

<% foreach topic in topics %>
  <ul>
  <% item %>
     <li><a href="<% topic.url %>"><% topic.title %></a></li>
  <% end %>
  </ul>
<% empty %>
  <h2>The message board is empty</h2>
<% end %>

We do the same for topic template that would display slightly different list of messages. It has a special display option of content variable:

<% msg.content | escape | ext totext %>

First the message is HTML escaped and then external filter "totext" is applied. This filter will replace "new-line" symbols with <br/>.

Note: HTML escaping is always applied unless we use our special filter, thus in order to add escaping we make an explicit filter chain: escaping and then applying our filter.

Code

Members

First we create our class bb with following members:

renderer rnd;
session sql;
content cnt;
// Major functions
void main_page(string from);
void topic(string no);
void new_topic();
// Major URLS
string base_url;
string more_url;
string topic_url;
string new_topic_url;

Where session is dbixx sql session we use to communicate with DB, content is general content we fill.

We define 3 functions that would be called up to appropriate requests: display main page, create new topic and display certain topic.

We also store a representation of major urls in order to simplify our work.

Constructor

sql.driver("sqlite3");
sql.param("sqlite3_dbdir",app.config.sval("sql.dir"));
sql.param("dbname",app.config.sval("sql.db"));
sql.connect();

First we establish connection to our database. We use general configuration facilities of CppCMS by accessing reference to manager: app that is a member of worker_thread.

Now we create a connection between URLs and functions that proceed them:

base_url=app.config.sval("bb.base_url");

url.add("^/?$",
    boost::bind(&bb::main_page,this,"0"));

url.add("^/page/(\\d+)$",
    boost::bind(&bb::main_page,this,$1));
more_url=base_url+"/page/%d";

url.add("^/topic/(\\d+)$",
    boost::bind(&bb::topic,this,$1));
topic_url=base_url+"/topic/%d";

url.add("^/new_topic$",
    boost::bind(&bb::new_topic,this));
new_topic_url=base_url+"/new_topic";

Each call to url.add has a regular expression as first parameter and a callback as a second. We also setup our url references like topic_url for future use.

rnd.add_string_filter("totext",
   boost::bind(to_text,_1,_2));

And the last one we create a new filter "totext" by binding to it a callback function to_text that receives two parameters input and output strings.

Main Loop

Now lets see how our main execution function will look now:

cnt.clear();
if(url.parse()==-1) {
   set_header(new HTTPStatusHeader(404,"not found"));
}

First we clean the content of previous calls. Then we just call "url.parse()" that tries to match a url with a regular expression and call to appropriate callback function. If it fails -1 returned, in this case we assign 404 header.

Callback function

Now let’s see how typical callback function works:

void bb::topic(string id);

It receives as parameter a string id matched by regular expression /topic/(\d+) ,

transaction tr(sql);

First we start a DB transaction.

int topic_id=atoi(id.c_str());
sql<<"SELECT title FROM topics WHERE id==?",topic_id;
row r;
if(!sql.single(r)) {
    set_header(new HTTPRedirectHeader(base_url));
    return;
}
string title;
r>>title;

Now we try to fetch a title of the topic and figure out if the requested id exists.

cgicc::const_form_iterator t,m;
t=cgi->getElement("title");
m=cgi->getElement("content");
if(t!=cgi->getElements().end()
   && m!=cgi->getElements().end()
   && t->getValue()!="")
{
    sql<<"insert into messages(topic_id,title,content) "
         "values(?,?,?)",
            topic_id,t->getValue(),m->getValue();
    sql.exec();
}

Now we test if the form was filled and we have a new comment. If so we insert it into database. The variable cgi is a member of worker_thread of type pointer to cgicc::cgicc. Please refer to CgiCC documentation for additional information.

Note: every string is automatically escaped by dbixx.

result res;
sql<<"SELECT title,content FROM messages "
     "WHERE topic_id=? "
     "ORDER BY id",topic_id,res;

Now we fetch all messages for this topic and push them to a sequence:

content::vector_t &v=cnt.vector("messages",res.rows());

int i;
for(i=0;res.next(r);i++){
    string title,content;
    r>>title>>content;
    v[i]["title"]=title;
    v[i]["content"]=content;
}
tr.commit();

Now we fill the sequence with titles and content of comments fetched from the DB. And commit our transaction.

cnt["submit_url"]=str(format(topic_url) % topic_id);
cnt["base_url"]=base_url;

How we define some additional template variables – including postback address.

cnt["master_ref"]=string("topic");

This is important line: it defines which template should be included by master template: topic

rnd.render(cnt,"master",out);

Now we render master template that would include topic template to output,

Execution

The full code can be downloaded from there. It order to build it run make, in order to start lighttpd web server execute "./run.sh" and now you can browse to http://127.0.0.1:8080/bb and see how it works.

Pages

Categories

Development

Powered By

3rd Party