Commit 636cea07 authored by Leigh B Stoller's avatar Leigh B Stoller

Couple of small reservation system changes:

1. Show the reason on the listing page, as popover in the last
   column. Makes it easier to approve directly from the listing page if
   you can see the reason.

2. Add optional approval message to pass along to the user.
parent 3d6d4aca
......@@ -643,9 +643,10 @@ sub DoDelete()
#
sub DoApprove()
{
my $optlist = "p:";
my $optlist = "p:F:";
my $portal;
my $errmsg;
my $message;
my %options = ();
if (! getopts($optlist, \%options)) {
......@@ -654,6 +655,10 @@ sub DoApprove()
if (defined($options{"p"})) {
$portal = $options{"p"}
}
if (defined($options{"F"})) {
$message = readfile($options{"F"});
chomp($message);
}
usage()
if (@ARGV != 1 || !defined($portal));
my $uuid = shift(@ARGV);
......@@ -666,7 +671,6 @@ sub DoApprove()
if (!defined($brand)) {
fatal("Bad branding");
}
if (!$this_user->IsAdmin()) {
fatal("No permission to approve reservations")
}
......@@ -674,11 +678,14 @@ sub DoApprove()
if ($aggregate->CheckStatus(\$errmsg, 1)) {
UserError($errmsg);
}
my $blob = {"uuid" => $uuid};
if (defined($message) && $message ne "") {
$blob->{'message'} = $message;
}
# PortalRPC will use the root context in this case, which is
# essentially saying the caller is an admin.
my $response =
APT_Geni::PortalRPC($authority, undef, "ApproveReservation",
{"uuid" => $uuid});
APT_Geni::PortalRPC($authority, undef, "ApproveReservation", $blob);
if (GeniResponse::IsError($response)) {
ExitWithError($response);
}
......@@ -687,7 +694,7 @@ sub DoApprove()
#
# Schedule an announcement for the user.
#
my $blob = $response->value();
$blob = $response->value();
if (exists($blob->{'user'})) {
my $geniuser = GeniUser->Lookup($blob->{'user'}, 1);
if (defined($geniuser) && $geniuser->IsLocal()) {
......
......@@ -927,7 +927,7 @@ sub Reserve($)
chmod(0755, "$fp");
}
my $command = ($asadmin ? "$WAP " : "") . "$TB/sbin/reserve $args";
print STDERR "$command\n";
#print STDERR "$command\n";
if ($asadmin) {
GeniUtil::FlipToElabMan();
......@@ -980,7 +980,7 @@ sub Reserve($)
"uuid" => $reservation->uuid(),
"approved" => $approved,
};
print STDERR Dumper($blob);
#print STDERR Dumper($blob);
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
......@@ -1174,10 +1174,20 @@ sub ApproveReservation($)
my $reservation = Reservation->Lookup($target);
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED)
if (!defined($reservation));
my $idx = $reservation->idx();
my $idx = $reservation->idx();
my $args = "-a -m $idx";
# Write the message to a tempfile to pass in. This will auto unlink.
my $fp;
if (exists($argref->{"message"}) && $argref->{"message"} ne "") {
$fp = File::Temp->new();
print $fp $argref->{"message"};
$args = "-F $fp $args";
chmod(0644, "$fp");
}
GeniUtil::FlipToElabMan();
my $output = GeniUtil::ExecQuiet("$WAP $TB/sbin/reserve -a -m $idx");
my $output = GeniUtil::ExecQuiet("$WAP $TB/sbin/reserve $args");
if ($?) {
GeniUtil::FlipToGeniUser();
......
......@@ -26,6 +26,7 @@ use English;
use Getopt::Std;
use Date::Parse;
use POSIX;
use Text::Wrap;
#
# Configure variables
......@@ -77,10 +78,11 @@ sub usage()
print STDERR " -A Supply file containing admin-only notes about reservation\n";
print STDERR " -N Supply file containing user notes justifying reservation\n";
print STDERR " -D Supply file containing reason why reservation was denied\n";
print STDERR " -F Supply file containing messsage to accompany approval\n";
exit( -1 );
}
my $optlist = "ac:de:fhilm:npqs:t:uA:CD:N:S:U:T:E:Oy";
my $optlist = "ac:de:fhilm:npqs:t:uA:CD:N:S:U:T:E:OyF:";
my $debug = 0;
my $info = 0;
my $list = 0;
......@@ -95,6 +97,7 @@ my $endtime = time + 24 * 60 * 60; # default to ending tomorrow
my $notes = undef;
my $adminnotes = undef;
my $denynotes = undef;
my $approvenotes = undef;
my $approve = 0;
my $pending = 0;
my $tidy = 0;
......@@ -252,6 +255,9 @@ if (defined($options{"A"})) {
if (defined($options{"D"})) {
$denynotes = readfile( $options{"D"} );
}
if (defined($options{"F"})) {
$approvenotes = readfile( $options{"F"} );
}
if (defined($options{"U"})) {
fatal( "-U option requires administrator privileges" ) unless( $admin );
$target_user = User->Lookup($options{"U"});
......@@ -640,6 +646,8 @@ while( 1 ) {
my $s = convert( $starttime );
my $e = convert( $endtime );
if( $approve ) {
$Text::Wrap::columns = 60;
# The reservation is approved -- presumably it is either newly
# approved or edited since first approval. E-mail the user
# unconditionally, since it's probably good for them to hear
......@@ -650,6 +658,11 @@ while( 1 ) {
"starting at $s and ending at\n" .
"$e, has been approved.\n" .
"\n" .
($approvenotes ?
"*****************************************************\n".
wrap("", "", "$approvenotes\n") .
"*****************************************************\n\n"
: "") .
"If you do not intend to use these resources, please\n" .
"cancel this reservation as soon as possible, since\n" .
"the nodes are currently unavailable to other users for\n" .
......
......@@ -219,6 +219,11 @@ $(function ()
delay: {"hide" : 250, "show" : 250},
placement: 'auto',
});
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
placement: 'auto',
container: 'body',
});
}
var xmlthing = sup.CallServerMethod(null, "reserve",
"ListReservations",
......@@ -337,10 +342,12 @@ $(function ()
// Bind the confirm button in the modal. Do the approval.
$('#approve-modal #confirm-approve').click(function () {
sup.HideModal('#approve-modal', function () {
var message = $('#approve-modal .user-message').val().trim();
sup.ShowModal('#waitwait-modal');
var xmlthing = sup.CallServerMethod(null, "reserve",
"Approve",
{"uuid" : uuid,
"message" : message,
"cluster" : cluster});
xmlthing.done(callback);
});
......
......@@ -456,20 +456,34 @@ $(function ()
*/
function Approve()
{
var callback = function(json) {
sup.HideWaitWait();
var callback = function (json) {
sup.HideModal('#waitwait-modal');
if (json.code) {
sup.SpitOops("oops", json.value);
return;
}
window.location.reload(true);
};
sup.ShowWaitWait();
var xmlthing = sup.CallServerMethod(null, "reserve",
"Approve",
{"cluster" : window.CLUSTER,
"uuid" : window.UUID});
xmlthing.done(callback);
// Bind the confirm button in the modal. Do the approval.
$('#approve-modal #confirm-approve').click(function () {
sup.HideModal('#approve-modal', function () {
var message = $('#approve-modal .user-message').val().trim();
sup.ShowModal('#waitwait-modal');
var xmlthing = sup.CallServerMethod(null, "reserve",
"Approve",
{"cluster" : window.CLUSTER,
"uuid" : window.UUID,
"message" : message});
xmlthing.done(callback);
});
});
// Handler so we know the user closed the modal. We need to
// clear the confirm button handler.
$('#approve-modal').on('hidden.bs.modal', function (e) {
$('#approve-modal #confirm-approve').unbind("click");
$('#approve-modal').off('hidden.bs.modal');
})
sup.ShowModal("#approve-modal");
}
function PopulateReservation()
......
......@@ -482,6 +482,22 @@ function Do_Approve()
SPITAJAX_ERROR(-1, "Only administrators can approve reservations");
return;
}
$opt = "";
if (isset($ajax_args["message"])) {
$message = $ajax_args["message"];
if (!TBvalid_fulltext($message)) {
SPITAJAX_ERROR(-1, "Illegal characters in message");
return;
}
$messagefile = tempnam("/tmp", "message");
$fp = fopen($messagefile, "w");
fwrite($fp, $message);
fclose($fp);
chmod($messagefile, 0666);
$opt = "-F $messagefile";
}
$webtask = WebTask::CreateAnonymous();
$this_uid = $this_user->uid();
$urn = $aggregate->urn();
......@@ -489,9 +505,11 @@ function Do_Approve()
$retval = SUEXEC($this_uid, "nobody",
"webmanage_reservations -a '$urn' ".
"-t " . $webtask->task_id() .
" approve -p $PORTAL_GENESIS $uuid",
" approve -p $PORTAL_GENESIS $opt $uuid",
SUEXEC_ACTION_IGNORE);
if (isset($messagefile)) {
unlink($messagefile);
}
if ($retval) {
$webtask->Refresh();
if (!$webtask->exited() || $retval < 0) {
......@@ -505,6 +523,7 @@ function Do_Approve()
$webtask->Delete();
return;
}
$webtask->Delete();
SPITAJAX_RESPONSE("list-reservations.php");
}
......@@ -681,6 +700,7 @@ function Do_ListReservations()
$blob["created"] = $details['created'];
$blob["start"] = $details['start'];
$blob["end"] = $details['end'];
$blob["notes"] = $details['notes'];
$blob["cluster"] = $aggregate->nickname();
$rlist[$uuid] = $blob;
}
......
......@@ -23,6 +23,9 @@
.panel-heading-list {
padding: 2px;
}
.tablesorter-green th.sorter-false .tablesorter-header-inner {
padding-left: 4px;
}
</style>
<div>
<div class='panel panel-default'>
......@@ -60,6 +63,7 @@
<th>Starts</th>
<th>Ends</th>
<th>Status</th>
<th class="sorter-false">Notes</th>
</tr>
</thead>
<tbody>
......@@ -204,6 +208,17 @@
<% } %>
</span>
</td>
<td align="center">
<a href="#"
data-toggle='popover'
data-html='true'
data-trigger='hover'
data-title="Reservation Notes"
data-content="<div class='form-control'
style='height: auto;'><%- value.notes %></div>">
<span>&#8230;</span>
</a>
</td>
</tr>
<% }); %>
</tbody>
......@@ -240,6 +255,11 @@
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>&times;</button>
<center><h4>Confirm to Approve Reservation</h4>
Additional text to send with approval (no text is okay):
<div>
<textarea class='form-control user-message'
rows=5></textarea>
</div>
<br>
<button class='btn btn-danger btn-sm'
id='confirm-approve'>Confirm</a>
......@@ -274,7 +294,7 @@
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>&times;</button>
<center><h4>Confirm to Send Warning Message</h4>
Additional text to send with warning (no text is okay):</span>
Additional text to send with warning (no text is okay):
<div>
<textarea class='form-control user-message'
rows=5></textarea>
......
......@@ -505,4 +505,23 @@
</div>
</div>
</div>
<div id='approve-modal' class='modal fade'>
<div class='modal-dialog'>
<div class='modal-content'>
<div class='modal-body'>
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>&times;</button>
<center><h4>Confirm to Approve Reservation</h4>
Additional text to send with approval (no text is okay):
<div>
<textarea class='form-control user-message'
rows=5></textarea>
</div>
<br>
<button class='btn btn-danger btn-sm'
id='confirm-approve'>Confirm</a>
</center>
</div>
</div>
</div>
</div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment