Commit 976f63d9 authored by Leigh Stoller's avatar Leigh Stoller

Give me stats or give me death.

parent 9be2e098
......@@ -744,6 +744,7 @@ if (!defined($manifest)) {
}
$instance->SetStatus("provisioned");
$instance->SetManifest($manifest);
$instance->ComputeNodeCounts();
#
# but have to wait for the sliver to be ready, which means polling.
......
......@@ -38,7 +38,7 @@ sub usage()
print("Usage: manage_instance snapshot instance ".
"[-n node_id] [-i imagename] [-u node|all]\n");
print("Usage: manage_instance consoleurl instance node\n");
print("Usage: manage_instance extend instance seconds\n");
print("Usage: manage_instance extend instance [-f] seconds\n");
print("Usage: manage_instance terminate instance\n");
print("Usage: manage_instance refresh instance\n");
print("Usage: manage_instance reboot instance node_id [node_id ...]\n");
......@@ -755,15 +755,26 @@ sub DoTerminate()
#
sub DoExtend()
{
my $force = 0;
usage()
if (!@ARGV);
if (@ARGV == 2) {
my $arg = shift(@ARGV);
if ($arg eq "-f") {
$force = 1;
}
else {
usage();
}
}
my $seconds = shift(@ARGV);
if ($seconds !~ /^\d*$/) {
usage();
}
if ($instance->status() eq "failed") {
if ($instance->status() eq "failed" && !$force) {
fatal("Cannot extend failed instance!");
}
my $slice = $instance->GetGeniSlice();
......
......@@ -154,6 +154,7 @@ APTUIFILES += $(wildcard $(SRCDIR)/aptui/*.gif)
APTUIFILES += $(wildcard $(SRCDIR)/aptui/.htaccess)
APTJSFILES = $(wildcard $(SRCDIR)/aptui/js/*.js)
APTLIBFILES = $(wildcard $(SRCDIR)/aptui/js/lib/*.js)
APTJQRFILES = $(wildcard $(SRCDIR)/aptui/js/lib/jQRangeSlider/*.js)
APTTEMPLATES = $(wildcard $(SRCDIR)/aptui/template/*.html)
APTCSSFILES = $(wildcard $(SRCDIR)/aptui/css/*.css)
APTFONTS = $(wildcard $(SRCDIR)/aptui/fonts/*)
......@@ -218,6 +219,7 @@ ALLBLOB = $(notdir $(BLOBFILES))
ALLAPTUI = $(notdir $(APTUIFILES))
ALLAPTJS = $(notdir $(APTJSFILES))
ALLAPTLIB = $(notdir $(APTLIBFILES))
ALLAPTJQR = $(notdir $(APTJQRFILES))
ALLAPTTEMPLATES = $(notdir $(APTTEMPLATES))
ALLAPTCSS = $(notdir $(APTCSSFILES))
ALLAPTFONTS = $(notdir $(APTFONTS))
......@@ -266,6 +268,7 @@ endif
apt-install: $(addprefix $(INSTALL_WWWDIR)/apt/, $(ALLAPTUI)) \
$(addprefix $(INSTALL_WWWDIR)/apt/js/, $(ALLAPTJS)) \
$(addprefix $(INSTALL_WWWDIR)/apt/js/lib/, $(ALLAPTLIB)) \
$(addprefix $(INSTALL_WWWDIR)/apt/js/lib/jQRangeSlider/, $(ALLAPTJQR)) \
$(addprefix $(INSTALL_WWWDIR)/apt/template/, $(ALLAPTTEMPLATES)) \
$(addprefix $(INSTALL_WWWDIR)/apt/css/, $(ALLAPTCSS)) \
$(addprefix $(INSTALL_WWWDIR)/apt/fonts/, $(ALLAPTFONTS)) \
......
<?php
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -28,6 +28,13 @@ include("quickvm_sup.php");
include("profile_defs.php");
$page_title = "My Profiles";
#
# Verify page arguments.
#
$optargs = OptionalPageArguments("target_user", PAGEARG_USER,
"target_project", PAGEARG_PROJECT,
"min", PAGEARG_INTEGER,
"max", PAGEARG_INTEGER);
#
# Get current user.
#
......@@ -36,64 +43,106 @@ $this_user = CheckLoginOrRedirect();
SPITHEADER(1);
if (!ISADMIN()) {
SPITUSERERROR("Not enough permission to view this page!");
if (isset($target_user)) {
if (!$target_user->SameUser($this_user)) {
SPITUSERERROR("Not enough permission to view this page!");
}
}
elseif (isset($target_project)) {
$approved = 0;
if (!$target_project->IsMember($this_user, $approved) && $approved) {
SPITUSERERROR("Not enough permission to view this page!");
}
}
else {
$target_user = $this_user;
}
}
$instances = array();
#
# First existing instances and then the history table.
# Allow for targeted searches
#
$query1_result =
DBQueryFatal("select i.uuid,i.profile_version,i.created,'' as destroyed, ".
" i.creator,p.uuid as profile_uuid,p.name,p.pid,u.email ".
" from apt_instances as i ".
"left join apt_profile_versions as p on ".
" p.profileid=i.profile_id and ".
" p.version=i.profile_version ".
"left join geni.geni_users as u on u.uuid=i.creator_uuid ".
"order by i.created desc");
$query2_result =
$whereclause = "";
if (isset($target_user)) {
$target_idx = $target_user->idx();
$whereclause = "where h.creator_idx='$target_idx'";
}
elseif (isset($target_project)) {
$target_idx = $target_project->pid_idx();
$whereclause = "where h.pid_idx='$target_idx'";
}
if (isset($min) || isset($max)) {
if ($whereclause != "") {
$whereclause = "$whereclause and ";
}
else {
$whereclause = "where ";
}
if (isset($min)) {
$whereclause .= "UNIX_TIMESTAMP(h.created) > $min ";
if (isset($max)) {
$whereclause .= "and ";
}
}
if (isset($max)) {
$whereclause .= "UNIX_TIMESTAMP(h.created) < $max ";
}
}
$query_result =
DBQueryFatal("select h.uuid,h.profile_version,h.created,h.destroyed, ".
" h.creator,p.uuid as profile_uuid,p.name,p.pid,u.email ".
" h.creator,p.uuid as profile_uuid,p.name,p.pid,u.email, ".
" h.physnode_count,h.virtnode_count, ".
" truncate(h.physnode_count * ".
" ((UNIX_TIMESTAMP(h.destroyed) - ".
" UNIX_TIMESTAMP(h.created)) / 3600.0),2) as phours ".
" from apt_instance_history as h ".
"left join apt_profile_versions as p on ".
" p.profileid=h.profile_id and ".
" p.version=h.profile_version ".
"left join geni.geni_users as u on u.uuid=h.creator_uuid ".
$whereclause . " " .
"order by h.created desc");
if (mysql_num_rows($query1_result) == 0 &&
mysql_num_rows($query2_result) == 0) {
if (mysql_num_rows($query_result) == 0) {
$message = "<b>Oops, there is no activity to show you.</b><br>";
SPITUSERERROR($message);
exit();
}
foreach (array($query1_result, $query2_result) as $query_result) {
if (1) {
while ($row = mysql_fetch_array($query_result)) {
$uuid = $row["uuid"];
$pname = $row["name"];
$pproj = $row["pid"];
$puuid = $row["profile_uuid"];
$pversion = $row["profile_version"];
$created = $row["created"];
$destroyed = $row["destroyed"];
$created = DateStringGMT($row["created"]);
$destroyed = DateStringGMT($row["destroyed"]);
$creator = $row["creator"];
$email = $row["email"];
$pcount = $row["physnode_count"];
$vcount = $row["virtnode_count"];
$phours = $row["phours"];
# Backwards compat.
if (!isset($pproj)) {
$pproj = "";
}
if (!isset($destroyed)) {
$destroyed = "";
}
# If a guest user, use email instead.
if (isset($email)) {
$creator = $email;
}
$instance = array();
$instance["uuid"] = $uuid;
$instance["p_name"] = $pname;
$instance["p_pid"] = $pproj;
$instance["p_uuid"] = $puuid;
$instance["p_version"] = $pversion;
$instance["creator"] = $creator;
$instance["created"] = $created;
$instance["destroyed"] = $destroyed;
# Save space with array instead of hash.
$instance =
array($pname, $pproj, $puuid, $pcount, $vcount,
$creator, $created, $destroyed, $phours);
$instances[] = $instance;
}
}
......@@ -103,11 +152,47 @@ echo "<div id='activity-body'></div>\n";
echo "<script type='text/javascript'>\n";
echo " window.AJAXURL = 'server-ajax.php';\n";
if (isset($min)) {
echo " window.MIN = $min;\n";
}
else {
echo " window.MIN = null;\n";
}
if (isset($max)) {
echo " window.MAX = $max;\n";
}
else {
echo " window.MAX = null;\n";
}
if (isset($target_user)) {
echo " window.ARG = 'user=$target_idx';\n";
}
elseif (isset($target_project)) {
echo " window.ARG = 'project=$target_idx';\n";
}
else {
echo " window.ARG = null;\n";
}
echo "</script>\n";
echo "<script type='text/plain' id='instances-json'>\n";
echo json_encode($instances);
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<link rel='stylesheet' href='css/tablesorter.css'>\n";
echo "<link rel='stylesheet' href='css/jQRangeSlider.css'>\n";
echo "<script src='js/lib/jquery.min.js'></script>\n";
echo "<script src='js/lib/jquery-ui.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQRangeSliderMouseTouch.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQRangeSliderDraggable.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQRangeSliderHandle.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQRangeSliderBar.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQRangeSliderLabel.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQRangeSlider.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQDateRangeSliderHandle.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQDateRangeSlider.js'></script>\n";
echo "<script src='js/lib/jQRangeSlider/jQRuler.js'></script>\n";
echo "<script src='js/lib/jquery.tablesorter.min.js'></script>\n";
echo "<script src='js/lib/jquery.tablesorter.widgets.min.js'></script>\n";
echo "<script src='js/lib/jquery.tablesorter.widget-math.js'></script>\n";
echo "<script src='js/lib/bootstrap.js'></script>\n";
echo "<script src='js/lib/require.js' data-main='js/activity'></script>\n";
......
......@@ -332,5 +332,28 @@ class Instance
}
return 0;
}
#
# Determine user current usage.
#
function CurrentUsage($user) {
$user_idx = $user->idx();
$pcount = 0;
$phours = 0;
$query_result =
DBQueryFatal("select physnode_count, ".
" truncate(physnode_count * ".
" ((UNIX_TIMESTAMP(now()) - ".
" UNIX_TIMESTAMP(created)) / 3600.0),2) as phours ".
" from apt_instances ".
"where creator_idx='$user_idx' and physnode_count>0");
while ($row = mysql_fetch_array($query_result)) {
$pcount += $row["physnode_count"];
$phours += $row["phours"];
}
return array($pcount, $phours);
}
}
?>
require(window.APT_OPTIONS.configObject,
['underscore', 'js/quickvm_sup',
['underscore', 'js/quickvm_sup', 'moment',
'js/lib/text!template/activity.html'],
function (_, sup, profileString)
function (_, sup, moment, profileString)
{
'use strict';
var ajaxurl = null;
......@@ -11,11 +11,86 @@ function (_, sup, profileString)
{
window.APT_OPTIONS.initialize(sup);
ajaxurl = window.AJAXURL;
var default_min = new Date(2014, 6, 1);
var default_max = new Date();
if (window.MIN) {
default_min = new Date(window.MIN * 1000);
}
if (window.MAX) {
default_max = new Date(window.MAX * 1000);
}
var instances =
JSON.parse(_.unescape($('#instances-json')[0].textContent));
var activity_html = profileTemplate({instances: instances});
$('#activity-body').html(activity_html);
// Format dates with moment before display.
$('.format-date').each(function() {
var date = $.trim($(this).html());
if (date != "") {
$(this).html(moment($(this).html()).format("ll"));
}
});
$("#date-slider").dateRangeSlider({
bounds: {min: new Date(2014, 6, 1),
max: new Date()},
defaultValues: {min: default_min, max: default_max},
arrows: false,
});
// Handler for the date range search button.
$('#slider-go-button').click(function() {
var dateValues = $("#date-slider").dateRangeSlider("values");
var min = Math.floor(dateValues.min.getTime()/1000);
var max = Math.floor(dateValues.max.getTime()/1000);
var url = "activity.php?";
if (window.ARG) {
url = url + window.ARG + "&";
}
url = url + "min=" + min + "&max=" + max;
window.location.replace(url);
});
var tablename = "#activity_table";
var searchname = "#activity_table_search";
var table = $(tablename)
.tablesorter({
theme : 'green',
//cssChildRow: "tablesorter-childRow",
// initialize zebra and filter widgets
widgets: ["zebra", "filter", "math"],
widgetOptions: {
// include child row content while filtering, if true
filter_childRows : true,
// include all columns in the search.
filter_anyMatch : true,
// class name applied to filter row and each input
filter_cssFilter : 'form-control',
// search from beginning
filter_startsWith : false,
// Set this option to false for case sensitive search
filter_ignoreCase : true,
// Only one search box.
filter_columnFilters : false,
// data-math attribute
math_data : 'math',
// ignore first column
math_ignore : [0],
// integers
math_mask : '',
}
});
// Target the $('.search') input using built in functioning
// this binds to the search using "search" and "keyup"
// Allows using filter_liveSearch or delayed search &
// pressing escape to cancel the search
$.tablesorter.filter.bindSearch(table, $(searchname));
}
$(document).ready(initialize);
});
......@@ -10,7 +10,7 @@ require(window.APT_OPTIONS.configObject,
'js/lib/text!template/snapshot-help.html',
'js/lib/text!template/oneonly-modal.html',
'js/lib/text!template/approval-modal.html',
'jquery-ui', 'contextmenu'],
'contextmenu'],
function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
ShowExtendModal, statusString, waitwaitString, oopsString,
registerString, terminateString,
......
......@@ -7,6 +7,15 @@ function (sup, moment)
function initialize()
{
window.APT_OPTIONS.initialize(sup);
var default_min = new Date(2014, 6, 1);
var default_max = new Date();
if (window.MIN) {
default_min = new Date(window.MIN * 1000);
}
if (window.MAX) {
default_max = new Date(window.MAX * 1000);
}
// Format dates with moment before display.
$('.format-date').each(function() {
......@@ -16,8 +25,23 @@ function (sup, moment)
.format("MMM Do, h:mm a"));
}
});
$("#date-slider").dateRangeSlider({
bounds: {min: new Date(2014, 6, 1),
max: new Date()},
defaultValues: {min: default_min, max: default_max},
arrows: false,
});
InitTable("uid");
InitTable("pid");
// Handler for the date range search button.
$('#slider-go-button').click(function() {
var dateValues = $("#date-slider").dateRangeSlider("values");
var min = Math.floor(dateValues.min.getTime()/1000);
var max = Math.floor(dateValues.max.getTime()/1000);
window.location.replace("sumstats.php?min=" + min +
"&max=" + max);
});
}
function InitTable(name)
......
......@@ -26,8 +26,8 @@ include("defs.php3");
include_once("geni_defs.php");
chdir("apt");
include("quickvm_sup.php");
include("profile_defs.php");
include("instance_defs.php");
include_once("profile_defs.php");
include_once("instance_defs.php");
$page_title = "My Experiments";
$dblink = GetDBLink("sa");
......@@ -70,7 +70,10 @@ if ($all && ISADMIN()) {
$query_result1 =
DBQueryFatal("select a.*,s.expires,s.hrn,u.email, ".
" (UNIX_TIMESTAMP(now()) > ".
" UNIX_TIMESTAMP(s.expires)) as expired ".
" UNIX_TIMESTAMP(s.expires)) as expired, ".
" truncate(a.physnode_count * ".
" ((UNIX_TIMESTAMP(now()) - ".
" UNIX_TIMESTAMP(a.created)) / 3600.0),2) as phours ".
" from apt_instances as a ".
"left join geni.geni_slices as s on ".
" s.uuid=a.slice_uuid ".
......@@ -81,7 +84,10 @@ else {
$query_result1 =
DBQueryFatal("select a.*,s.expires,s.hrn,u.email, ".
" (UNIX_TIMESTAMP(now()) > ".
" UNIX_TIMESTAMP(s.expires)) as expired ".
" UNIX_TIMESTAMP(s.expires)) as expired, ".
" truncate(a.physnode_count * ".
" ((UNIX_TIMESTAMP(now()) - ".
" UNIX_TIMESTAMP(a.created)) / 3600.0),2) as phours ".
" from apt_instances as a ".
"left join geni.geni_slices as s on ".
" s.uuid=a.slice_uuid ".
......@@ -91,7 +97,10 @@ else {
$query_result2 =
DBQueryFatal("select distinct a.*,s.expires,s.hrn,u.email, ".
" (UNIX_TIMESTAMP(now()) > ".
" UNIX_TIMESTAMP(s.expires)) as expired ".
" UNIX_TIMESTAMP(s.expires)) as expired, ".
" truncate(a.physnode_count * ".
" ((UNIX_TIMESTAMP(now()) - ".
" UNIX_TIMESTAMP(a.created)) / 3600.0),2) as phours ".
" from apt_instances as a ".
"left join geni.geni_slices as s on ".
" s.uuid=a.slice_uuid ".
......@@ -124,11 +133,10 @@ function SPITROWS($all, $name, $result)
}
echo " <th>Project</th>
<th>Status</th>
<th>Cluster</th>\n";
if (ISADMIN()) {
echo " <th>P</th>\n";
echo " <th>V</th>\n";
}
<th>Cluster</th>
<th>PCs</th>
<th>PHours<b>[1]</b></th>
<th>VMs</th>\n";
echo " <th>Created</th>
<th>Expires</th>
</tr>
......@@ -150,6 +158,7 @@ function SPITROWS($all, $name, $result)
$cluster = $urn_mapping[$urn];
$pcount = $row["physnode_count"];
$vcount = $row["virtnode_count"];
$phours = $row["phours"];
list($foo,$hrn) = preg_split("/\./", $row["hrn"]);
$email = $row["email"];
# If a guest user, use email instead.
......@@ -191,42 +200,31 @@ function SPITROWS($all, $name, $result)
}
echo " <td>$status</td>\n";
echo " <td>$cluster</td>\n";
if (ISADMIN()) {
echo "<td>$pcount</td>";
echo "<td>$vcount</td>";
}
echo " <td>$pcount</td>";
echo " <td>$phours</td>";
echo " <td>$vcount</td>";
echo" <td class='format-date'>$created</td>
<td class='format-date'>$expires</td>
</tr>\n";
}
echo " </tbody>
</table>\n";
echo "[1] <b>PHours</b>: Number of nodes times number of hours in use.<br>";
}
echo "<div class='row'>
<div class='col-lg-10 col-lg-offset-1
col-md-10 col-md-offset-1
<div class='col-lg-12 col-lg-offset-0
col-md-12 col-md-offset-0
col-sm-12 col-sm-offset-0
col-xs-12 col-xs-offset-0'>\n";
if (mysql_num_rows($query_result1) == 0) {
$message = "<b>No experiments to show you. Maybe you want to ".
"<a href='instantiate.php'>start one?</a></b><br>";
if (ISADMIN()) {
$message .= "<img src='images/redball.gif'>".
"<a href='myexperiments.php?all=1'>";
$message .= "Show all user Experiments</a><br>";
}
echo $message;
}
else {
SPITROWS($all, "table1", $query_result1);
if (ISADMIN() && !$all) {
echo "<img src='images/redball.gif'>
<a href='myexperiments.php?all=1'>Show all user Experiments</a>\n";
echo "<br>\n";
}
}
if ($query_result2 && mysql_num_rows($query_result2)) {
echo "<br>\n";
......
......@@ -40,6 +40,8 @@ $CHANGEPSWD_PAGE= "changepswd.php";
$MAXGUESTINSTANCES = 10;
$WITHPUBLISHING = 0;
include_once("instance_defs.php");
#
# Global flag to disable accounts. We do this on some pages which
# should not display login/account info.
......@@ -281,10 +283,19 @@ $PAGEHEADER_FUNCTION = function($thinheader = 0, $ignore1 = NULL,
<li class='divider'></li>
<li><a href='list-datasets.php?all=1'>List Datasets</a></li>
<li><a href='create-dataset.php'>Create Dataset</a></li>";
echo " <li class='divider'></li>\n";
if (ISADMIN()) {
echo " <li class='divider'></li>
<li><a href='activity.php'>Activity</a></li>
<li><a href='sumstats.php'>Summary Stats</a></li>";
$then = time() - (30 * 3600 * 24);
echo " <li><a href='activity.php?min=$then'>
History Data</a></li>
<li><a href='sumstats.php'>Summary Stats</a></li>
<li><a href='myexperiments.php?all=1'>
All Experiments</a></li>";
}
else {
$then = time() - (90 * 3600 * 24);
echo " <li><a href='activity.php?user=$login_uid&min=$then'>
My History</a></li>\n";
}
echo " </ul>
</li>\n";
......@@ -303,6 +314,18 @@ $PAGEHEADER_FUNCTION = function($thinheader = 0, $ignore1 = NULL,
echo "<center style='margin-bottom: 5px; margin-top: -8px'>
<font color=red>$message</font></center>\n";
}
if ($login_user) {
list($pcount, $phours) = Instance::CurrentUsage($login_user);
if ($pcount) {
$average = $phours / $pcount;
echo "<center style='margin-bottom: 5px; margin-top: -8px'>
<span class=text-warning>
You are using $pcount physical nodes
(average $average hours per node)
</span></center>\n";
}
}
if (!NOLOGINS() && !$login_user && $page_title != "Login") {
SpitLoginModal("quickvm_login_modal");
......@@ -394,7 +417,7 @@ function SPITREQUIRE($main, $extras = "")
{
global $spatrequired;
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<script src='js/lib/jquery.min.js'></script>\n";
echo $extras;
echo "<script src='js/lib/bootstrap.js'></script>\n";
echo "<script src='js/lib/require.js' data-main='js/$main'></script>\n";
......
......@@ -216,6 +216,7 @@ if (isset($extend) && $extend != "") {
echo "var FOO = null;\n";
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<script src='js/lib/jquery-ui.js'></script>\n";
echo "<script src='js/lib/codemirror-min.js'></script>\n";
echo "<script src='js/lib/bootstrap.js'></script>\n";
echo "<script src='js/lib/require.js' data-main='js/status'></script>";
......
This diff is collapsed.
<div class='row'>
<div class='col-xs-10 col-xs-offset-1'>
<div id='date-slider'></div>
</div>
<button type='button' id='slider-go-button'
class='btn btn-primary btn-xs'
style='margin-left: 20px; margin-top: 40px;'>Go</button>
</div>
<br>
<div class='row'>
<div class='col-lg-12 col-lg-offset-0
col-md-12 col-md-offset-0
col-sm-12 col-sm-offset-0
col-xs-12 col-xs-offset-0'>
<table class='table table-striped table-condensed'>
<input class='form-control search' type='search' data-column='all'
id='activity_table_search' placeholder='Search'>
<table class='tablesorter' id='activity_table'>
<thead>
<tr>
<th>Profile</th>
<th>Pid</th>
<th>Vers</th>
<th>Creator</th>
<th>PCs</th>
<th>PHours</th>
<th>VMs</th>
<th>Created</th>
<th>Destroyed</th>
</tr>
<tr>
<th class='filter-false sorter-false'>Totals</th>
<th class='filter-false sorter-false' colspan=2></th>
<th class='filter-false sorter-false' data-math='col-sum'>0</th>
<th class='filter-false sorter-false' data-math='col-sum'
data-math-mask='##0'>0</th>
<th class='filter-false sorter-false' data-math='col-sum'>0</th>
<th class='filter-false sorter-false' data-math='col-count'>0</th>
<th class='filter-false sorter-false'></th>
</tr>
</thead>
<tfoot>
<tr>
<th class='filter-false sorter-false'>Totals</th>
<th class='filter-false sorter-false' colspan=2></th>
<th class='filter-false sorter-false' data-math='col-sum'>0</th>
<th class='filter-false sorter-false' data-math='col-sum'
data-math-mask='##0'>0</th>
<th class='filter-false sorter-false' data-math='col-sum'>0</th>
<th class='filter-false sorter-false' data-math='col-count'>0</th>
<th class='filter-false sorter-false'></th>
</tr>
</tfoot>
<tbody>
<% _.each(instances, function(instance) { %>
<tr>
<td><%- instance.p_name %></a></td>
<td><%- instance.p_pid %></a></td>
<td>
<a href='manage_profile.php?action=edit&uuid=<%- instance.p_uuid %>'>
<%- instance.p_version %></a>
<a href='manage_profile.php?action=edit&uuid=<%- instance[2] %>'>
<%- instance[0] %></a>
</td>
<td><%- instance.creator %></td>
<td><%- instance.created %></td>
<td><%- instance.destroyed %></td>
<td><%- instance[1] %></a></td>
<td><%- instance[5] %></td>
<td><%- instance[3] %></td>
<td><%- instance[8] %></td>
<td><%- instance[4] %></td>
<td class='format-date'><%- instance[6] %></td>
<td class='format-date'><%- instance[7] %></td>
</tr>
<% }); %>
</tbody>
......
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