Commit 244034cd authored by Leigh B Stoller's avatar Leigh B Stoller

New List Images page. This closes issue #109.

parent 56d8fb53
<?php
#
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir("..");
include("defs.php3");
chdir("apt");
include("quickvm_sup.php");
$page_title = "Image List";
#
# Verify page arguments.
#
$optargs = OptionalPageArguments("target_user", PAGEARG_USER,
"all", PAGEARG_BOOLEAN);
#
# Get current user.
#
RedirectSecure();
$this_user = CheckLoginOrRedirect();
# Ignore all flag if not an admin
if (!ISADMIN()) {
$all = 0;
}
elseif (!isset($all)) {
$all = 0;
}
if (!isset($target_user)) {
$target_user = $this_user;
}
if (!$this_user->SameUser($target_user)) {
if (!ISADMIN()) {
SPITUSERERROR("You do not have permission to view ".
"target user's images");
exit();
}
# Do not show admin access images if targeting a different user.
$all = 0;
}
$target_idx = $target_user->uid_idx();
$projlist = $target_user->ProjectAccessList($TB_PROJECT_CREATEEXPT);
SPITHEADER(1);
echo "<link rel='stylesheet'
href='css/tablesorter-blue.css'>\n";
# Place to hang the toplevel template.
echo "<div id='main-body'></div>\n";
if (ISADMIN() && $all) {
$joinclause = "";
$whereclause = "";
}
else {
#
# User is allowed to view the list of all global images, and all images
# in his project. Include images in the subgroups too, since its okay
# for the all project members to see the descriptors. They need proper
# permission to use/modify the image/descriptor of course, but that is
# checked in the pages that do that stuff. In other words, ignore the
# shared flag in the descriptors.
#
$uid_idx = $target_user->uid_idx();
$joinclause =
"left join image_permissions as p1 on ".
" p1.imageid=i.imageid and p1.permission_type='group' ".
"left join image_permissions as p2 on ".
" p2.imageid=i.imageid and p2.permission_type='user' and ".
" p2.permission_idx='$uid_idx' ".
"left join group_membership as g on ".
" g.uid_idx='$uid_idx' and ".
" (g.pid_idx=i.pid_idx or ".
" g.gid_idx=p1.permission_idx) ";
$whereclause = "and (iv.global or p2.imageid is not null or ".
"g.uid_idx is not null) ";
}
$query =
"select distinct iv.*,ov.* from images as i ".
"left join image_versions as iv on ".
" iv.imageid=i.imageid and iv.version=i.version ".
"left join os_info_versions as ov on ".
" i.imageid=ov.osid and ov.vers=i.version ".
"left join osidtoimageid as map on map.osid=i.imageid ".
$joinclause .
"where (iv.ezid = 1 or iv.isdataset = 1) $whereclause ".
"order by i.imagename";
$query_result = DBQueryFatal($query);
$images = array();
while ($row = mysql_fetch_array($query_result)) {
$imageid = $row["imageid"];
$name = $row["imagename"];
$pid = $row["pid"];
$urn = "urn:publicid:IDN+${OURDOMAIN}+image+${pid}//${name}";
$blob = array();
#
# This is for the hidden search filter column. It indicates how
# the user has access to the image. Creator, project, public.
#
$filters = array();
if ($row["creator_idx"] == $target_user->uid_idx()) {
$filters[] = "creator";
}
if (array_key_exists($pid, $projlist)) {
$filters[] = "project";
}
if ($pid == "emulab-ops") {
$filters[] = "system";
}
if ($row["global"] != "0") {
$filters[] = "public";
}
# If none of the filters match, then mark as admin so we can show
# those under a separate checkbox.
if (!count($filters)) {
$filters[] = "admin";
}
$blob["imageid"] = $imageid;
$blob["description"] = $row["description"];
$blob["imagename"] = $row["imagename"];
$blob["pid"] = $row["pid"];
$blob["pid_idx"] = $row["pid_idx"];
$blob["global"] = $row["global"];
$blob["creator"] = $row["creator"];
$blob["creator_idx"] = $row["creator_idx"];
$blob["urn"] = $urn;
$blob["filter"] = implode(",", $filters);
$blob["url"] = $TBBASE . "/" .
CreateURL("showimageid",
URLARG_IMAGEID, $imageid);
$images[$imageid] = $blob;
}
echo "<script type='text/plain' id='images-json'>\n";
echo htmlentities(json_encode($images)) . "\n";
echo "</script>\n";
echo "<script type='text/javascript'>\n";
$isadmin = (isset($this_user) && ISADMIN() ? 1 : 0);
echo " window.ISADMIN = $isadmin;\n";
echo " window.ALL = $all;\n";
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.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/bootstrap.js'></script>\n";
echo "<script src='js/lib/require.js' data-main='js/images'></script>\n";
SPITFOOTER();
?>
require(window.APT_OPTIONS.configObject,
['underscore', 'js/quickvm_sup', 'moment',
'js/lib/text!template/images.html'
],
function (_, sup, moment, mainString)
{
'use strict';
var mainTemplate = _.template(mainString);
function initialize()
{
window.APT_OPTIONS.initialize(sup);
// Image data
var images = JSON.parse(_.unescape($('#images-json')[0].textContent));
// Generate the main template.
var html = mainTemplate({
"images" : images,
"all" : window.ISADMIN && window.ALL,
"isadmin" : window.ISADMIN,
"manual" : window.MANUAL,
});
$('#main-body').html(html);
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
placement: 'auto',
});
// This activates the tooltip subsystem.
$('[data-toggle="tooltip"]').tooltip({
delay: {"hide" : 500, "show" : 150},
placement: 'auto',
});
$('body').on('click', function (e) {
$('[data-toggle="popover"]').each(function () {
//the 'is' for buttons that trigger popups
//the 'has' for icons within a button that triggers a popup
if (!$(this).is(e.target) &&
$(this).has(e.target).length === 0 &&
$('.popover').has(e.target).length === 0) {
$(this).popover('hide');
}
});
});
// Bind handlers for the checkboxes.
$('#my-images, #project-images, #public-images, ' +
'#admin-images, #system-images')
.change(function () {
SetFilters();
});
var table = $("#images-table")
.tablesorter({
theme : 'blue',
// initialize zebra and filter widgets
widgets: ["zebra", "filter"],
widgetOptions: {
// include child row content while filtering, if true
filter_childRows : true,
// include all columns in the search.
filter_anyMatch : true,
// 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,
// Search as typing
filter_liveSearch : true,
},
headers: {
3: {sorter: false},
4: {sorter: false},
},
});
/*
* We have to implement our own live search cause we want to combine
* the search box with the checkbox filters. To do that, we have to
* call SetFilters() on the table directly.
*/
var search_timeout = null;
$("#images-search").on("search keyup", function (event) {
var userInput = $("#images-search").val();
window.clearTimeout(search_timeout);
search_timeout =
window.setTimeout(function() {
var filters = $.tablesorter.getFilters($('#images-table'));
filters[6] = userInput;
console.info("Search", filters);
$.tablesorter.setFilters($('#images-table'), filters, true);
}, 500);
});
SetFilters();
}
function SetFilters()
{
var tmp = [];
var filters = $.tablesorter.getFilters($('#images-table'));
// The "any" filter needs a value or everything disappears.
// If there is a term in the search box, it will have a value.
if (filters[6] === undefined) {
filters[6] = "";
}
if ($('#my-images').is(":checked")) {
tmp.push("creator");
}
if ($('#project-images').is(":checked")) {
tmp.push("project");
}
if ($('#system-images').is(":checked")) {
tmp.push("system");
}
if ($('#public-images').is(":checked")) {
tmp.push("public");
}
if (window.ALL) {
if ($('#admin-images').is(":checked")) {
tmp.push("admin");
}
}
if (tmp.length) {
// regex search, plain | does not work.
filters[5] = "/" + tmp.join("|") + "/";
}
else {
// Hmm, an empty string will get everything.
filters[5] = "WHY";
}
console.info("SetFilters", filters);
$.tablesorter.setFilters($('#images-table'), filters, true);
}
$(document).ready(initialize);
});
......@@ -265,7 +265,8 @@ $PAGEHEADER_FUNCTION = function($thinheader = 0, $ignore1 = NULL,
if ($login_user->IsActive()) {
echo " <li class='divider'></li>
<li><a href='list-datasets.php'>List Datasets</a></li>
<li><a href='create-dataset.php'>Create Dataset</a></li>";
<li><a href='create-dataset.php'>Create Dataset</a></li>
<li><a href='images.php'>List Images</a></li>";
echo " <li class='divider'></li>\n";
$then = time() - (90 * 3600 * 24);
echo " <li><a href='activity.php?user=$login_uid&min=$then'>
......@@ -295,6 +296,8 @@ $PAGEHEADER_FUNCTION = function($thinheader = 0, $ignore1 = NULL,
All Profiles</a></li>
<li><a href='list-datasets.php?all=1'>
All Datasets</a></li>
<li><a href='images.php?all=1'>
All Images</a></li>
<li><a href='lists.php'>
Users/Projects</a></li>";
......
<style type="text/css">
.popover {
width:auto !important;
max-width: none !important;
min-width: 450 !important;
}
.hidden-column {
display: none;
}
</style>
<div class='row'>
<div class='col-sm-12'>
<div class='panel panel-default'>
<div class='panel-body' id="images-div">
<div>
<label class="checkbox-inline">
<input type="checkbox" id="my-images" checked>
<span data-toggle='tooltip'
data-container="body"
data-trigger="hover"
title='Images you have created'>
My Images
</span>
</label>
<label class="checkbox-inline">
<input type="checkbox" id="project-images" checked>
<span data-toggle='tooltip'
data-container="body"
data-trigger="hover"
title='Images created in projects you are a member of'>
Project Images
</span>
</label>
<label class="checkbox-inline">
<input type="checkbox" id="system-images" checked>
<span data-toggle='tooltip'
data-container="body"
data-trigger="hover"
title='Official images created by system adminstrators'>
System Images
</span>
</label>
<label class="checkbox-inline">
<input type="checkbox" id="public-images" checked>
<span data-toggle='tooltip'
data-container="body"
data-trigger="hover"
title='Public images created by anyone'>
Public Images
</span>
</label>
<% if (all) { %>
<label class="checkbox-inline">
<input type="checkbox" id="admin-images">
<span data-toggle='tooltip'
data-container="body"
data-trigger="hover"
title='All other images (red-dot mode)'>
Red-Dot
</span>
</label>
<% } %>
<a href='#' class='btn btn-xs' data-toggle='modal'
data-target='#help-modal'>
<span class='glyphicon glyphicon-question-sign'
style='margin-bottom: 4px;'>
</span></a>
</div>
<div>
<input class='form-control search' type='search' data-column='all'
id='images-search' placeholder='Search'>
</div>
<div class="table-responsive">
<table class='tablesorter' id='images-table'>
<thead>
<tr>
<th>Name</th>
<th>Creator</th>
<th>Project</th>
<th class="sorter-false">Description</th>
<th class="sorter-false">URN</th>
<th class="hidden-column">Filters</th>
</tr>
</thead>
<tbody>
<% var creator_re = /(creator|project)/; %>
<% var project_re = /project/; %>
<% _.each(images, function(value, imageid) { %>
<tr>
<% if (isadmin) { %>
<td><a href="<%- value.url %>"><%= value.imagename %></td>
<% } else { %>
<td><%= value.imagename %></td>
<% } %>
<% if (isadmin || value.filter.search(creator_re) >= 0) { %>
<td><a href='user-dashboard.php?uid=<%- value.creator_idx %>'>
<%= value.creator %></td>
<% } else { %>
<td><%= value.creator %></td>
<% } %>
<% if (isadmin || value.filter.search(project_re) >= 0) { %>
<td><a href='show-project.php?pid=<%- value.pid_idx %>'>
<%= value.pid %></td>
<% } else { %>
<td><%= value.pid %></td>
<% } %>
<td><%- value.description %></td>
<td align="center">
<a href="#"
data-toggle='popover'
data-html='true'
data-trigger='click'
data-title="URN for your geni-lib script or RSpec"
data-content="<input type=text readonly=readonly
class=form-control
onClick='this.select();'
value='<%- value.urn %>'>">
<span class="glyphicon glyphicon-link"></span>
</a>
</td>
<td class="hidden-column"><%- value.filter %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div id='help-modal' class='modal fade'>
<div class='modal-dialog'>
<div class='modal-content'>
<div class='modal-header'>
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>&times;</button>
<h4 class='modal-title text-center'>How to use an image?</h4>
</div>
<div class='modal-body'>
<p>
The images shown on this page are the ones you can use in
your profiles. These include images created by you, system
images created by adminstrators, images created other users
in projects you belong to, and lastly, images that have been
created by users in other projects, that have been marked
public and available for anyone to use.
</p>
<p>
To use an image listed on this page, click on the link icon in the
URN column and copy the text. Paste that text into your
geni-lib script or RSpec. For example, see the
<a href="show-profile.php?project=PortalProfiles&profile=single-pc-ubuntu">single-pc-ubuntu</a> profile.
</p>
<p>
For more information please see the manual.
</p>
</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