package DBIx::Tree::NestedSet::Manage; use strict; use Carp; use base 'CGI::Application'; $DBIx::Tree::NestedSet::Manage::VERSION='0.12'; #POD Below!! ################################################################################ sub setup { my $self=shift; $self->start_mode('show_nodes'); $self->mode_param('rm'); $self->run_modes( show_nodes=>'show_nodes', add_child_form=>'add_child_form', move_up=>'move_up', move_down=>'move_down', delete_node=>'delete_node', edit_node=>'edit_node', denied=>'denied', 'AUTOLOAD'=>'show_nodes' ); } ######################################## ################################################################################ sub cgiapp_init{ my $self=shift; my $q=$self->query(); $self->param( template=>$self->load_tmpl( $self->param('template_name'), die_on_bad_params=>0 ) ); } ######################################## ################################################################################ sub stuff_in_extra_info{ my ($self,$array)=@_; my $q=$self->query(); my $script_name=$q->script_name(); my $upper_sibling; my $lower_sibling; my $i=1; foreach (@$array) { $_->{LOWER_SIBLING}=$array->[$i]->{id} if($array->[$i]); $_->{UPPER_SIBLING}=$upper_sibling; $_->{SCRIPT_NAME}=$script_name; $upper_sibling=$_->{id}; $i++; } } ######################################## ################################################################################ sub show_nodes{ my $self=shift; my $q=$self->query(); my $tree=$self->param('tree'); my $template=$self->param('template'); my $id = $q->param('id') || $self->param('start_root') || $tree->get_root(); my $current_nodes=$tree->get_children_flat( id => $id, depth => 1 ); #my $foo=shift @$current_nodes; my $parents=$tree->get_self_and_parents_flat(id=>$id); $self->stuff_in_extra_info($parents); $self->stuff_in_extra_info($current_nodes); my $node_info=$tree->get_hashref_of_info_by_id($id); $template->param( SHOW_NODES=>1, CURRENT_NODES=>$current_nodes, PARENTS=>$parents, CURRENT_ID=>$id, NAME=>$node_info->{name} ); return $template->output(); } ######################################## ################################################################################ sub redirect_to_category{ my ($self,$id)=@_; my $q=$self->query(); $self->header_type('redirect'); $self->header_add(-location=>$q->script_name().'?rm=show_nodes;id='.$q->escape($id)); } ######################################## ################################################################################ sub add_child_form{ my $self=shift; my $q=$self->query(); my $tree=$self->param('tree'); my $template=$self->param('template'); my $id = $q->param('id') || $self->param('start_root') || $tree->get_root(); my $errors={}; if($q->param('submit')){ if(! $q->param('name')){ $errors->{NO_NAME}=1; } else { $tree->add_child_to_right(id=>$id,name=>$q->param('name')); return $self->redirect_to_category($id); } } my $node_info=$tree->get_hashref_of_info_by_id($id); my $form .= $q->start_form(). $q->hidden(-name=>'rm',-value=>'add_child_form',-override=>1). $q->hidden(-name=>'id'). $q->textfield(-name=>'name'). $q->submit(-name=>'submit'). $q->end_form(); $template->param( ADD_CHILD_FORM=>1, FORM=>$form, ERRORS=>$errors, PARENT=>$node_info->{name}, ERROR_NO_NAME=>$errors->{NO_NAME} ); return $template->output(); } ######################################## ################################################################################ sub move_up{ my $self=shift; my $q=$self->query; my $tree=$self->param('tree'); my $up_id=$q->param('up_id'); my $id=$q->param('id'); if($id && $up_id){ my $parents=$tree->get_self_and_parents_flat(id=>$id); $tree->swap_nodes(first_id=>$id,second_id=>$up_id); return $self->redirect_to_category($parents->[-2]->{id} || $tree->get_root); } } ######################################## ################################################################################ sub move_down{ my $self=shift; my $q=$self->query; my $tree=$self->param('tree'); my $down_id=$q->param('down_id'); my $id=$q->param('id'); if($id && $down_id){ my $parents=$tree->get_self_and_parents_flat(id=>$id); $tree->swap_nodes(first_id=>$id,second_id=>$down_id); return $self->redirect_to_category($parents->[-2]->{id} || $tree->get_root); } } ######################################## ################################################################################ sub confirm_node_can_be_deleted{ # You should customize this method to check for you own # criteria as to what nodes may be deleted. # my $self=shift; # my $dbh=$self->param('dbh'); # my $q=$self->query(); # my $tree=$self->param('tree'); # my $nodes=$tree->get_self_and_children_flat(id=>$q->param('id')); # my @ids=map{$dbh->quote($_->{id})} @$nodes; # my $id_sql=join(',',@ids); # my ($count)=$dbh->selectrow_array(qq|select count(*) from doc_categories where primary_cat in($id_sql)|); # #If there's a positive count, we can't delete. # return ($count) ? 0 : 1 ; return 1; } ######################################## ################################################################################ sub delete_node{ my $self=shift; my $q=$self->query(); my $tree=$self->param('tree'); my $id=$q->param('id'); my $confirm_node_can_be_deleted=$self->confirm_node_can_be_deleted(); if($q->param('confirm') && $id && $confirm_node_can_be_deleted){ my $parents=$tree->get_self_and_parents_flat(id=>$id); $tree->delete_self_and_children(id=>$id); return $self->redirect_to_category($parents->[-2]->{id} || $tree->get_root); } else { my $template=$self->param('template'); my $node_info=$tree->get_hashref_of_info_by_id($id); $template->param( CONFIRM_NODE_DELETION=>1, NODE_CAN_BE_DELETED=>$confirm_node_can_be_deleted, SCRIPT_NAME=>$q->script_name(), NODE_NAME=>$node_info->{name}, ID=>$id, ); return $template->output(); } } ######################################## ################################################################################ sub denied{ return 'Access is denied.'; } ######################################## ################################################################################ sub edit_node{ my $self=shift; my $q=$self->query(); my $id=$q->param('id'); my $tree=$self->param('tree'); my $node_info=$tree->get_hashref_of_info_by_id($id); if($q->param('name') && $id){ # We passed tests. $tree->edit_node(id=>$id,name=>$q->param('name')); my $parents=$tree->get_self_and_parents_flat(id=>$id); return $self->redirect_to_category($parents->[-2]->{id} || $tree->get_root); } else { my $form={}; $form->{START_FORM}=$q->start_form(). $q->hidden(-name=>'rm',-value=>'edit_node',-override=>1). $q->hidden(-name=>'id'); $form->{NAME_TEXTFIELD}= $q->textfield(-name=>'name',-value=>$node_info->{name}); $form->{SUBMIT}=$q->submit(-name=>'submit',-value=>'Edit Node'); $form->{END_FORM}=$q->end_form(); $form->{EDIT_NODE}=1; $form->{SCRIPT_NAME}=$q->script_name; $form->{NODE_NAME}=$node_info->{name}; $form->{ID}=$id; my $template=$self->param('template'); $template->param( %$form ); return $template->output(); } } ######################################## 1; __END__ =pod =head1 NAME DBIx::Tree::NestedSet::Manage =head1 SYNOPSIS A CGI::Application and HTML::Template based helper class that provides an interface to DBIx::Tree::NestedSet methods. =head1 DESCRIPTION The idea of this module is that you subclass it and add your own cgiapp_prerun(), denied(), and cgiapp_postrun() methods. You should probably tweak the add_child_form() and delete_node() methods too to include the metadata you want in your tree. confirm_node_can_be_deleted() should be overridden too, it's used to "confirm" whether or not a node can be deleted without messing up your database. Returning a true value means the node is OK to delete. See the "templates", "cgi-bin", and "graphics" directories of this distribution for an example HTML::Template, graphics (thank you to WebGUI) and an instance script. Example Module: package My::NestedSetTree; use base 'DBIx::Tree::NestedSet::Manage'; use strict; sub cgiapp_prerun{ #Controls access to this module. my $self=shift; if ($self->access_not_allowed()) { $self->prerun_mode('denied'); } else { return; } } sub denied{ #Content returned if a user isn't allowed to access this module return 'Access is denied.'; } sub cgiapp_postrun { #HTML content to "wrap around" this module. my $self = shift; my $output_ref = shift; my $new_output = "