CppCMS
message_board/apps/thread.cpp Source File
#include <apps/thread.h>
#include <cppcms/util.h>
#include <cppcms/url_dispatcher.h>
#include <cppcms/url_mapper.h>
#include <cppcms/session_interface.h>
#include <cppcms/cache_interface.h>



namespace data {

reply_form::reply_form()
{
        using cppcms::locale::translate;
        author.message(translate("Author"));
        comment.message(translate("Comment"));
        send.value(translate("Send"));
        add(author);
        add(comment);
        add(send);
        author.limits(1,64);
        comment.limits(1,256);
}


} // data

namespace {
        std::string text2html(std::string const &s)
        {
                std::string tmp=cppcms::util::escape(s);
                std::string res;
                res.reserve(tmp.size());
                for(unsigned i=0;i<tmp.size();i++) {
                        if(tmp[i]=='\n') {
                                res+="<br />";
                        }
                        res+=tmp[i];
                }
                return res;
        }
}


namespace apps {

thread_shared::thread_shared(cppcms::service &s) : apps::master(s)
{
}


bool thread_shared::prepare(data::thread_shared &c,int id)
{
        master::prepare(c);
        c.thread_id = id;
        

        cppdb::result r;
        r=sql<<"SELECT title FROM threads WHERE id=?" << id << cppdb::row;
        if(r.empty()) {
                response().make_error_response(404);
                return false;
        }
        
        r>> c.title;
        c.text2html = text2html;
        return true;

}

flat_thread::flat_thread(cppcms::service &s) : thread_shared(s)
{
        dispatcher().assign(".*",&flat_thread::prepare,this,0);
        mapper().assign("{1}");
}


void flat_thread::prepare(std::string sid)
{
        std::string key = "flat_thread_" + sid;
        
        if(cache().fetch_page(key))
                return;

        cache().add_trigger("thread_" + sid);
        
        data::flat_thread c;
        int id = atoi(sid.c_str());
        if(!thread_shared::prepare(c,id))
                return;
        cppdb::result r;
        r=sql<< "SELECT id,author,content "
                "FROM messages WHERE thread_id=? "
                "ORDER BY id" << id;
        
        c.messages.reserve(10);
        for(int i=0;r.next();i++) {
                c.messages.resize(i+1);
                r>>c.messages[i].msg_id>>c.messages[i].author>>c.messages[i].content;
        }
        session()["view"]="flat";
        render("flat_thread",c);

        cache().store_page(key);
}

typedef std::map<int,std::map<int,data::msg> > msg_ord_t;

namespace {

void make_tree(data::tree_t &messages,std::map<int,std::map<int,data::msg> > &content,int start)
{
        std::pair<msg_ord_t::iterator,msg_ord_t::iterator>
                range=content.equal_range(start);
        for(msg_ord_t::iterator p=range.first;p!=range.second;++p) {
                for(std::map<int,data::msg>::iterator p2=p->second.begin(),e=p->second.end();p2!=e;++p2) {
                        data::tree_thread::tree_msg &m=messages[p2->first];
                        m.author=p2->second.author;
                        m.content=p2->second.content;
                        m.msg_id=p2->second.msg_id;
                        make_tree(m.repl,content,p2->first);
                }
        }
        
}

} // anon

tree_thread::tree_thread(cppcms::service &s) : thread_shared(s)
{
        dispatcher().assign(".*",&tree_thread::prepare,this,0);
        mapper().assign("{1}");
}



void tree_thread::prepare(std::string sid)
{
        std::string key = "tree_thread_" + sid;
        
        if(cache().fetch_page(key))
                return;

        cache().add_trigger("thread_" + sid);

        int id = atoi(sid.c_str());
        data::tree_thread c;
        if(!thread_shared::prepare(c,id))
                return;
        cppdb::result r;
        r=sql<< "SELECT reply_to,id,author,content "
                "FROM messages WHERE thread_id=? "
                "ORDER BY reply_to,id DESC" << id;
        msg_ord_t all;
        while(r.next()) {
                int msg_id,rpl_id;
                std::string author,comment;
                r>>rpl_id>>msg_id;
                data::msg &message=all[rpl_id][msg_id];
                r>>message.author>>message.content;
                message.msg_id=msg_id;
        }
        
        make_tree(c.messages,all,0);

        session()["view"]="tree";
        render("tree_thread",c);

        cache().store_page(key);
}

reply::reply(cppcms::service &srv) : thread_shared(srv)
{
        dispatcher().assign(".*",&reply::prepare,this,0);
        mapper().assign("{1}");
}

void reply::prepare(std::string smid)
{
        int mid;
        mid=atoi(smid.c_str());

        data::reply c;
        
        if(request().request_method()=="POST") {
                c.form.load(context());
                if(c.form.validate()) {
                        cppdb::transaction tr(sql);
                        cppdb::result r;
                        r=sql<<"SELECT thread_id FROM messages WHERE id=?" << mid << cppdb::row;
                        if(r.empty()) {
                                response().make_error_response(404);
                                return;
                        }
                        int tid = r.get<int>(0);
                        // we want this as string for trigger
                        std::string stid = r.get<std::string>(0);
                        r.clear();
                        sql<<   "INSERT INTO messages(reply_to,thread_id,author,content) "
                                "VALUES(?,?,?,?) " 
                                << mid << tid << c.form.author.value() << c.form.comment.value() 
                                << cppdb::exec;

                        cache().rise("thread_" + stid);
                        tr.commit();

                        response().set_redirect_header(url("/user_thread",tid));
                        return;
                }
        }

        cppdb::result r;
        r=sql<< "SELECT threads.id,author,content,title "
                "FROM messages "
                "JOIN threads ON thread_id=threads.id "
                "WHERE messages.id=?" << mid << cppdb::row;
        if(r.empty()) {
                response().make_error_response(404);
                return;
        }

        int tid;

        r>>tid>>c.author>>c.content>>c.title;
        c.thread_id = tid;
        c.msg_id = mid;
        
        if(!thread_shared::prepare(c,tid))
                return;

        render("reply",c);
}

} // namespace apps