<?php

// HTML-forms based XML editor
// Works only with valid and wellformed XML
// - MAJOR Caveats:
//   - only allows to modify content (NOT to add new elements/attributes)
//   - It's a basically stupid approach
//   - may break and not do what it is intended for

// Copyright Daniel.Schneider@tecfa.unige.ch
// You can freely use, copy and modify this if you give and send me major
// improvements you add and if you let others do the same with your code.

// Parameters for files, directories and URLs we use
// If you copy this you NEED TO edit all these

$project_dir "/web/staf/staf-e/staf18/proj/";
$xml_path "http://tecfa2.unige.ch/staf/staf-e/staf18/proj/";
$xml_source_path "http://tecfa2.unige.ch/staf/staf-e/staf18/proj/";
$xml_servlet "http://tecfa2.unige.ch:8080/servlet/XSLatorServlet";
$style_sheet "http://tecfa2.unige.ch/staf/staf-e/staf18/project.xsl";


// other globals (commented, since no need to init)
# $stack = array();
# $astack = array();
# $pointer = 0;
# $apointer = 0;


// ---------------- Utils
// 'Pops' off and returns the last element of $array. By 
// doing so, the size of $array is reduced by 1.
// Adapted from Harding Shay (mekla@geocities.com)

function pop(&$array) {
  if (!
is_array($array)){ return; }
  
$variable $array[count($array)-1];
  unset(
$array[count($array)-1]);
  return 
$variable;
}


function 
push(&$array,$variable) {
  
$array[count($array)] = $variable;
  return 
count($array);
}


// ---------------------------------------------------------------------------
// ------------------------------- XML Parsing ------------------------------


function widget_no () {
  static 
$Nwidget;
  
$Nwidget++;
  return 
$Nwidget;
}


// ------------- printing functions

function padding ($depth$string) {
  
$pads "";
  for (
$i 0$i $depth$i++) {
    
$pads $pads $string;
  }
  return 
$pads;
}


function 
attr_printer ($attrs) {
  global 
$pointer;
  if (
$attrs) {
    $
$tt 1;
    echo 
"<table border=\"2\">";
    
// echo "\n" . padding ($pointer," * ");
    // echo "ATTRIBUTES: ";
    // echo "<table align = \"left\">";
    
while (list ($attr$content) = each ($attrs)) {
      
$name_attr " name = \"" "attr_" $attr "_" widget_no() . "\"";
      
$value_attr " value = \"" $content "\"";
      
      
$modulus $tt 3;
      if (
$modulus == 0) {
    if (
$tt 1) { echo "</tr>\n"; } ;
    echo 
"\n<tr>";
      }
      echo 
"<td>";
      
// echo "D: tt=$tt, modulus=$modulus - ";
      
echo "$attr ";
      echo 
"</td>";
      echo 
"<td>";
      echo 
"<input type=\"text\" size=\"10\""$name_attr $value_attr ">";
      echo 
"</td>";
      
$tt++;
    }
    
$empty_cells $modulus;
    for (
$i=1$i $empty_cells$i++) {
      echo 
"<td colspan=\"2\">";
      echo 
"*";
      echo 
"</td>";
    }
    echo 
"</tr>\n";
    echo 
"</table>\n\n";
    
    
// echo "<br clear = \"all\">";
  
}
}


function 
array_printer ($el) {
  echo 
"&lt;$el&gt; ";
}

function 
stack_printer () {
  global 
$stack;
  global 
$pointer;
  for (
$i=0$i <= $pointer$i++) {
    echo 
"&lt;" $stack[$i] . "&gt; ";
  }
}


// ----------------------- The 3 functions for the PHP XML Parser
// xml_parse fait appel à ces fonctions

// grab current element and push it onto the stack
// push its attributs on the astack

function startElement($parser$name$attrs) {
  global 
$stack$astack;
  global 
$pointer$apointer$current;
  global 
$xml_tree$xml_stack$xml_data$xml_attrs$html_tree;
  
  
$pointer push ($stack$name) -;
  
$apointer push ($astack$attrs) -;
  
// the current element
  
$current $name;
  
  echo 
"<hr>";
  
// --- make a hidden tag for later processing
  // have to distinguish tags with attributes and tags without
  
if ($attrs) {
    
$name_attr " name = \"" "elattsstart_" $current "_" widget_no() . "\"";
  }
  else { 
$name_attr " name = \"" "elstart_" $current "_" widget_no() . "\"";
  }
  
  
$value_attr " value = \"" $current "\"";
  echo 
"<input type=\"hidden\"" $name_attr $value_attr ">";
  
  
// --- print on page
  
if ($html_tree) { 
    echo 
"\n";
    echo 
"<i>$current</i><br>\n"; };
  if (
$xml_tree) { 
    echo 
"\n";
    
stack_printer();
  }
  if (
$xml_attrs) { attr_printer($attrs); } ;
}

// pop off the element from the stack
// pop off the attributes from the astack

function endElement($parser$name) {
  global 
$stack$astack;
  global 
$pointer$apointer$current;
  global 
$xml_tree$xml_stack$xml_data$xml_attrs$html_tree;
  
  
// make a hidden tag for later processing
  
$name_attr " name = \"" "elend_" $stack[$pointer] . "_" widget_no() . "\"";
  
$value_attr " value = \"" $current "\"";
  echo 
"<input type=\"hidden\"" $name_attr $value_attr ">";
  
  if (
$html_tree) {
    echo 
"\n<br>";
  };
  
pop ($stack);
  
pop ($astack);
  
$pointer $pointer 1;
  
$apointer $apointer 1;
  if (
$xml_tree) { 
    echo 
"\n<br>";
    echo 
"<font size=\"-2\">";
    
stack_printer();
    echo 
"</font>";
  }
  
$current FALSE;
}

function 
characterData($parser$data) {
  global 
$stack$astack;
  global 
$pointer$apointer$current;
  global 
$xml_tree$xml_stack$xml_data$xml_attrs$html_tree;


  if (
$xml_data) {
    
// filter the junk (no real data but whitespaces)
    
if (trim($data) != "") {
      echo 
"\n" padding ($pointer,"   ");
      
$name_attr " name = \"" "data_" $current "_" widget_no() . "\"";
      
$rows 5;
      
$cols 60;
      echo 
"<textarea" $name_attr "rows=\"$rows\" cols=\"$cols\">";
      echo 
"$data";
      echo 
"</textarea>";
    }
  }
}


  
// -------------------- XML Parser 


function parse_xml($file) {

  
// create the XML Parser, a new one for each filep
  
$xml_parser xml_parser_create("ISO-8859-1");
  
xml_parser_set_option($xml_parserXML_OPTION_CASE_FOLDINGfalse);
  
xml_set_element_handler($xml_parser,  "startElement",  "endElement");
  
xml_set_character_data_handler($xml_parser,  "characterData");
  
  
// echo "<p>DEBUG: file = $file";
  
if (!($fp fopen($file,  "r"))) {
    die( 
"could not open XML input");
  }
  else {
    
// init per-file global vars
    
$wp_count 0;
  }
  
  while (
$data fread($fp50000)) {
    if (!
xml_parse($xml_parser$datafeof($fp))) {
      die(
sprintf"XML error: %s at line %d",
           
xml_error_string(xml_get_error_code($xml_parser)),
           
xml_get_current_line_number($xml_parser)));
    }
  }
  
// close file and free parser
  
fclose($fp);
  
xml_parser_free($xml_parser);
 
}

// ------------------------------ File ops



function make_file_option($file) {
  echo 
"<option value = \"" $file "\"> Fichier " $file;
}

function 
make_file_menu ($files) {
  
// echo "DEBUG: files = $files <p>\n";
  
echo "<select name=\"projectfile\">";
  
array_walk ($files'make_file_option');
  echo 
"</select>";
}




// ----------------------------------------------------------------------
// ----------------------- Main routine

// -------------------------- Get the list of files

$handle=opendir($project_dir);
// read the files into an array
$files = array();
while (
$file readdir($handle)) {
  
$pieces explode("."$file);
  if (
$pieces[1] == "xml") {
    
$files[] = $file;
  }
}
closedir($handle);

// add a template file to the list ??
push ($files,"templates/project-templ-dtd.xml");


// sort them in alphabetical order
sort($files);
reset($files);

// ----------------------------------------------------------------------
// HMTL-> XML Translator.
// Watch out: returns text
// We have to do some stupid dectective work and figure which elements
// - are data-less <el attr=xxx/> 
// - which delements are just empty but can have data <el> </el>
// - which delements are just empty, have attrs but can have data <el a="b"> </el>
//   This last case breaks :(

// Disgusting, quick hacks don't work, will rewrite this and parse into trees

$stack = array();

if (
$process) {
  
// phpinfo();
  
Header"Content-type: text/plain");
  echo 
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
  echo 
"<!DOCTYPE project SYSTEM \"/web/staf/staf-e/staf18/project.dtd\">\n";

  while (list (
$attr$content) = each ($HTTP_POST_VARS)) {

    list (
$type$name$junk) = explode ("_"$attr);

    if (
$type == "elstart") {
      
$pointer push ($stack"elstart") -;
      
$pointer push ($stack$name) -;
      if (
$with_attrs) { 
    echo 
">\n";
    
$with_attrs FALSE;
      };
      echo 
"<" $name ">\n";

    }

    elseif (
$type == "elattsstart") {
      
$pointer push ($stack"elattstart") -;
      
$pointer push ($stack$name) -;
      if (
$with_attrs) { 
    echo 
">\n";
      };
      echo 
"<" $name "\n";
      
// We have a tag attributes, means that we should wait before closing the tag.
      
$with_attrs TRUE;
    }

    elseif (
$type == "elend") {
  
      if (
$with_attrs) { 
    if (
$name == $stack[$pointer]) {
      echo 
"/>\n";
      
$with_attrs FALSE;
    } ;
      }
      else {
    echo 
"</$name>\n";
      
$found_data FALSE;
      }
      
pop ($stack);     
      
$pointer $pointer 1;
      
pop ($stack);
      
$pointer $pointer 1;
    }

    
// attributes - one by one
    
elseif ($type == "attr") {
      
$found_attrs TRUE;
      echo 
"$name=\"" $content "\"\n";
    }

    elseif (
$type == "data") {
      if (
$with_attrs) { 
    echo 
">\n";
    
$with_attrs FALSE;
      };
      echo 
"$content\n";
      
$found_data TRUE;
    };

  }

   
// EXIT !!
   
exit();
}


// ------------------------------------------------------------------------------
// If we don't translate we always have an HTML file and we show the file
// selection FORM
// ------------------------------ Make a HTML form

?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
  <HEAD>
    <TITLE>Modification d'un arbre XML d'un fichier project</TITLE>
    <!-- Created by: D.K.S., 02-Jun-1999 -->
<!-- Changed by: D.K.S., 14-Jun-1999 -->

<!-- TREE Display of project files
     This code would work for any XML
     Author: Daniel.Schneider@tecfa.unige.ch
             You can freely use and distribute and modify this program
         but please let me know if you come up with something much better 
 -->

  </HEAD>
  <BODY>
    <H1>Modification d'un arbre XML d'un fichier project <A HREF="http://tecfa.unige.ch/tecfa/teaching/staf18/staf18-overview.html">STAF-18</A> (EVA) déposée</H1>

Ce formulaire s addresse aux gens qui veulent programmer PHP/XML ou
aux curieux qui veulent regarder la structure de leur fichier XML.
<p>
Ce formulaire permet d'editer (et sauver localement) un arbre XML. Mais ATTENTION:
il ne permet pas de rajouter des éléments ou attributs qui n'existent pas dans l'arbre.
(Donc c'est un éditeur assez inutile sauf pour des structures très simples ou il faut
obligatoirement remplir tous les éléments. Vous pouvez adapter le code à vos besoins.
En principe il devrait marcher avec tous les fichiers XML well-formed).
<p>
A faire (mais c'est plus dur): intégrer un DTD parser pour faire un vrai éditeur.

<FORM METHOD="Post" ACTION= <? echo "\""$PHP_SELF "\""?> >
  <? make_file_menu($files); ?>

  <INPUT TYPE="submit" VALUE="Submit" name="process2"> <br>

  <input type="radio" name="display" value="xml_tree" checked> XML TREE
  <input type="radio" name="display" value="html_tree"> HTML TREE
  <input type="radio" name="display" value="no_tree"> No tree
 <br>

  <input type="checkbox" name="xml_data" checked> Data (element contents)
  <input type="checkbox" name="xml_attrs" checked> Attributes

</FORM>
<hr>
 
<?

echo  "[ <a href = \""$PHP_SELF ."\">Restart Over</a> ]";
echo  
"[ <a href = \""$PHP_SELF ."s\">Source Code</a> ] <hr>";

// ------------------------------  Here we deal with form requests

if ($projectfile
  
// to call like ?projectfile=file.xml
{
  
//  ----- PARSE XML
  
  // --- fix options
  
  
if     ($display == "xml_tree") { $xml_tree "on";}
  elseif (
$display == "html_tree") { $html_tree "on";}

  
// XML stack needs xml_tree
  //if ($xml_stack) $xml_tree = "on";


  // An URL to contents

  
$xml_url $xml_path $projectfile;
  
$url $xml_servlet "?xml=" $xml_url "&xsl=" $style_sheet;
  echo 
"<p>Modify project file: <A HREF=\"$url\">$projectfile</A>: \n";
  echo 
"Save results with the 'SAVE AS' button in your browser!";

  
// ----- DOIT 
  // calls xml_parse for the file, which in turn calls
  // - startElement when it finds an open tag
  // - endElement when it finds a close tag
  // - characterData when it finds CDATA (things inside a tag including blanks)

  // echo "<pre>";
  
?>

    <FORM METHOD="Post" ACTION= <? echo "\""$PHP_SELF "\""?> >
    <br><INPUT TYPE="submit" VALUE="Translate" name="process">
<?

  parse_xml
($project_dir.$projectfile);
  echo 
"</form>";
  
// echo "</pre>";
}


?>



<hr>
<A HREF="http://tecfa.unige.ch/tecfa/teaching/staf18/pm-eva.html">Gestion de projets</A> | 
<A HREF="http://tecfa.unige.ch/tecfa/teaching/staf18/staf18-overview.html">Retour à STAF-18</A>
<hr>
    <ADDRESS>
      <A NAME="Signature"
     HREF="http://tecfa.unige.ch/tecfa-people/schneider.html">D.K.S.</A>
    </ADDRESS>
  </BODY>
</HTML>