Simple Threaded Web Posts

= Simple threaded web posts =

This is a simple recipe for threaded posts with CDBI. A live version of the following code is available sometimes here: http://majenta.org/cgi/threaded-posts.cgi (posted by [apv]). It is not a finished application, but a proof of concept for how this can be setup in just around 100 lines of code.

MySQL setup
USE test; DROP TABLE IF EXISTS post; CREATE TABLE post (   id       INT(9) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,    parent   INT(9),       # post.id referential                                           text     TEXT NOT NULL,    );

GRANT ALL ON test.post TO nobody@localhost identified by 'abadpassword';

.my-web.cnf
[client] password=abadpassword database=test user=nobody

note: Class::DBI::DATA::Schema w/ SQL
package ThreadedPosts; use base 'Class::DBI'; use Class::DBI::DATA::Schema; my $DBD = 'mysql'; my $DB = 'test'; my $HOST = 'localhost'; my $CONNECT_FILE = "./.my-web.cnf"; my $ATTR = {}; __PACKAGE__->connection("dbi:$DBD:$DB:$HOST;" .                             "mysql_read_default_file=$CONNECT_FILE;",                         undef, undef,                         $ATTR ); __PACKAGE__->table('post'); __PACKAGE__->columns(All => qw( id parent text ) ); __PACKAGE__->has_a(parent => __PACKAGE__); __PACKAGE__->has_many(replies => __PACKAGE__,                      { order_by => 'id' }                      ); sub drop_and_recreate { my $package = shift; $package->run_data_sql; } 1; __DATA__ USE test; DROP TABLE IF EXISTS post; CREATE TABLE post (    id       INT(9) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,     parent   INT(9),       # post.id referential                                           text     TEXT NOT NULL,     );
 * 1) probably have to run this as mysql root
 * 2) GRANT ALL ON test.post TO nobody@localhost;

threaded-posts.cgi
use warnings; no warnings 'uninitialized'; use strict; use CGI qw(:standard); use ThreadedPosts; print header, start_html(               -head  => style({type => 'text/css'}, join('',), ),               -title => "Threaded Posts with CDBI"               ), start_form; if ( param('DELETE ALL!') ) { ThreadedPosts->drop_and_recreate; } if ( param('Add Post') ) { my $post = ThreadedPosts->create ({text => 'This is an original post'}); $post->update; } if ( my $id = param('reply_id') ) { my $post = ThreadedPosts->retrieve( $id ); $post->add_to_replies({ text => "This is a reply to $id" }); } my @posts = ThreadedPosts->retrieve_all; for my $post ( @posts ) { print show_post($post); } print submit("Add Post"); print submit("DELETE ALL!") if @posts; exit 0; sub show_post { my $post = shift; my $depth = shift; return if $post->parent and not $depth; $depth++; return div({-class => 'post',},            div({-style => 'padding:1ex; margin:0'}, "id: ", $post, '('. $post->text .")", 'depth: ', $depth, br, "In reply to", ( $post->parent ?                  $post->parent->text : 'nothing'). '.',                ( $depth >= 8 ? '' :                   a({-href => '?reply_id='. $post}, 'Reply to this', ),                ),                ),             ( map { show_post($_, $depth) } $post->replies ),            ); } __DATA__ .post { border-left:1px dotted black; border-top:1px dotted black; margin:.3ex 0 0 1em; padding:.3ex 0 0 0; background-color:#eef; font-size:10pt; } .post > .post { background-color:#ffe; } .post > .post > .post { background-color:#fef; } .post > .post > .post > .post { background-color:#eff; } .post > .post > .post > .post > .post { background-color:#ffe; } .post > .post > .post > .post > .post > .post { background-color:#dde; } .post > .post > .post > .post > .post > .post > .post { background-color:#ded; } .post > .post > .post > .post > .post > .post > .post > .post { background-color:#edd; }
 * 1) Subroutines

Comments and feedback
...