Commit 16f40f8f authored by Leigh B. Stoller's avatar Leigh B. Stoller

Add first cut at a search templates page. I ended up making a bunch

of changes to the new form wrapper stuff to support more interesting
forms.
parent bfce32d9
......@@ -126,6 +126,7 @@ function FormRenderRadio($name, $attrs)
return $html;
}
function FormRenderSelect($name, $attrs)
{
$html = "<select name=\"formfields[$name]\" ";
......@@ -139,7 +140,12 @@ function FormRenderSelect($name, $attrs)
$html .= $attrs['#javascript'] . " ";
}
$html .= ">\n";
$html .= "<option value=''>Please Select &nbsp</option>\n";
if (isset($attrs['#default']))
$default = $attrs['#default'];
else
$default = "Please Select";
$html .= "<option value=''>$default &nbsp</option>\n";
if (isset($attrs['#options'])) {
while (list ($selectvalue, $selectlabel) = each ($attrs['#options'])) {
......@@ -182,7 +188,6 @@ function FormRenderSubmit($name, $attrs)
{
$html = "";
$html .= "<td align=center colspan=2>";
$html .= "<input type=submit name=\"$name\" ";
if (isset($attrs['#value'])) {
$html .= "value=\"" . $attrs['#value'] . "\" ";
......@@ -199,7 +204,34 @@ function FormRenderSubmit($name, $attrs)
if (isset($attrs['#javascript'])) {
$html .= $attrs['#javascript'] . " ";
}
$html .= "></td>";
$html .= ">";
return $html;
}
function FormRenderImage($name, $attrs)
{
$html = "";
$img = $attrs['#image'];
$html .= "<input type=image name=\"formfields[$name]\" ";
if (isset($attrs['#value'])) {
$html .= "value=\"" . $attrs['#value'] . "\" ";
}
else {
$html .= "value=Submit ";
}
if (isset($attrs['#class'])) {
$html .= "class=\"" . $attrs['#class'] . "\" ";
}
else {
$html .= "class=\"form-image\" ";
}
if (isset($attrs['#javascript'])) {
$html .= $attrs['#javascript'] . " ";
}
$html .= "src=$img ";
$html .= ">";
return $html;
}
......@@ -213,6 +245,23 @@ function FormRenderTable($name, $attributes, $submitted)
return $html;
}
# Render a list of elements together.
function FormRenderList($name, $attributes, $submitted)
{
$html = "";
while (list ($subname, $subattrs) = each ($attributes['#elements'])) {
if ($submitted && array_key_exists($subname, $submitted)) {
$subattrs['#value'] = $submitted[$subname];
}
$html .= FormRenderElement($subname, $subattrs, $submitted);
if (isset($subattrs['#label']))
$html .= $subattrs['#label'] . " &nbsp; ";
$html .= " &nbsp; ";
}
return $html;
}
function FormRenderElement($name, $attributes, $submitted)
{
$field_html = null;
......@@ -232,11 +281,16 @@ function FormRenderElement($name, $attributes, $submitted)
break;
case "hidden":
$value = $attributes['#value'];
$field_html .= "<input type=hidden name=$name value=\"$value\">\n";
$field_html .=
"<input type=hidden name=\"formfields[$name]\" ".
"value=\"$value\">\n";
break;
case "submit":
$field_html = FormRenderSubmit($name, $attributes);
break;
case "image":
$field_html = FormRenderImage($name, $attributes);
break;
case "checkbox":
$field_html = FormRenderCheckBox($name, $attributes);
break;
......@@ -262,6 +316,9 @@ function FormRenderElement($name, $attributes, $submitted)
case "table":
$field_html = FormRenderTable($name, $attributes, $submitted);
break;
case "list":
$field_html = FormRenderList($name, $attributes, $submitted);
break;
}
return $field_html;
}
......@@ -311,7 +368,8 @@ function FormRenderElements($fields, $submitted)
}
$html .= "<tr>";
if ($attributes['#type'] != "submit") {
if ($attributes['#type'] != "submit" &&
!isset($attributes['#colspan'])) {
$html .= "<td $mouseover $cols>";
# Required fields mark with *
......@@ -335,8 +393,11 @@ function FormRenderElements($fields, $submitted)
$html .= "<td>$field_html</td>";
}
}
else
else {
$html .= "<td align=center colspan=2>";
$html .= "$field_html";
$html .= "</td>\n";
}
$html .= "</tr>\n";
}
}
......@@ -416,30 +477,76 @@ function FormRender($attributes, $errors, $fields, $submitted = null)
echo "$html\n";
}
function FormValidate($form, &$errors, $fields, $submitted)
function FormValidateElement($name, &$errors, $attributes, &$submitted)
{
while (list ($name, $attributes) = each ($fields)) {
# Check for required fields not filled out
if (isset($attributes['#required']) && $attributes['#required'] &&
!(isset($submitted[$name]) && $submitted[$name] != "")) {
$errors[$attributes['#label']] = "Missing required value";
# Check for required fields not filled out
if (isset($attributes['#required']) && $attributes['#required'] &&
!(isset($submitted[$name]) && $submitted[$name] != "")) {
$errors[$attributes['#label']] = "Missing required value";
}
elseif (isset($attributes['#checkslot'])) {
$check = $attributes['#checkslot'];
if (function_exists($check)) {
$check($name, $errors, $attributes, $submitted[$name]);
}
else if (isset($attributes['#checkslot']) &&
isset($submitted[$name]) && $submitted[$name] != "") {
# Check slot
if (preg_match("/^([-\w]+):([-\w]+)$/",
$attributes['#checkslot'], $matches)) {
if (!TBcheck_dbslot($submitted[$name],
$matches[1], $matches[2],
TBDB_CHECKDBSLOT_WARN|
TBDB_CHECKDBSLOT_ERROR)) {
$errors[$attributes['#label']] = TBFieldErrorString();
}
elseif (preg_match("/^([-\w]+):([-\w]+)$/", $check, $matches)) {
#
# What if not required and not set?
#
if (!isset($submitted[$name]) || $submitted[$name] == "") {
$submitted[$name] = "";
}
else {
TBERROR("Could not parse " . $attributes['#checkslot'], 1);
if (!TBcheck_dbslot($submitted[$name],
$matches[1], $matches[2],
TBDB_CHECKDBSLOT_WARN|
TBDB_CHECKDBSLOT_ERROR)) {
$errors[$attributes['#label']] = TBFieldErrorString();
}
}
elseif (substr($check, 0, 1) == "/") {
# Regular expression.
if (!preg_match($check, $submitted[$name])) {
$errors[$attributes['#label']] = "Illegal characters";
}
}
else {
TBERROR("Could not parse checkslot: $check", 1);
}
}
}
function FormValidate($form, &$errors, $fields, &$submitted)
{
while (list ($name, $attributes) = each ($fields)) {
switch ($attributes['#type']) {
case "textfield":
case "password":
case "hidden":
case "submit":
case "checkbox":
case "radio":
case "file":
case "select":
FormValidateElement($name, $errors, $attributes, $submitted);
break;
case "checkboxes":
while (list ($subname, $subattrs) = each ($attributes['#boxes'])) {
FormValidateElement($subname, $errors, $subattrs, $submitted);
}
break;
case "table":
FormValidate($form, $errors, $attributes['#fields'], $submitted);
break;
case "list":
while (list ($subname, $subattrs) =
each ($attributes['#elements'])) {
FormValidateElement($subname, $errors, $subattrs, $submitted);
}
break;
default:
$errors[$name] = "Invalid slot type: " . $attributes['#type'];
break;
}
}
}
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2006, 2007 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
include_once("template_defs.php");
include_once("form_defs.php");
#
# Only known and logged in users can look at experiments.
#
$this_user = CheckLoginOrDie();
$uid = $this_user->uid();
$isadmin = ISADMIN();
#
# Verify page arguments.
#
$reqargs = RequiredPageArguments("template", PAGEARG_TEMPLATE);
$optargs = OptionalPageArguments("search", PAGEARG_STRING,
"addclause", PAGEARG_STRING,
"formfields", PAGEARG_ARRAY);
#
# Standard Testbed Header after argument checking.
#
PAGEHEADER("Template Search");
#
# Check permission.
#
if (! $template->AccessCheck($this_user, $TB_EXPT_READINFO)) {
USERERROR("You do not have permission to view experiment template ".
"$guid/$version!", 1);
}
#
# We display the info for all versions of the template.
#
$root = $template->LookupRoot($template->guid());
$guid = $root->guid();
echo $root->PageHeader();
echo "<br><br>\n";
# A list of match conditions we know about.
$clauseselection = array('equal' => "==",
'like' => "like",
'less' => "<",
'lesseq' => "<= ",
'greater' => ">",
'greatereq' => ">=");
#
# Define the form.
#
$form = array('#id' => 'myform',
'#action' => CreateURL("template_search", $root),
'#caption' => "Search your template (records) ".
"by parameter values');
$fields = array();
#
# Search either templates, instances, or runs.
#
$fields['searchwhich'] =
array('#type' => 'radio',
'#label' => 'What do you want to search',
'#required' => TRUE,
'#radios' => array('Template' =>
array('#label' => 'Templates',
'#return_value' => "template"),
'Instance' =>
array('#label' => 'Instances',
'#return_value' => "instance"),
'Run' =>
array('#label' => 'Runs',
'#return_value' => "run")),
'#checkslot' => "/^(template|instance|run)$/");
$fields['matchif'] =
array('#type' => 'radio',
'#label' => 'Match on',
'#description'=> 'all clauses match or any clause matches',
'#required' => TRUE,
'#radios' => array('Any' =>
array('#label' => 'Any',
'#return_value' => "any"),
'All' =>
array('#label' => 'All',
'#return_value' => "all")),
'#checkslot' => "/^(any|all)$/");
#
# Grab a list of all parameters across all the templates and create
# a list of input boxes.
#
$query_result =
DBQueryFatal("select distinct name from experiment_template_parameters ".
"where parent_guid='$guid' ".
"order by name");
$index = 1;
$paramselection = array();
while ($row = mysql_fetch_array($query_result)) {
$paramselection[$index++] = $row['name'];
}
#
# Start with a single clause.
#
$fields['clause1'] =
array('#type' => 'list',
'#label' => 'Clause 1',
'#elements' => array('parameter1' =>
array('#type' => 'select',
'#default' => 'Parameter',
'#which' => 1,
'#checkslot' => 'CheckParameter',
'#options' => $paramselection),
'condition1' =>
array('#type' => 'select',
'#default' => 'Cond',
'#which' => 1,
'#checkslot' => 'CheckCondition',
'#options' => $clauseselection),
'value1' =>
array('#type' => 'textfield',
'#which' => 1,
'#checkslot' => 'CheckValue',
'#size' => 30)));
$fields['clausecount'] = array('#type' => 'hidden',
'#required' => TRUE,
'#value' => "1");
#
# Spit the form out using the array of data.
#
function SPITFORM($formfields, $errors)
{
global $form, $fields;
$fields['submits'] =
array('#type' => 'list',
'#colspan' => TRUE,
'#elements' => array('addclause' =>
array('#type' => 'submit',
'#value' => "Add Clause"),
'search' =>
array('#type' => 'submit',
'#value' => "Search")));
echo "<center>";
FormRender($form, $errors, $fields, $formfields);
echo "</center>";
}
#
# On first load, display a virgin form and exit.
#
if (!isset($formfields)) {
$defaults = array();
$defaults["searchwhich"] = "template";
$defaults["matchif"] = "any";
SPITFORM($defaults, null);
PAGEFOOTER();
return;
}
# Form submitted.
if (!isset($formfields['clausecount']) ||
$formfields['clausecount'] <= 0 || $formfields['clausecount'] > 20) {
PAGEARGERROR("Invalid form arguments.");
}
#
# Need to generate new clauses for any added previously, plus a new one
# if the Add Clause button was pressed. But, have to watch for deleted
# clauses since the formfield names encode the clause number. In other
# words, we end up with holes in the array.
#
$clausecount = $formfields['clausecount'];
if (isset($addclause)) {
$clausecount++;
$formfields['clausecount'] = $clausecount;
}
for ($i = 2; $i <= $clausecount; $i++) {
#
# If the user used the Trash button, then delete (skip) that clause.
# It does not matter that there are values in $formfields; they will
# be ignored.
#
if (isset($formfields["delete${i}"]))
continue;
# Watch for holes or end of list.
if (!isset($formfields["parameter${i}"])) {
# Added clause goes at the end of course (not in a hole).
if (isset($addclause) && $i == $clausecount) {
unset($addclause);
}
else
continue;
}
$fields["clause${i}"] =
array('#type' => 'list',
'#label' => "Clause $i",
'#elements' => array("parameter${i}" =>
array('#type' => 'select',
'#default' => 'Parameter',
'#which' => $i,
'#checkslot' => 'CheckParameter',
'#options' => $paramselection),
"condition${i}" =>
array('#type' => 'select',
'#default' => 'Cond',
'#which' => $i,
'#checkslot' => 'CheckCondition',
'#options' => $clauseselection),
"value${i}" =>
array('#type' => 'textfield',
'#which' => $i,
'#checkslot' => 'CheckValue',
'#size' => 30),
"delete${i}" =>
array('#type' => 'image',
'#value' => 'Delete',
'#image' => "trash.jpg")));
}
if (!isset($search)) {
SPITFORM($formfields, null);
PAGEFOOTER();
return;
}
#
# Otherwise, must validate and redisplay if errors
#
$errors = array();
#
# This is the callback referenced above in the checkslot fields.
#
function CheckParameter($name, &$errors, $attributes, $value)
{
global $paramselection;
$which = $attributes['#which'];
if (!(isset($value) && $value != "")) {
$errors["Parameter $which"] = "Missing required value";
return;
}
#
# The param value is the index of a param. It must be a real param
# index.
#
if ($value < 0 || $value > count($paramselection)) {
$errors["Parameter $which"] = "Out of range";
}
}
function CheckCondition($name, &$errors, $attributes, $value)
{
global $clauseselection;
$which = $attributes['#which'];
if (!(isset($value) && $value != "")) {
$errors["Condition $which"] = "Missing required value";
return;
}
if (!array_key_exists($value, $clauseselection)) {
$errors["Condition $which"] = "Invalid condition";
}
}
function CheckValue($name, &$errors, $attributes, $value)
{
$which = $attributes['#which'];
# Allow a null value. User wants to compare against ""
if (!isset($value) || $value == "") {
return;
}
#
# The value has to be plain text, but thats about it. We will escape
# it before passing off to mysql in a query.
#
if (!TBvalid_userdata($value)) {
$errors["Value $which"] = TBFieldErrorString();
}
}
FormValidate($form, $errors, $fields, $formfields);
#
# If any errors, respit the form with the current values and the
# error messages displayed. Iterate until happy.
#
if (count($errors)) {
SPITFORM($formfields, $errors);
PAGEFOOTER();
return;
}
#
# Build up the match clauses first.
#
$clauses = array();
for ($i = 1; $i <= $clausecount; $i++) {
$name = $paramselection[$formfields["parameter${i}"]];
$cond = $formfields["condition${i}"];
$value = addslashes($formfields["value${i}"]);
switch ($cond) {
case 'equal':
$clauses[] = "(name='$name' and value='$value')";
break;
case 'like':
$clauses[] = "(name='$name' and value like '$value')";
break;
case 'less':
$clauses[] = "(name='$name' and value regexp '^[0-9\.]+$' and ".
"CAST(value as signed)<'$value')";
break;
case 'lesseq':
$clauses[] = "(name='$name' and value regexp '^[0-9\.]+$' and ".
"CAST(value as signed)<='$value')";
break;
case 'greater':
$clauses[] = "(name='$name' and value regexp '^[0-9\.]+$' and ".
"CAST(value as signed)>'$value')";
break;
case 'greatereq':
$clauses[] = "(name='$name' and value regexp '^[0-9\.]+$' and ".
"CAST(value as signed)>='$value')";
break;
}
}
# We want all rows that match.
$clausestring = join(" or ", $clauses);
# Any/All will match the number of rows returned by above clause.
if ($formfields["matchif"] == "any") {
$matchif = ">=1";
}
else {
$matchif = "=" . count($clauses);
}
if ($formfields["searchwhich"] == "template") {
$query_string =
"select t.* from experiment_template_parameters as p ".
"left join experiment_templates as t on t.guid=p.parent_guid and ".
" t.vers=p.parent_vers ".
"where p.parent_guid='$guid' and ($clausestring) ".
"group by t.vers having count(t.vers) $matchif";
}
elseif ($formfields["searchwhich"] == "instance") {
$query_string =
"select i.* from experiment_template_instance_bindings as b ".
"left join experiment_template_instances as i on ".
" i.parent_guid=b.parent_guid and ".
" i.parent_vers=b.parent_vers ".
"where b.parent_guid='$guid' and ($clausestring) ".
"group by i.parent_vers having count(i.parent_vers) $matchif";
}
else {
# This is complicated by the fact tha neither experiment_runs nor
# experiment_run_bindings has a backlink to the template.
$query_string =
"select r.*,b.runidx,i.parent_vers ".
" from experiment_template_instances as i ".
"left join experiment_run_bindings as b on b.exptidx=i.exptidx ".
"left join experiment_runs as r on ".
" r.exptidx=b.exptidx and r.idx=b.runidx ".
"where i.parent_guid='$guid' and ($clausestring) ".
"group by b.runidx having count(b.runidx) $matchif";
}
#TBERROR($query_string, 0);
$query_result = DBQueryWarn($query_string);
if (!$query_result || !mysql_num_rows($query_result)) {
$errors["Match Failure"] = "There were no matches";
SPITFORM($formfields, $errors);
return;
}
# Spit the form again so the user can change the search criteria.
SPITFORM($formfields, $errors);
echo "<br>\n";
if ($formfields["searchwhich"] == "template") {
AddSortedTable("mytable");
echo "<table align=center id='mytable'
border=1 cellpadding=5 cellspacing=2>\n";
echo "<thead class='sort'>\n";
echo "<tr>
<th>Vers</th>
<th>Parent</th>
<th>TID</th>
<th>Created</th>
<th>Description</th>
</tr>\n";
echo "</thead>\n";
while ($row = mysql_fetch_array($query_result)) {
$vers = $row['vers'];
$tid = $row['tid'];
$pvers = $row['parent_vers'];
$tid = $row['tid'];
$desc = $row['description'];