@ -153,7 +153,9 @@ plugin I18N => {
# Load mailer plugin with its default values
plugin Mailer = > {
from = > $ config - > { emailFrom } ,
transport = > Email::Sender::Transport::Sendmail - > new ( { sendmail = > $ config - > { sendmail } } ) ,
transport = > Email::Sender::Transport::Sendmail - > new ( {
sendmail = > $ config - > { sendmail }
} )
} ;
# Wrapper arround DBI
@ -173,8 +175,10 @@ helper login => sub {
my $ self = shift ;
return if $ self - > session ( 'name' ) ;
my $ login = $ ENV { 'REMOTE_USER' } || lc $ self - > get_random ( 29 ) ;
$ self - > session ( name = > $ login ,
ip = > $ self - > tx - > remote_address ) ;
$ self - > session (
name = > $ login ,
ip = > $ self - > tx - > remote_address
) ;
$ self - > app - > log - > info ( $ self - > session ( 'name' ) . " logged in from " . $ self - > tx - > remote_address ) ;
} ;
@ -197,8 +201,10 @@ helper create_room => sub {
my ( $ name , $ owner ) = @ _ ;
$ name = lc $ name unless ( $ name eq lc $ name ) ;
# Exit if the name isn't valid or already taken
return undef if ( $ self - > get_room ( $ name ) || ! $ self - > valid_room_name ( $ name ) ) ;
my $ sth = eval { $ self - > db - > prepare ( "INSERT INTO `rooms` (`name`,`create_timestamp`,`activity_timestamp`,`owner`,`token`,`realm`) VALUES (?,?,?,?,?,?);" ) } || return undef ;
return undef if ( $ self - > get_room ( $ name ) || ! $ self - > valid_room_name ( $ name ) ) ;
my $ sth = eval {
$ self - > db - > prepare ( "INSERT INTO `rooms` (`name`,`create_timestamp`,`activity_timestamp`,`owner`,`token`,`realm`) VALUES (?,?,?,?,?,?);" )
} || return undef ;
# Gen a random token. Will be used as a turnPassword
my $ tp = $ self - > get_random ( 49 ) ;
$ sth - > execute ( $ name , time ( ) , time ( ) , $ owner , $ tp , $ config - > { realm } ) || return undef ;
@ -214,7 +220,9 @@ helper create_room => sub {
helper get_room = > sub {
my $ self = shift ;
my ( $ name ) = @ _ ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT * FROM `rooms` WHERE `name`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT * FROM `rooms` WHERE `name`=?;" )
} || return undef ;
$ sth - > execute ( $ name ) || return undef ;
return $ sth - > fetchall_hashref ( 'name' ) - > { $ name } ;
} ;
@ -223,7 +231,9 @@ helper get_room => sub {
helper get_room_by_id = > sub {
my $ self = shift ;
my ( $ id ) = @ _ ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT * FROM `rooms` WHERE `id`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT * FROM `rooms` WHERE `id`=?;" )
} || return undef ;
$ sth - > execute ( $ id ) || return undef ;
return $ sth - > fetchall_hashref ( 'id' ) - > { $ id } ;
} ;
@ -235,7 +245,9 @@ helper lock_room => sub {
my ( $ name , $ lock ) = @ _ ;
return undef unless ( % { $ self - > get_room ( $ name ) } ) ;
return undef unless ( $ lock =~ m/^0|1$/ ) ;
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `rooms` SET `locked`=? WHERE `name`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `rooms` SET `locked`=? WHERE `name`=?;" )
} || return undef ;
$ sth - > execute ( $ lock , $ name ) || return undef ;
my $ action = ( $ lock eq '1' ) ? 'locked' : 'unlocked' ;
$ self - > app - > log - > info ( "room $name $action by " . $ self - > session ( 'name' ) ) ;
@ -248,7 +260,9 @@ helper add_participant => sub {
my $ self = shift ;
my ( $ name , $ participant ) = @ _ ;
my $ room = $ self - > get_room ( $ name ) || return undef ;
my $ sth = eval { $ self - > db - > prepare ( "INSERT IGNORE INTO `participants` (`id`,`participant`,`activity_timestamp`) VALUES (?,?,?);" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "INSERT IGNORE INTO `participants` (`id`,`participant`,`activity_timestamp`) VALUES (?,?,?);" )
} || return undef ;
$ sth - > execute ( $ room - > { id } , $ participant , time ( ) ) || return undef ;
$ self - > app - > log - > info ( $ self - > session ( 'name' ) . " joined the room $name" ) ;
return 1 ;
@ -259,7 +273,9 @@ helper remove_participant => sub {
my $ self = shift ;
my ( $ name , $ participant ) = @ _ ;
my $ room = $ self - > get_room ( $ name ) || return undef ;
my $ sth = eval { $ self - > db - > prepare ( "DELETE FROM `participants` WHERE `id`=? AND `participant`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "DELETE FROM `participants` WHERE `id`=? AND `participant`=?;" )
} || return undef ;
$ sth - > execute ( $ room - > { id } , $ participant ) || return undef ;
$ self - > app - > log - > info ( $ self - > session ( 'name' ) . " leaved the room $name" ) ;
return 1 ;
@ -270,7 +286,9 @@ helper get_participants => sub {
my $ self = shift ;
my ( $ name ) = @ _ ;
my $ room = $ self - > get_room ( $ name ) || return undef ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT `participant` FROM `participants` WHERE `id`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT `participant` FROM `participants` WHERE `id`=?;" )
} || return undef ;
$ sth - > execute ( $ room - > { id } ) || return undef ;
my @ res ;
while ( my @ row = $ sth - > fetchrow_array ) {
@ -284,10 +302,14 @@ helper set_peer_role => sub {
my $ self = shift ;
my ( $ room , $ name , $ id , $ role ) = @ _ ;
# Check if this ID isn't the one from another peer first
my $ sth = eval { $ self - > db - > prepare ( "SELECT * FROM `participants` WHERE `peer_id`=? AND `participant`!=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT * FROM `participants` WHERE `peer_id`=? AND `participant`!=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" )
} || return undef ;
$ sth - > execute ( $ id , $ name , $ room ) || return undef ;
return undef if ( $ sth - > rows > 0 ) ;
$ sth = eval { $ self - > db - > prepare ( "UPDATE `participants` SET `peer_id`=?,`role`=? WHERE `participant`=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" ) } || return undef ;
$ sth = eval {
$ self - > db - > prepare ( "UPDATE `participants` SET `peer_id`=?,`role`=? WHERE `participant`=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" )
} || return undef ;
$ sth - > execute ( $ id , $ role , $ name , $ room ) || return undef ;
$ self - > app - > log - > info ( "User $name (peer id $id) has now the $role role in room $room" ) ;
return 1 ;
@ -297,7 +319,9 @@ helper set_peer_role => sub {
helper get_peer_role = > sub {
my $ self = shift ;
my ( $ room , $ id ) = @ _ ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT `role` FROM `participants` WHERE `peer_id`=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT `role` FROM `participants` WHERE `peer_id`=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" )
} || return undef ;
$ sth - > execute ( $ id , $ room ) || return undef ;
if ( $ sth - > rows == 1 ) {
my ( $ role ) = $ sth - > fetchrow_array ( ) ;
@ -312,10 +336,14 @@ helper get_peer_role => sub {
helper promote_peer = > sub {
my $ self = shift ;
my ( $ room , $ id ) = @ _ ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT * FROM `participants` WHERE `peer_id`=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT * FROM `participants` WHERE `peer_id`=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" )
} || return undef ;
$ sth - > execute ( $ id , $ room ) || return undef ;
return undef if ( $ sth - > rows != 1 ) ;
$ sth = eval { $ self - > db - > prepare ( "UPDATE `participants` SET `role`='owner' WHERE `peer_id`=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" ) } || return undef ;
$ sth = eval {
$ self - > db - > prepare ( "UPDATE `participants` SET `role`='owner' WHERE `peer_id`=? AND `id` IN (SELECT `id` FROM `rooms` WHERE `name`=?)" )
} || return undef ;
$ sth - > execute ( $ id , $ room ) || return undef ;
return 1 ;
} ;
@ -326,7 +354,9 @@ helper has_joined => sub {
my $ self = shift ;
my ( $ session , $ name ) = @ _ ;
my $ ret = 0 ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT * FROM `rooms` WHERE `name`=? AND `id` IN (SELECT `id` FROM `participants` WHERE `participant`=?)" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT * FROM `rooms` WHERE `name`=? AND `id` IN (SELECT `id` FROM `participants` WHERE `participant`=?)" )
} || return undef ;
$ sth - > execute ( $ name , $ session ) || return undef ;
$ ret = 1 if ( $ sth - > rows > 0 ) ;
return $ ret ;
@ -336,7 +366,9 @@ helper has_joined => sub {
helper delete_participants = > sub {
my $ self = shift ;
$ self - > app - > log - > debug ( 'Removing inactive participants from the database' ) ;
my $ sth = eval { $ self - > db - > prepare ( "DELETE FROM participants WHERE (`activity_timestamp` < 7200 OR `activity_timestamp` IS NULL);" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "DELETE FROM `participants` WHERE (`activity_timestamp` < 7200 OR `activity_timestamp` IS NULL);" )
} || return undef ;
$ sth - > execute ( ) || return undef ;
return 1 ;
} ;
@ -346,13 +378,17 @@ helper delete_rooms => sub {
my $ self = shift ;
$ self - > app - > log - > debug ( 'Removing unused rooms' ) ;
my $ timeout = time ( ) - $ config - > { inactivityTimeout } ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT `name` FROM rooms WHERE `activity_timestamp` < $timeout AND `persistent`='0';" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT `name` FROM `rooms` WHERE `activity_timestamp` < $timeout AND `persistent`='0';" )
} || return undef ;
$ sth - > execute ( ) ;
my @ toDeleteName = $ sth - > fetchrow_array ;
my @ toDeleteId = ( ) ;
if ( $ config - > { persistentInactivityTimeout } > 0 ) {
$ timeout = time ( ) - $ config - > { persistentInactivityTimeout } ;
$ sth = eval { $ self - > db - > prepare ( "SELECT `name` FROM rooms WHERE `activity_timestamp` < $timeout AND `persistent`='1';" ) } || return undef ;
$ sth = eval {
$ self - > db - > prepare ( "SELECT `name` FROM `rooms` WHERE `activity_timestamp` < $timeout AND `persistent`='1';" )
} || return undef ;
$ sth - > execute ( ) ;
while ( my $ room = $ sth - > fetchrow_array ) {
push @ toDeleteName , $ room ;
@ -427,9 +463,13 @@ helper ping_room => sub {
my ( $ name ) = @ _ ;
my $ data = $ self - > get_room ( $ name ) ;
return undef unless ( $ data ) ;
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `rooms` SET `activity_timestamp`=? WHERE `id`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `rooms` SET `activity_timestamp`=? WHERE `id`=?;" )
} || return undef ;
$ sth - > execute ( time ( ) , $ data - > { id } ) || return undef ;
$ sth = eval { $ self - > db - > prepare ( "UPDATE `participants` SET `activity_timestamp`=? WHERE `id`=? AND `participant`=?;" ) } || return undef ;
$ sth = eval {
$ self - > db - > prepare ( "UPDATE `participants` SET `activity_timestamp`=? WHERE `id`=? AND `participant`=?;" )
} || return undef ;
$ sth - > execute ( time ( ) , $ data - > { id } , $ self - > session ( 'name' ) ) || return undef ;
$ self - > app - > log - > debug ( $ self - > session ( 'name' ) . " pinged the room $name" ) ;
return 1 ;
@ -492,7 +532,9 @@ helper set_join_pass => sub {
my $ self = shift ;
my ( $ room , $ pass ) = @ _ ;
return undef unless ( % { $ self - > get_room ( $ room ) } ) ;
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `rooms` SET `join_password`=? WHERE `name`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `rooms` SET `join_password`=? WHERE `name`=?;" )
} || return undef ;
$ pass = ( $ pass ) ? Crypt::SaltedHash - > new ( algorithm = > 'SHA-256' ) - > add ( $ pass ) - > generate : undef ;
$ sth - > execute ( $ pass , $ room ) || return undef ;
if ( $ pass ) {
@ -513,13 +555,17 @@ helper set_owner_pass => sub {
# For now, setting an owner password makes the room persistant
# Might be separated in the future
if ( $ pass ) {
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `rooms` SET `owner_password`=?,`persistent`='1' WHERE `name`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `rooms` SET `owner_password`=?,`persistent`='1' WHERE `name`=?;" )
} || return undef ;
my $ pass = Crypt::SaltedHash - > new ( algorithm = > 'SHA-256' ) - > add ( $ pass ) - > generate ;
$ sth - > execute ( $ pass , $ room ) || return undef ;
$ self - > app - > log - > debug ( $ self - > session ( 'name' ) . " has set an owner password on room $room, which is now persistent" ) ;
}
else {
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `rooms` SET `owner_password`=?,`persistent`='0' WHERE `name`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `rooms` SET `owner_password`=?,`persistent`='0' WHERE `name`=?;" )
} || return undef ;
$ sth - > execute ( undef , $ room ) || return undef ;
$ self - > app - > log - > debug ( $ self - > session ( 'name' ) . " has removed the owner password on room $room, which is not persistent anymore" ) ;
}
@ -531,7 +577,9 @@ helper add_notification => sub {
my ( $ room , $ email ) = @ _ ;
my $ data = $ self - > get_room ( $ room ) ;
return undef unless ( $ data ) ;
my $ sth = eval { $ self - > db - > prepare ( "INSERT INTO `notifications` (`id`,`email`) VALUES (?,?)" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "INSERT INTO `notifications` (`id`,`email`) VALUES (?,?)" )
} || return undef ;
$ sth - > execute ( $ data - > { id } , $ email ) || return undef ;
$ self - > app - > log - > debug ( $ self - > session ( 'name' ) . " has added $email to the list of email which will be notified when someone joins room $room" ) ;
return 1 ;
@ -542,7 +590,9 @@ helper get_notification => sub {
my $ self = shift ;
my ( $ room ) = @ _ ;
$ room = $ self - > get_room ( $ room ) || return undef ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT `email` FROM `notifications` WHERE `id`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT `email` FROM `notifications` WHERE `id`=?;" )
} || return undef ;
$ sth - > execute ( $ room - > { id } ) || return undef ;
my @ res ;
while ( my @ row = $ sth - > fetchrow_array ) {
@ -557,7 +607,9 @@ helper remove_notification => sub {
my ( $ room , $ email ) = @ _ ;
my $ data = $ self - > get_room ( $ room ) ;
return undef unless ( $ data ) ;
my $ sth = eval { $ self - > db - > prepare ( "DELETE FROM `notifications` WHERE `id`=? AND `email`=?" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "DELETE FROM `notifications` WHERE `id`=? AND `email`=?" )
} || return undef ;
$ sth - > execute ( $ data - > { id } , $ email ) || return undef ;
$ self - > app - > log - > debug ( $ self - > session ( 'name' ) . " has removed $email from the list of email which are notified when someone joins room $room" ) ;
return 1 ;
@ -570,7 +622,9 @@ helper ask_for_name => sub {
my ( $ room , $ set ) = @ _ ;
my $ data = $ self - > get_room ( $ room ) ;
return undef unless ( $ data ) ;
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `rooms` SET `ask_for_name`=? WHERE `name`=?" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `rooms` SET `ask_for_name`=? WHERE `name`=?" )
} || return undef ;
$ sth - > execute ( $ set , $ room ) || return undef ;
$ self - > app - > log - > debug ( $ self - > session ( 'name' ) . " has configured room $room to ask for a name before joining it" ) ;
return 1 ;
@ -591,7 +645,9 @@ helper add_invitation => sub {
my $ data = $ self - > get_room ( $ room ) ;
my $ id = $ self - > get_random ( 30 ) ;
return undef unless ( $ data ) ;
my $ sth = eval { $ self - > db - > prepare ( "INSERT INTO `invitations` (`id`,`from`,`token`,`email`,`timestamp`) VALUES (?,?,?,?,?)" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "INSERT INTO `invitations` (`id`,`from`,`token`,`email`,`timestamp`) VALUES (?,?,?,?,?)" )
} || return undef ;
$ sth - > execute ( $ data - > { id } , $ from , $ id , $ email , time ( ) ) || return undef ;
$ self - > app - > log - > debug ( $ self - > session ( 'name' ) . " has invited $email to join room $room" ) ;
return $ id ;
@ -602,7 +658,9 @@ helper add_invitation => sub {
helper get_invitation = > sub {
my $ self = shift ;
my ( $ id ) = @ _ ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT * FROM `invitations` WHERE `token`=? AND `processed`='0';" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT * FROM `invitations` WHERE `token`=? AND `processed`='0';" )
} || return undef ;
$ sth - > execute ( $ id ) || return undef ;
return $ sth - > fetchall_hashref ( 'token' ) - > { $ id } ;
} ;
@ -610,11 +668,13 @@ helper get_invitation => sub {
# Find invitations which have a unprocessed repsponse
helper find_invitations = > sub {
my $ self = shift ;
my $ sth = eval { $ self - > db - > prepare ( "SELECT `token` FROM `invitations` WHERE `from`=? AND `response` IS NOT NULL AND `processed`='0';" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT `token` FROM `invitations` WHERE `from`=? AND `response` IS NOT NULL AND `processed`='0';" )
} || return undef ;
$ sth - > execute ( $ self - > session ( 'name' ) ) || return undef ;
my @ res ;
while ( my @ row = $ sth - > fetchrow_array ) {
push @ res , $ row [ 0 ] ;
while ( my $ invit = $ sth - > fetchrow_array ) {
push @ res , $ invit ;
}
return @ res ;
} ;
@ -622,7 +682,9 @@ helper find_invitations => sub {
helper respond_invitation = > sub {
my $ self = shift ;
my ( $ id , $ response , $ message ) = @ _ ;
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `invitations` SET `response`=?,`message`=? WHERE `token`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `invitations` SET `response`=?,`message`=? WHERE `token`=?;" )
} || return undef ;
$ sth - > execute ( $ response , $ message , $ id ) || return undef ;
return 1 ;
} ;
@ -631,7 +693,9 @@ helper respond_invitation => sub {
helper processed_invitation = > sub {
my $ self = shift ;
my ( $ id ) = @ _ ;
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `invitations` SET `processed`='1' WHERE `token`=?;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `invitations` SET `processed`='1' WHERE `token`=?;" )
} || return undef ;
$ sth - > execute ( $ id ) || return undef ;
return 1 ;
} ;
@ -642,7 +706,9 @@ helper delete_invitations => sub {
$ self - > app - > log - > debug ( 'Removing expired invitations' ) ;
# Invitation older than 2 hours doesn't make much sense
my $ timeout = time ( ) - 7200 ;
my $ sth = eval { $ self - > db - > prepare ( "DELETE FROM `invitations` WHERE `timestamp` < $timeout;" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "DELETE FROM `invitations` WHERE `timestamp` < $timeout;" )
} || return undef ;
$ sth - > execute ( ) || return undef ;
return 1 ;
} ;
@ -659,7 +725,9 @@ helper check_invite_token => sub {
if ( ! $ data || ! $ token ) {
return undef ;
}
my $ sth = eval { $ self - > db - > prepare ( "SELECT * FROM `invitations` WHERE id=? AND token=? AND (`response` IS NULL OR `response`='later');" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "SELECT * FROM `invitations` WHERE `id`=? AND `token`=? AND (`response` IS NULL OR `response`='later');" )
} || return undef ;
$ sth - > execute ( $ data - > { id } , $ token ) || return undef ;
if ( $ sth - > rows == 1 ) {
$ ret = 1 ;
@ -681,7 +749,9 @@ helper create_pad => sub {
if ( ! $ data - > { etherpad_group } ) {
my $ group = $ ec - > create_group ( ) || undef ;
return undef unless ( $ group ) ;
my $ sth = eval { $ self - > db - > prepare ( "UPDATE `rooms` SET `etherpad_group`=? WHERE `name`='$room';" ) } || return undef ;
my $ sth = eval {
$ self - > db - > prepare ( "UPDATE `rooms` SET `etherpad_group`=? WHERE `name`='$room';" )
} || return undef ;
$ sth - > execute ( $ group ) || return undef ;
$ data = $ self - > get_room ( $ room ) ;
}
@ -728,6 +798,7 @@ get '/about' => sub {
get '/help' = > 'help' ;
# Route for the admin page
# This one displas the details of a room
get '/admin/(:room)' = > sub {
my $ self = shift ;
my $ room = $ self - > stash ( 'room' ) ;
@ -741,6 +812,7 @@ get '/admin/(:room)' => sub {
}
$ self - > stash ( room = > $ room ) ;
} = > 'manage_room' ;
# And this one displays the list of existing rooms
get '/admin' = > sub {
my $ self = shift ;
$ self - > delete_rooms ;