#!/usr/bin/perl # tagging.cgi - an experiment in folksonomies # Eric Lease Morgan # 2007-12-10 - calling it version 1.0 # 2007-12-03 - started hotlinking tags; can list tags; kludge is now username # 2007-11-30 - can not add tags and they are listed as your and everybody; kludge! # 2007-11-27 - first investigations # require/use use CGI; use CGI::Carp qw(fatalsToBrowser); use MyLibrary::Core; use MyLibrary::Patron; use MyLibrary::Auth::Basic; use strict; use constant TEMPLATE => 'etc/template.txt'; use constant HOME => 'etc/home.txt'; use constant INSTANCE => 'tagging'; use constant COOKIENAME => 'tagging'; use constant EXPIRE => '60m'; use constant CMDTERM => 'term'; use constant ERROR => 'etc/error.txt'; use constant TERM => 'etc/term.txt'; use constant LOCATION => 'URL'; use constant CREATE => 'etc/create.txt'; use constant SIGNIN => 'etc/signin.txt'; use constant CHOICES_A => 'etc/choices-authenticated.txt'; use constant CHOICES_N => 'etc/choices-not.txt'; use constant TAG_FACET => 'Tags'; use constant TAG_NOTE => 'Descriptive terms initilized by users'; use constant TAGS => './etc/tags.txt'; use constant TAGS_YOURS => './etc/tags_yours.txt'; # configure MyLibrary::Config->instance( INSTANCE ); # initialize my $cgi = CGI->new; my $html = ''; # authenticate, if there is a cookie my ( $auth, $session_id, $status, $username ); if ( $cgi->cookie( COOKIENAME )) { $session_id = $cgi->cookie( COOKIENAME ); $auth = MyLibrary::Auth::Basic->new( sessid => $session_id, session_expire => EXPIRE ); $status = $auth->status; $username = $auth->username; } # get the command my $cmd = $cgi->param( 'cmd' ); # branch accordingly if (! $cmd ) { # display the home page $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( HOME )/e; $html =~ s/##NUMBER_OF_RESOURCES##/&commify( &number_of_items )/e; $html =~ s/##FACETS##/&facet_term_combinations( MyLibrary::Facet->new( name => 'Subjects' ))/e; } elsif ($cmd eq 'term') { # get the input and find the term my $term_id = $cgi->param('id'); my $term = MyLibrary::Term->new( id => $term_id ); # build an html list of resources from the input my @resource_ids = $term->related_resources( sort => 'name' ); my $list; foreach my $id ( @resource_ids ) { # get this resource and its url my $resource = MyLibrary::Resource->new( id => $id ); my $url = &get_location( $resource, LOCATION ); # get the everybody's tags my $everybodys_tags = ''; my $facet_id = &make_or_get_facet_id( TAG_FACET, TAG_NOTE ); foreach ( $resource->related_terms ) { my $term = MyLibrary::Term->new( id => $_ ); if ( $term->facet_id == $facet_id ) { $everybodys_tags .= $cgi->a({ -href => './?cmd=' . CMDTERM . '&id=' . $term->term_id }, $term->term_name ) . '; '; } } if ( $everybodys_tags ) { $everybodys_tags = $cgi->br( $cgi->span({ -class => 'tags' }, "Everybody's tags: $everybodys_tags" ))} # get the user's tags my $users_tags = ''; if ( $username ) { my $facet_id = &make_or_get_facet_id( TAG_FACET . "_$username", TAG_NOTE ); my @related_terms = $resource->related_terms; if ( scalar( @related_terms )) { foreach ( @related_terms ) { my $term = MyLibrary::Term->new( id => $_ ); if ( $term->facet_id == $facet_id ) { my $term_name = $term->term_name; $term_name =~ s/_$username//g; $users_tags .= $cgi->a({ -href => './?cmd=' . CMDTERM . '&id=' . $term->term_id }, $term_name ) . '; '; } } } if ( $users_tags ) { $users_tags = $cgi->br( $cgi->span({ -class => 'tags' }, "Your tags: $users_tags" ))} } # check to see if they've logged in if ( $username ) { # create add_tag form my $form = ''; $form .= $cgi->start_form( -onSubmit => 'return add_tag(this)' ); $form .= $cgi->hidden( -name => 'resource', -value => $resource->id ); $form .= $cgi->hidden( -name => 'username', -value => $username ); $form .= $cgi->textfield( -name => 'tag' ); $form .= $cgi->submit( -name => 'Go', ); $form .= $cgi->end_form; $form = $cgi->td( $form ); # wrap it in a table and then a hidden div $form = $cgi->table({ -border => 0, -class => 'detail' }, $cgi->Tr( { -valign => 'top' }, $form )); $form = $cgi->div({ -id => 'd' . $resource->id, -style => 'display: none' }, $form ); # build a list $list .= $cgi->li($cgi->a({ -href => $url }, $resource->name ) . ' ' . $cgi->a( { -href => "javascript:expand('d" . $resource->id . "')", -style => 'color: grey; text-decoration: none' }, 'Add a tag' ) . $users_tags . $everybodys_tags . $form ); } # give them the plain ol' list; boring else { # build a list $list .= $cgi->li($cgi->a({ -href => $url }, $resource->name ) . $users_tags . $everybodys_tags ); } } $list = $cgi->ol($list); my $term_name = $term->term_name; $term_name =~ s/_$username//g; # display the page $html = &slurp(TEMPLATE); $html =~ s/##CONTENT##/&slurp( TERM )/e; $html =~ s/##TERM##/$term_name/e; $html =~ s/##TERM_NOTE##/$term->term_note/e; $html =~ s/##RESOURCE_LIST##/$list/e; } elsif ( $cmd eq 'tags' ) { # display the home page $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( TAGS )/e; $html =~ s/##FACETS##/&facet_term_combinations( MyLibrary::Facet->new( id => &make_or_get_facet_id( TAG_FACET, TAG_NOTE )))/e; } elsif ( $cmd eq 'your_tags' ) { # display the home page $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( TAGS_YOURS )/e; $html =~ s/##FACETS##/&facet_term_combinations( MyLibrary::Facet->new( id => &make_or_get_facet_id( TAG_FACET . "_$username", TAG_NOTE )))/e; } elsif ( $cmd eq 'post_tag' ) { # get the input my $resource_id = $cgi->param( 'resource' ); my $tag = $cgi->param( 'tag' ); my $username = $cgi->param( 'username' ); # add the tag to the tag facet my $tag_facet_id = &make_or_get_facet_id( TAG_FACET, TAG_NOTE ); my $tag_term_id = &make_or_get_term_id( $tag, "This tag was initilized by $username", $tag_facet_id ); # add the tag to the tag_username facet my $user_facet_id = &make_or_get_facet_id( TAG_FACET . "_$username", "Terms created by $username" ); my $user_term_id = &make_or_get_term_id( $tag . "_$username", "This tag was initilized by $username", $user_facet_id ); # update the resource my $resource = MyLibrary::Resource->new( id => $resource_id ); $resource->related_terms( new => [ $tag_term_id, $user_term_id ]); # return success $html = "1"; print $cgi->header( -type => 'text/xml', -charset => 'utf-8' ); print $html; exit; } elsif ( $cmd eq 'create' ) { # get the input my $username = $cgi->param( 'username' ); my $password = $cgi->param( 'password' ); my $echo = $cgi->param( 'echo' ); # sanity check if ( ! $username or ! $password ) { # display an error $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( CREATE )/e; } # more sanity checking elsif ( ! $username or ! $password or ! $echo ) { # display an error my $title = $cgi->h1( 'Something went wrong' ); my $message = $cgi->p( 'Alas, you need to complete all three fields. Please go back and try again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; } # even more checking elsif ( $password ne $echo ) { # display an error my $title = $cgi->h1( 'Something went wrong' ); my $message = $cgi->p( 'Bummer, your password and echoed password do not match. Please go back and try again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; } # make sure the name isn't already taken elsif ( MyLibrary::Patron->new( username => $username )) { # display an error my $title = $cgi->h1( 'Something went wrong' ); my $message = $cgi->p( 'Hmmm, that username has already been take. Please try another.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; } # whew! else { # do the work my $patron = MyLibrary::Patron->new; $patron->patron_username( $username ); $patron->patron_password( $password ); $patron->patron_stylesheet_id( 1 ); $patron->commit; # display the result my $title = $cgi->h1( 'Congrats!' ); my $message = $cgi->p( "The account named $username has been created. Now, please " . $cgi->a({ href => './?cmd=signin' }, 'sign in' ) . '.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; } } elsif ( $cmd eq 'signin' ) { # get the input my $username = $cgi->param( 'username' ); my $password = $cgi->param( 'password' ); # make sure they aren't already logged in if ( $status eq 'authenticated' ) { # display an error my $username = $auth->username; my $title = $cgi->h1( 'Oops!' ); my $message = $cgi->p( "It seems as if you are already signed in as $username. If this is not you, then please " . $cgi->a({ href => './?cmd=signout' }, 'sign out' ) . ' and sign in again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; } # display the form elsif ( ! $username or ! $password ) { # display form $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( SIGNIN )/e; } # do the work else { # authenticate; my $auth = MyLibrary::Auth::Basic->new( session_expire => EXPIRE ); my $return_code = $auth->authenticate( username => $username, password => $password ); # cool if ( $return_code eq 'success' ) { # set the cookie $session_id = $auth->sessid; $status = $auth->status; $username = $auth->username; my $cookie = $cgi->cookie( -name => COOKIENAME, -value => $session_id ); my $header = $cgi->header( -cookie => $cookie, -type => 'text/html', -charset => 'utf-8' ); # set up the result my $title = $cgi->h1( "Hello, $username" ); my $message = $cgi->p( 'You have sucessfully signed in. Welcome.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##STATUS##/$status/ge; $html =~ s/##USERNAME##/$username/ge; $html =~ s/##SESSIONID##/$session_id/ge; $html =~ s/##CHOICES##/&choices/ge; # send the cookie, the html, and quit print $header; print $html; exit; } # bad input elsif ( $return_code eq 'password failure' or $return_code eq 'username failure' ) { # display an error my $title = $cgi->h1( 'Username/password problem' ); my $message = $cgi->p( 'That username/password combination is invalid. Please try again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; } # unknown error else { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( "Unknown value for return_code ($return_code). Call Eric." ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; } } } elsif ( $cmd eq 'signout' ) { # do the work, immediately my $cookie = $cgi->cookie( -name => COOKIENAME, -value => '', -expires => '-1h' ); my $header = $cgi->header( -cookie => $cookie, -type => 'text/html', -charset => 'utf-8' ); $auth->close_session; # re-define $session_id = ''; $status = ''; $username = ''; # set up the result my $title = $cgi->h1( 'Sign out' ); my $message = $cgi->p( 'You have successfully signed out. Thank you for being with us. ' . $cgi->a({ href => './?cmd=signin' } , 'Sign in' ) . ' again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##STATUS##/$status/ge; $html =~ s/##USERNAME##/$username/ge; $html =~ s/##SESSIONID##/$session_id/ge; $html =~ s/##CHOICES##/&choices/ge; # reset the cookie, send the html, and quit, immediately print $header; print $html; exit; } else { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( "Unknown value for cmd ($cmd). Call Eric." ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; } # done $html =~ s/##STATUS##/$status/ge; $html =~ s/##USERNAME##/$username/ge; $html =~ s/##SESSIONID##/$session_id/ge; $html =~ s/##CHOICES##/&choices/ge; print $cgi->header( -type => 'text/html', -charset => 'utf-8' ); print $html; exit; sub facet_term_combinations { # get the input my @facets = @_; # initialize my $terms; foreach ( @facets ) { # create a list of terms associated with each facet foreach my $id ($_->related_terms(sort => 'name')) { my $term = MyLibrary::Term->new( id => $id ); my $term_name = $term->term_name; $term_name =~ s/_$username//g; $terms .= '
  • ' . $term_name . ' (' . &commify( scalar( $term->related_resources )) . ' items)
  • '; } } # done return $cgi->ol( $terms ); } sub commify { # cool hack; http://www.unix.org.ua/orelly/perl/cookbook/ch02_18.htm my $text = reverse $_[0]; $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; return scalar reverse $text; } sub slurp { # open a file named by the input and return its contents my $f = @_[0]; my $r; open (F, "< $f"); while () { $r .= $_ } close F; return $r; } sub get_location { # get the input my $resource = shift; my $location_type = shift; # initialize my $location; # process each location foreach ( $resource->resource_locations ) { my $type = MyLibrary::Resource::Location::Type->new( id => $_->resource_location_type ); if ( $type->name eq $location_type ) { $location = $_->location; last; } } # done return $location } sub number_of_items { return scalar(MyLibrary::Resource->get_resources(output => 'id')) } sub choices { # return a set of menu options if ( $status eq 'authenticated' ) { return &slurp( CHOICES_A )} else { return &slurp( CHOICES_N )} } sub make_or_get_facet_id { # get the input my $name = shift; my $note = shift; # initialize my $facet = MyLibrary::Facet->new; # check for the name if (! MyLibrary::Facet->new( name => $name )) { # create it $facet->facet_name( $name ); $facet->facet_note( $note ); $facet->commit; #print "The facet $name ($note) has been created.\n"; } else { # already exists $facet = MyLibrary::Facet->new( name => $name ); #print "The facet $name already exists.\n"; } # done return $facet->facet_id; } sub make_or_get_term_id { # get the input my $name = shift; my $note = shift; my $facet_id = shift; # initialize my $term = MyLibrary::Term->new; # check for the name if (! MyLibrary::Term->new( name => $name )) { # create it $term->term_name( $name ); $term->term_note( $note ); $term->facet_id( $facet_id ); $term->commit; #print "The term $name ($note) has been created.\n"; } else { # already exists $term = MyLibrary::Term->new( name => $name ); #print "The term $name already exists.\n"; } # done return $term->term_id; }