Browse Source

server side web ui fleet management

clementinecomputing 4 năm trước cách đây
mục cha
commit
223acd411f

+ 12 - 0
server/ui/fleet-management/README.md

@@ -0,0 +1,12 @@
+Fleet Management
+---
+
+
+This is a bare bones Web interface to manage the driver logins,
+stops, paddles, billing log annotations and configurations for
+the fleet.
+
+---
+
+Credentials are located in the `connect.php` and `annotation.php` files and should be filled
+in with the appropriate SQL connection credentials.

+ 197 - 0
server/ui/fleet-management/annotation.php

@@ -0,0 +1,197 @@
+<?php
+// Allows a user to upload billing annotations to the billing_log_annotation table.Add
+// Written by Chris Martin
+
+session_start();
+
+// Connect to database
+$mysqli = new mysqli ( "localhost", "XXX_DBUSER_XXX", "XXX_DBPASSWORD_XXX", "XXX_DB_XXX" );
+if ( $mysqli -> connect_errno ) {
+  echo "Failed to connect to MySQL: " . $mysqli -> connect_error;
+}
+
+// Valid HTML
+echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
+  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
+  <html xmlns=\"http://www.w3.org/1999/xhtml\">
+  <head>
+    <title>Annotation Uploader</title>
+  </head>
+  <body>
+  <h1>Annotation Uploader</h1>";
+
+// If no annotation file is provided, prompt for .CSV upload
+if(!isset($_POST['confirm']) && !isset($_FILES['annotation_file'])) {
+  echo "Upload a .CSV file of billing annotations.<br />
+    <ul>
+      <li>The first row in your CSV file must be a header consisting of field/column names that match fields in the billing_log table.</li>
+      <li>The seq_num field is required, all others are optional.</li>
+      <li>Fields can be in any order.</li>
+      <li>Only fields provided will be entered into the database.</li>
+      <li>There is also an optional 'note' field, indicating a reason for the annotation.</li>
+      <li>If using Excel to prepare your .CSV file, ensure that Excel does not auto-format your date or credential fields in the .CSV file. You can enforce this by enclosing your values in double quotes preceded with an equal sign, like =\"this\" (demonstrated in the example .CSV file).</li>
+    </ul>
+    <a href=\"example.csv\">Download a sample .CSV file</a> with all fields populated.
+    <form enctype=\"multipart/form-data\" action=\"annotation.php\" method=\"POST\">
+      <input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"4000000\" />
+      Upload annotations file (.csv): <input name=\"annotation_file\" type=\"file\" accept=\"text/csv,application/csv\" /> <input type=\"submit\" value=\"Upload File\" />
+    </form>
+    <a href=\"index.php?goto=mainmenu\">Return to menu</a>";
+  exit;
+}
+
+// If .CSV file is uploaded, store as two-dimensional array and perform
+// input validation. Present table of data to user for confirmation.
+if(isset($_FILES['annotation_file'])) {
+  // Convert .CSV file to two-dimensional array
+  $tmp_name = $_FILES['annotation_file']['tmp_name'];
+  $csv_array = array_map('str_getcsv', file($tmp_name));
+
+  // If values are encapulated like ="this", remove encapsulation.
+  // This is a workaround for Excel mangling date fields.
+  for($row_count = 0; $row_count < count($csv_array); $row_count++) {
+    for($col_count = 0; $col_count < count($csv_array[$row_count]); $col_count++) {
+      // Match on values encapsulated like ="this"
+      $pattern = "/\A=\"(.*)\"\z/";
+      if(preg_match($pattern,$csv_array[$row_count][$col_count],$match) == 1) {
+        // Remove encapulation if match is found
+        $csv_array[$row_count][$col_count] = $match[1];
+      }
+    }
+  }
+
+  // If seq_num is not provided, exit with error message
+  if(!in_array("seq_num",$csv_array[0])) {
+    echo "seq_num is a required field and is missing from your input .csv file. Please try again.";
+    exit;
+  }
+
+  // Query database for valid fields in billing_log
+  $field_result = $mysqli -> query("SHOW FIELDS FROM billing_log");
+  while ($field = $field_result -> fetch_assoc()) {
+    $valid_fields[] = $field['Field'];
+  }
+  $valid_fields[] = "note";
+
+  // Check if each field entered is a valid field in billing_log.
+  // If an invalid field is found, exit with error message.
+  foreach($csv_array[0] as $input_field) {
+    if(!in_array($input_field,$valid_fields)) {
+      echo "Your field \"$input_field\" is not valid. Please try again. Valid fields in billing_log:<br />";
+      foreach($valid_fields as $valid_field) {
+        echo "$valid_field<br />";
+      }
+      exit;
+    }
+  }
+
+  // Don't allow any column to appear more than once
+  if(count(array_unique($csv_array[0]))<count($csv_array[0])) {
+    echo "You have provided more than one column with the same name. Please try again.";
+    exit;
+  }
+
+  // Regexes of invalid input for each field
+  $valid_chars = array(
+    'seq_num' => "/\A\d*\z/",
+    'equip_num' => "/\A\d*\z/",
+    'driver' => "/\A\d*\z/",
+    'paddle' => "/\A\d*\z/",
+    'route' => "/\A\d*\z/",
+    'trip' => "/\A\d*\z/",
+    'stop' => "/\A\d*\z/",
+    'ride_time' => "/\A\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{2}(:\d{2})?\z/",
+    'latitude' => "/\A-?\d+(\.\d+)?\z/",
+    'longitude' => "/\A-?\d+(\.\d+)?\z/",
+    'action' => "/\A[A-Z]+\z/",
+    'rule' => "/.*/",
+    'ruleparam' => "/.*/",
+    'reason' => "/.*/",
+    'credential' => "/\A[0-9:]+\z/",
+    'logical_card_id' => "/\A\d*\z/",
+    'cash_value' => "/\A\d*\z/",
+    'stop_name' => "/.*/",
+    'note' => "/.*/",
+  );
+
+  // Check all input against invalid characters array.
+  foreach($csv_array[0] as $input_field) {
+    $col_pos = array_search($input_field, $csv_array[0]);
+    for ($row = 1; $row <= count($csv_array)-1; $row++) {
+      $value = $csv_array[$row][$col_pos];
+      if(!empty($value)) {
+        $valid_match = preg_match($valid_chars[$input_field],$value);
+        if($valid_match==0) {
+          $human_row = $row + 1;
+          echo "Row $human_row of your CSV file contains invalid characters in field \"$input_field\", value \"$value\". Please <a href=\"annotation.php\">try again</a>.";
+          exit;
+        }
+      }
+    }
+  }
+  
+  // Store CSV array as session variable
+  $_SESSION['csv_array'] = $csv_array;
+  
+  // Display CSV table to user as HTML table and prompt for confirmation
+  echo "<p>The following changes will be entered into the database.</p>";
+  $table = "<table border=\"1\">";
+  $firstrow = true;
+  foreach($csv_array as $key => $element)
+  {
+    $table .= "<tr>";
+    foreach ($element as $subkey => $subelement) {
+      if ($firstrow == true) {
+        $table .= "<th>$subelement</th>";      
+      } else {
+        $table .= "<td>$subelement</td>";
+      }
+    }
+    $firstrow = false;
+    $table .= "</tr>";
+  }
+  $table .= "</table>";
+  echo $table;
+  echo "Confirm new annotations for insertion to the database.<br />  
+    <form method=\"post\" action=\"annotation.php\">
+    <input type=\"hidden\" name=\"confirm\" value=\"true\">
+    <button type=\"submit\">Confirm</button><br />
+    <a href=\"annotation.php\">Cancel</a><br />
+    </form>";
+}
+
+// If user has confirmed input, insert into database
+if(isset($_POST['confirm'])) {
+  $csv_array = $_SESSION['csv_array'];
+  $columns = implode(', ', array_shift($csv_array));
+  $values = array();
+  // Process each row of array
+  foreach($csv_array as $row) {
+    // Process each value in row
+    foreach($row as $key => $value) {
+      if(!empty($row[$key])) {
+        // Escape each input value for SQL query
+        $row[$key] = "'" . $mysqli->real_escape_string($row[$key]) . "'";
+      } else {
+        $row[$key] = 'NULL';
+      }
+    }
+    $values[] = "(" . implode(', ', $row) . ")";
+  }
+  $insert = "INSERT INTO billing_log_annotation ($columns) VALUES " . implode(', ', $values);
+  echo "Executing database insertion.<br />";
+  // echo "<code>" . $insert . "</code><br />";
+  if(!$mysqli -> query($insert)) {
+    echo "Insert operation failed: (" . $mysqli -> errno . ") ". $mysqli -> error;
+  } else {
+    echo "Done! Annotation table has been updated.<br />";
+  }
+}
+
+echo '<a href="index.php?goto=mainmenu">Return to Menu</a>';
+
+// Valid HTML
+echo "</body>
+  </html>";
+
+?>

+ 142 - 0
server/ui/fleet-management/bulk_update.php

@@ -0,0 +1,142 @@
+<?php
+  include 'connect.php';
+  
+  if(! (isset($_FILES['routesfile']) && isset($_FILES['paddlesfile'])) )
+  {
+    $okay=false;
+  }
+  else
+  {
+    $okay=true;
+  }
+  
+  if(!$okay)
+  {
+    echo 'Route spreadsheet should have the following format:';
+    echo '<table border="1"><tr><th>A</th><th>B</th><th>C</th><th>...</th></tr>
+                 <tr><td>Route#</td><td>Stop 1 Name</td><td>Stop 2 Name</td><td>...</td></tr>
+                 <tr><td colspan="4"><center>...</center></td></tr></table><br>';
+
+    echo 'Paddle spreadsheet should have the following format:';
+    echo '<table border="1"><tr><th>A</th><th>B</th><th>C</th><th>D</th><th>E</th><th>F</th><th>G</th><th>...</th></tr>
+                 <tr><td>Paddle#</td><td>Order#</td><td>Block#</td><td>Route #</td><td>Trip #</td><td>Stop 1 Time</td><td>Stop 2 Time</td><td>...</td></tr>
+                 <tr><td colspan="8"><center>...</center></td></tr></table><br>';
+    echo 'It should be noted that Order# and Block# are ignored, it is assumed that the spreadsheet is sorted.<br>';
+    
+    echo '
+    <form enctype="multipart/form-data" action="bulk_update.php" method="POST">
+    <input type="hidden" name="MAX_FILE_SIZE" value="400000" />
+    Upload routes file (.csv): <input name="routesfile" type="file" accept="text/csv,application/csv" /><br />
+    Upload paddles file (.csv): <input name="paddlesfile" type="file" accept="text/csv,application/csv" /><br />
+    <input type="submit" value="Upload Files" />
+    </form>';
+    exit;
+  }
+  
+  $routes=array();
+
+  $stops=array();
+  $res=mysqli_query($sql, "SELECT name,id FROM stops");
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $stops[$row['name']]=$row['id'];
+  }
+  
+
+  //print_r($_FILES);
+
+  $f=fopen($_FILES['routesfile']['tmp_name'],"rb");
+  while($row=fgetcsv($f,1024))
+  {
+    $num=intval($row[0]);
+    if($num > 0)
+    {
+      $idx=0;
+      $tmp=array();
+
+      while(strlen($row[$idx + 1]) > 0)
+      {
+        $tmp[$idx]=trim($row[$idx + 1]);
+        $idx++;  
+      }
+      
+      $routes[$num]=$tmp;
+    }    
+  }  
+  fclose($f);
+  
+//  echo "<pre>";
+//  print_r($routes);
+//  echo "</pre>";
+
+  $paddles=array();
+
+  $f=fopen($_FILES['paddlesfile']['tmp_name'],"rb");
+  while($row=fgetcsv($f,1024))
+  {
+    $pnum=intval($row[0]);
+    
+    if($pnum > 0)
+    {
+      $rt=intval($row[3]);
+      $tp=intval($row[4]);
+      
+      if(isset($routes[$rt]))
+      {
+        $n=count($routes[$rt]);
+        for($i=0;$i < $n; $i++)
+        {
+          $time=$row[$i + 5];
+          
+          if(isset($stops[$routes[$rt][$i]]))
+          {
+            $stpid=$stops[$routes[$rt][$i]];
+          }
+          else
+          {
+            $stpid=0;
+            $err=$routes[$rt][$i];
+            $x=$i+1;
+            echo "Ignoring Unknown stop \"$err\" (Work Run $pnum Route $rt Trip $tp Stop $x)<br>";
+            continue;
+          }
+          
+          if(strlen(trim($time)) > 0)
+          {
+            $paddles[$pnum][]=array( 'id' => $pnum, 'arrival' => $time, 'route' => $rt, 'trip' => $tp, 'stop' => ($i + 1), 'stage' => ($i + 1), 'stopid' => $stpid);
+          }
+        }
+      }
+    }
+    
+  }  
+  fclose($f);
+  
+  
+  
+  //echo '<pre>';
+  //print_r($paddles);
+  //echo '</pre>';
+
+  foreach($paddles as $pnum => $paddle)
+  {
+    mysqli_query($sql,"DELETE FROM paddles WHERE id='$pnum'");
+    
+    foreach($paddle as $slot => $record)
+    {
+      $arr=$record['arrival'];
+      $rt=$record['route'];
+      $tp=$record['trip'];
+      $stp=$record['stop'];
+      $stg=$record['stage'];
+      $stpid=$record['stopid'];
+      
+      mysqli_query($sql,"INSERT INTO paddles SET id='$pnum', slot='$slot', arrival='$arr', route='$rt', trip='$tp', stage='$stg', stop='$stp', stopid='$stpid'");
+    }
+    
+  }
+
+  echo "Done!<br>";
+  echo '<a href="index.php?goto=mainmenu">Return to menu</a>';
+  
+?>

+ 4 - 0
server/ui/fleet-management/connect.php

@@ -0,0 +1,4 @@
+<?php
+  $sql=mysqli_connect("localhost", "XXX_DBUSER_XXX", "XXX_DBPASSWORD_XXX", "XXX_DB_XXX");
+  if (! $sql) { die("Cannot connect to database"); }
+?>

+ 65 - 0
server/ui/fleet-management/create_tables.sql

@@ -0,0 +1,65 @@
+CREATE TABLE drivers (
+	id INTEGER UNIQUE NOT NULL,
+	pin VARCHAR(8) NOT NULL,
+	name VARCHAR(32)
+);
+
+CREATE TABLE stops (
+	id INTEGER UNIQUE PRIMARY KEY,
+	latitude REAL NOT NULL,
+	longitude REAL NOT NULL,
+	name VARCHAR(32)
+);
+
+CREATE TABLE paddles (
+	id INTEGER NOT NULL,
+	INDEX ididx(id),
+	slot INTEGER NOT NULL,
+	arrival TIME,
+	route INTEGER,
+	trip INTEGER,
+	stage INTEGER,
+	stop INTEGER,
+	stopid INTEGER NOT NULL
+);
+
+CREATE TABLE live_stops (
+	id INTEGER UNIQUE PRIMARY KEY,
+	latitude REAL NOT NULL,
+	longitude REAL NOT NULL,
+	name VARCHAR(32)
+);
+
+CREATE TABLE live_paddles (
+	id INTEGER NOT NULL,
+	INDEX ididx(id),
+	slot INTEGER NOT NULL,
+	arrival TIME,
+	route INTEGER,
+	trip INTEGER,
+	stage INTEGER,
+	stop INTEGER,
+	stopid INTEGER NOT NULL
+);
+
+CREATE TABLE old_stops (
+	verstring VARCHAR(256),
+	id INTEGER NOT NULL,
+	latitude REAL NOT NULL,
+	longitude REAL NOT NULL,
+	name VARCHAR(32)
+);
+
+CREATE TABLE old_paddles (
+	verstring VARCHAR(256),
+	id INTEGER NOT NULL,
+	slot INTEGER NOT NULL,
+	arrival TIME,
+	route INTEGER,
+	trip INTEGER,
+	stage INTEGER,
+	stop INTEGER,
+	stopid INTEGER NOT NULL
+);
+
+	

+ 323 - 0
server/ui/fleet-management/editdrivers.php

@@ -0,0 +1,323 @@
+<?php
+
+function changed_drivers()
+{
+  $accum=array();
+  
+  $temp=load_drivers();
+  
+  foreach($_SESSION['drivers'] as $key => $record)
+  {
+    if(isset($temp[$key]))
+    {
+      if(array_diff($temp[$key],$record))
+      {
+        $accum[$key]=$record;
+        $accum[$key]['comment'] = "Modified";
+      }
+    }
+    else
+    {
+      $accum[$key]=$record;
+      $accum[$key]['comment'] = "Added";
+    }
+  }
+  
+  foreach($temp as $key => $record)
+  {
+    if(!isset($_SESSION['drivers'][$key]))
+    {
+      $accum[$key]=$record;
+      $accum[$key]['comment'] = "Deleted";
+    }
+  }
+  
+  return $accum;
+}
+
+function load_drivers()
+{
+global $sql;
+  $qry="SELECT id,pin,name FROM drivers";
+  $res=mysqli_query($sql,$qry);
+  
+  $accum=array();
+    
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[$row['id']]=$row;
+  }
+  
+  return $accum;
+}
+
+function commit_drivers()
+{
+global $sql;
+  mysqli_query($sql,"BEGIN");
+  mysqli_query($sql,"DELETE FROM drivers");
+  
+  foreach($_SESSION['drivers'] as $record)
+  {
+    $escrec=escape_array($record);
+
+    mysqli_query($sql,"INSERT INTO drivers SET id='${escrec['id']}', pin='${escrec['pin']}', name='${escrec['name']}'");  
+  }
+  
+  mysqli_query($sql,"COMMIT");
+  
+  $_SESSION['drivers_dirty']=false;
+
+  $tmp=fopen("/tmp/new_drivers_exist","wb");
+  if($tmp) fclose($tmp);
+}
+
+function begin_driver_table($sortheaders)
+{
+  if($sortheaders)
+  {
+    echo '<center><table width="80%" border="1"><tr><th><a href="index.php?sortby=id&type=num">Driver Number</a></th><th>Pin Number</th><th><a href="index.php?sortby=name&type=str">Driver Name</a></th><th>Action</th></tr>';
+  }
+  else
+  {
+    echo '<center><table width="80%" border="1"><tr><th>Driver Number</th><th>Pin Number</th><th>Driver Name</th><th>Action</th></tr>';
+  }
+}
+
+function driver_compare($a, $b)
+{
+  if(isset($_SESSION['driver_sortby']))
+  {
+    $sortby=$_SESSION['driver_sortby'];
+  }
+  else
+  {
+    $sortby='name';
+  }
+  
+  $sorttype="str";
+  
+  if(isset($_SESSION['driver_sorttype']))
+  {
+    $sorttype=$_SESSION['driver_sorttype'];
+  }
+  
+  if(!strcmp($sorttype,"str"))
+  {
+    return strcmp($a[$sortby],$b[$sortby]);
+  }
+  else if(!strcmp($sorttype,"num"))
+  {
+    if(floatval($a[$sortby]) == floatval($b[$sortby])) return 0;
+    return ( floatval($a[$sortby]) < floatval($b[$sortby]) ) ? -1 : 1;
+  }
+   
+  return 0; 
+}
+
+function sort_drivers()
+{
+  uasort($_SESSION['drivers'],'driver_compare');  
+}
+
+//--------------------------------------------------------HANDLE LOADING DRIVERS IF THEY AREN'T
+if(!isset($_SESSION['drivers']))
+{
+  $_SESSION['drivers']=load_drivers(); 
+  sort_drivers();
+  $_SESSION['drivers_dirty']=false;
+}
+
+//--------------------------------------------------------HANDLE SORTING DRIVER LIST DISPLAY
+
+if(isset($_REQUEST['sortby']))
+{
+  $_SESSION['driver_sortby']=$_REQUEST['sortby'];
+  
+  if(isset($_REQUEST['type']))
+  {
+    $_SESSION['driver_sorttype']=$_REQUEST['type'];
+  }
+
+  sort_drivers();
+  redirect('index.php');
+}
+
+//---------------------------------------------------------HANDLE ADDING A DRIVER
+if(isset($_REQUEST['adddriver']))
+{
+  $okay=true;
+
+  if(!preg_match('/[\d][\d][\d][\d]*/',$_REQUEST['pin']))
+  {
+    $okay=false;
+    echo "PIN must be entirely numeric and at least three digits.<br>";
+  }        
+
+  if(!preg_match('/[\d]+/',$_REQUEST['num']))
+  {
+    $okay=false;
+    echo "Driver Number must be entirely numeric.<br>";
+  }        
+
+  foreach($_SESSION['drivers'] as $record)
+  {
+    if($record['id'] == $_REQUEST['num'])
+    {
+      $okay=false;
+      echo "Driver number must be unique (not already exist in the database).<br>";
+      break;
+    }      
+  }
+    
+  if($okay)
+  {
+    $_SESSION['drivers'][$_REQUEST['num']]=array('id' => $_REQUEST['num'], 'pin' => $_REQUEST['pin'], 'name' => $_REQUEST['name'] );
+    $_SESSION['drivers_dirty']=true;
+    sort_drivers();
+    redirect("index.php");
+  }
+  else
+  {
+    begin_driver_table(true);
+    echo "<tr><form method=\"POST\"><td><input type=\"text\" name=\"num\" value=\"${_REQUEST['num']}\"></td><td><input type=\"text\" name=\"pin\" value=\"${_REQUEST['pin']}\"></td><td><input type=\"text\" size=\"32\" name=\"name\" value=\"${_REQUEST['name']}\"></td><td><input type=\"submit\" name=\"adddriver\" value=\"Add a Driver\"></td></form></tr>";
+    echo '<tr><td colspan="4"><center><a href="index.php">Cancel Data Entry</a></center></td></tr>';
+    echo '</table></center>';
+    exit;
+  }
+}
+
+//-----------------------------------------------------HANDLE DELETING A DRIVER----------------------------
+if(isset($_REQUEST['deldriver']))
+{
+
+  begin_driver_table(true);
+  echo '<tr><td colspan="4"><center><big>Really delete this driver?</big></center></td></tr>';
+
+  $bkgr="#E0FFE0";
+  $record=$_SESSION['drivers'][$_REQUEST['id']];
+  $num=$record['id'];
+  echo "<tr bgcolor=\"$bkgr\"><td>$num</td><td>****</td><td>${record['name']}</td><td> </td></tr>";    
+
+  echo "<tr><td colspan=\"4\"><center><big><a href=\"index.php\">(Cancel)</a> <a href=\"index.php?deldriverconf&id=${_REQUEST['id']}\">(OK)</a></big></center></td></tr>";
+  
+  echo '</table></center>';
+  exit;  
+}
+else if(isset($_REQUEST['deldriverconf']))
+{
+  unset($_SESSION['drivers'][$_REQUEST['id']]);
+  $_SESSION['drivers_dirty']=true;
+  redirect('index.php');
+}
+//-----------------------------------------------------HANDLE THE REVERT OPERATION--------------------------
+if(isset($_REQUEST['revert_drivers']))
+{
+  $changes=changed_drivers();
+
+  begin_driver_table(false);
+  
+  $count=0;
+  foreach($changes as $record)
+  {
+    $num=$record['id'];
+
+    if( ($count % 2) == 1)
+    {
+      $bkgr="#E0FFE0";
+    }
+    else
+    {
+      $bkgr="#FFE0E0";
+    }
+      
+    echo "<tr bgcolor=\"$bkgr\"><td>$num</td><td>****</td><td>${record['name']}</td><td>${record['comment']}</td></tr>";    
+    $count++;
+  }
+
+  echo "<tr><td colspan=\"4\"><center><big>Really lose these $count changes?</big></center></td></tr>";
+  echo "<tr><td colspan=\"4\"><center><big><a href=\"index.php\">(Cancel)</a> <a href=\"index.php?revert_driversconf\">(OK)</a></big></center></td></tr>";
+  
+
+  echo '</table></center>';
+  exit;
+}
+else if(isset($_REQUEST['revert_driversconf']))
+{
+  unset($_SESSION['drivers']);
+  redirect('index.php');
+}
+
+//-----------------------------------------------------HANDLE THE COMMIT DRIVER OPERATION-------------------
+
+if(isset($_REQUEST['commit_drivers']))
+{
+  $changes=changed_drivers();
+
+  begin_driver_table(false);
+  
+  $count=0;
+  foreach($changes as $record)
+  {
+    $num=$record['id'];
+
+    if( ($count % 2) == 1)
+    {
+      $bkgr="#E0FFE0";
+    }
+    else
+    {
+      $bkgr="#FFE0E0";
+    }
+      
+    echo "<tr bgcolor=\"$bkgr\"><td>$num</td><td>****</td><td>${record['name']}</td><td>${record['comment']}</td></tr>";    
+    $count++;
+  }
+
+  echo "<tr><td colspan=\"4\"><center><big>Really apply these $count changes?</big></center></td></tr>";
+  echo "<tr><td colspan=\"4\"><center><big><a href=\"index.php\">(Cancel)</a> <a href=\"index.php?commit_driversconf\">(OK)</a></big></center></td></tr>";
+  
+
+  echo '</table></center>';
+  exit;
+}
+else if(isset($_REQUEST['commit_driversconf']))
+{
+  commit_drivers();
+  unset($_SESSION['drivers']);
+  redirect('index.php');
+}
+
+//-----------------------------------------------------BELOW GENERATES THE MAIN SCREEN FOR DRIVER MANAGEMENT
+
+begin_driver_table(true);
+  
+$count=0;
+foreach($_SESSION['drivers'] as $record)
+{
+    $num=$record['id'];
+    $delurl="index.php?deldriver&id=$num";
+
+    if( ($count % 2) == 1)
+    {
+      $bkgr="#E0FFE0";
+    }
+    else
+    {
+      $bkgr="#FFE0E0";
+    }
+      
+    echo "<tr bgcolor=\"$bkgr\"><td>$num</td><td>****</td><td>${record['name']}</td><td><a href=\"$delurl\">(Delete)</a></td></tr>";    
+    $count++;
+}
+  
+echo '<tr><form><td><input type="text" name="num"></td><td><input type="text" name="pin"></td><td><input type="text" size="32" name="name"></td><td><input type="submit" name="adddriver" value="Add a Driver"></td></tr>';
+  
+if($_SESSION['drivers_dirty'])
+{
+  echo '<tr bgcolor="#FFA0A0"><td colspan="4"><center><big><a href="index.php?commit_drivers">(Commit Changes to Drivers)</a> <a href="index.php?revert_drivers">(Revert to Saved Drivers)</a></big></center></td></tr>';
+}
+  
+echo '</table></center>';
+
+?>

+ 801 - 0
server/ui/fleet-management/editpaddles.php

@@ -0,0 +1,801 @@
+<?php
+
+$errstring="";
+
+//$_SESSION['paddle_dirty']=false;
+
+function paddle_list()
+{
+global $sql;
+  $qry="SELECT id FROM paddles GROUP BY id";
+  $res=mysqli_query($sql, $qry);
+  
+  $accum=array();
+  
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[]=$row['id'];
+  }
+  
+  return $accum;
+}
+
+function load_stops_name()
+{
+global $sql;
+  $qry="SELECT id,latitude,longitude,name FROM stops ORDER BY name";
+  $res=mysqli_query($sql,$qry);
+    
+  $accum=array();
+      
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[$row['id']]=$row;
+  }
+                
+  return $accum;
+}
+
+function load_stops_id()
+{
+global $sql;
+  $qry="SELECT id,latitude,longitude,name FROM stops ORDER BY id";
+  $res=mysqli_query($sql, $qry);
+    
+  $accum=array();
+      
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[$row['id']]=$row;
+  }
+                
+  return $accum;
+}
+
+function generate_stop_name_select($stops,$formname,$nums=false,$selid=-1)
+{
+  echo "<select name=\"$formname\">";
+  echo '<option value="-1">Select a Stop</option>';
+  foreach($stops as $stop)
+  {
+    if($nums)
+    {
+      $label=$stop['id'] . ": " . $stop['name'];
+    }
+    else
+    {
+      $label=$stop['name'];
+    }
+    
+    if($stop['id'] == $selid)
+    {
+      echo "<option value=\"${stop['id']}\" selected>$label</option>";
+    }
+    else
+    {
+      echo "<option value=\"${stop['id']}\">$label</option>";
+    }
+  } 
+  echo '</select>';  
+}
+
+function load_paddle($paddlenum)
+{
+global $sql;
+  $safenum=mysqli_escape_string($sql,$paddlenum);
+  $qry="SELECT id, DATE_FORMAT(arrival, '%H:%i') AS arrival, route, trip, stage, stop, stopid FROM paddles WHERE id='$paddlenum' ORDER BY slot ASC";
+  $res=mysqli_query($sql,$qry);
+  
+  $accum=array();
+  
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[]=array( 'id' => $row['id'], 'arrival' => $row['arrival'],  'route' => $row['route'], 'trip' => $row['trip'], 'stage' => $row['stage'], 'stop' => $row['stop'], 'stopid' => $row['stopid']);
+  }
+  
+  return $accum;
+}
+
+
+function move_up($index)
+{
+  if($index > 0)
+  {
+    $temp=$_SESSION['paddle'][$index];
+    $_SESSION['paddle'][$index]=$_SESSION['paddle'][$index-1];
+    $_SESSION['paddle'][$index-1]=$temp;
+    return true;
+  }
+  return false;
+}
+
+function move_down($index)
+{
+  if($index < (count($_SESSION['paddle']) - 1))
+  {
+    $temp=$_SESSION['paddle'][$index];
+    $_SESSION['paddle'][$index]=$_SESSION['paddle'][$index+1];
+    $_SESSION['paddle'][$index+1]=$temp;
+    return true;
+  }
+  return false;
+}
+
+function del_stop($index)
+{
+  $accum=array();
+
+  foreach($_SESSION['paddle'] as $key => $record)
+  {
+    if($key != $index)
+    {
+      $accum[] = $record;
+    }
+  } 
+
+  $_SESSION['paddle']=$accum; 
+}
+
+function compare_paddle($paddle1, $legend1, $paddle2, $legend2)
+{
+global $count;
+  global $stops;
+  
+//  $ugly=' bgcolor="#0000FF" ';
+   $ugly='';
+ 
+  echo "<center><table width =\"80%\" border=\"1\"><tr><th colspan=\"7\">$legend1</th><th $ugly>with</th><th colspan=\"7\">$legend2</th></tr>";
+  echo '<tr>';
+  echo '<th>Arrival</th><th>Route</th><th>Trip</th><th>Stage</th><th>Stop</th><th>Stop<br>ID</th><th>Stop Name</th>';
+  echo '<th> </th>';
+  echo '<th>Arrival</th><th>Route</th><th>Trip</th><th>Stage</th><th>Stop</th><th>Stop<br>ID</th><th>Stop Name</th>';
+  echo '</tr>';
+  
+  $idx=0;
+  
+  while( isset($paddle1[$idx]) || isset($paddle2[$idx]) )
+  {
+    if(isset($paddle1[$idx]))
+    {
+      $p1a=$paddle1[$idx]['arrival'];
+      $p1s=$paddle1[$idx]['stopid'];
+      $p1n=$stops[$p1s]['name'];
+
+      $p1rt=$paddle1[$idx]['route'];
+      $p1tr=$paddle1[$idx]['trip'];
+      $p1st=$paddle1[$idx]['stop'];
+      $p1sg=$paddle1[$idx]['stage'];
+    }
+    else
+    {
+      $p1a=$p1s=$p1n=$p1rt=$p1tr=$p1st=$p1sg=" ";
+    }
+    
+    if(isset($paddle2[$idx]))
+    {
+      $p2a=$paddle2[$idx]['arrival'];
+      $p2s=$paddle2[$idx]['stopid'];
+      $p2n=$stops[$p2s]['name'];
+
+      $p2rt=$paddle2[$idx]['route'];
+      $p2tr=$paddle2[$idx]['trip'];
+      $p2st=$paddle2[$idx]['stop'];
+      $p2sg=$paddle2[$idx]['stage'];
+    }
+    else
+    {
+      $p2a=$p2s=$p2n=$p2rt=$p2tr=$p2st=$p2sg=" ";
+    }
+    
+    if( ($count % 2) == 1)
+    {
+      $bkgr="#E0FFE0";
+    }
+    else
+    {
+      $bkgr="#FFE0E0";
+    }
+    
+    echo "<tr bgcolor=\"$bkgr\">";
+    
+    if($p1s < 0)
+    {
+      echo "<td>$p1a</td><td>$p1rt</td><td>$p1tr</td><td>$p1sg</td><td>$p1st</td><td colspan=\"2\"><i>Deleted Stop</i></td>";
+    }
+    else
+    {
+      echo "<td>$p1a</td><td>$p1rt</td><td>$p1tr</td><td>$p1sg</td><td>$p1st</td><td>$p1s</td><td>$p1n</td>";
+    }
+    
+    echo "<td $ugly> </td>";
+    
+    if($p2s < 0)
+    {
+      echo "<td>$p2a</td><td>$p2rt</td><td>$p2tr</td><td>$p2sg</td><td>$p2st</td><td colspan=\"2\"><i>Deleted Stop</i></td>";
+    }
+    else
+    {
+      echo "<td>$p2a</td><td>$p2rt</td><td>$p2tr</td><td>$p2sg</td><td>$p2st</td><td>$p2s</td><td>$p2n</td>";
+    }
+    
+    echo "</tr>";
+  
+    $idx++;
+  } 
+  
+  echo "</table></center>";
+}
+
+function commit_test()
+{
+  $firstidx=0;
+  $lastidx=count($_SESSION['paddle']) - 1;
+  
+  if($lastidx > 0)
+  {
+    if( strlen($_SESSION['paddle'][$firstidx]['arrival']) == 0 )
+    {
+      echo "First stop in paddle must have an arrival time.<br>";
+      return false;
+    }
+
+    if( strlen($_SESSION['paddle'][$lastidx]['arrival']) == 0 ) 
+    {
+      echo "Last stop in paddle must have an arrival time.<br>";
+      return false;
+    }
+  }
+  
+  return true;
+}
+
+function commit_paddle()
+{
+global $sql;
+  if(!commit_test())
+    return false;
+  
+  $safeid=mysqli_escape_string($sql,$_SESSION['current_paddle']);
+  
+  mysqli_query($sql,'BEGIN');
+  mysqli_query($sql,"DELETE FROM paddles WHERE id='$safeid'");
+  
+  $idx=1;
+  
+  foreach($_SESSION['paddle'] as $stop)
+  {
+    if(strlen($stop['arrival']) > 0)
+    {
+      $arrclause="'" . mysqli_escape_string($sql,$stop['arrival']) . "'";
+    }
+    else
+    {
+      $arrclause = "NULL";
+    }
+    
+    $safe_stopid=mysqli_escape_string($sql,$stop['stopid']);
+    
+    $sroute=mysqli_escape_string($sql,$stop['route']);
+    $strip=mysqli_escape_string($sql,$stop['trip']);
+    $sstage=mysqli_escape_string($sql,$stop['stage']);
+    $sstop=mysqli_escape_string($sql,$stop['stop']);
+    
+    $qry="INSERT INTO paddles SET id='$safeid', slot='$idx', route='$sroute', trip='$strip', stage='$sstage', stop='$sstop', arrival=$arrclause, stopid='$safe_stopid'";
+    
+    mysqli_query($sql,$qry);
+    
+    $idx++;
+    
+  }
+  
+  mysqli_query($sql,'COMMIT');
+
+  return true;
+}
+
+
+$stops=load_stops_name();
+$stopsid=load_stops_id();
+
+if(!isset($_SESSION['current_paddle']))
+{
+  $_SESSION['current_paddle']=0;
+}
+
+
+if(!isset($_SESSION['paddle']))
+{
+  if($_SESSION['current_paddle'] > 0)
+  {
+    $_SESSION['paddle']=load_paddle($_SESSION['current_paddle']);
+    $_SESSION['paddle_dirty']=false;
+  }
+}
+
+if(isset($_REQUEST['up']))
+{
+  if(isset($_SESSION['paddle'][$_REQUEST['key']]))
+  {
+    if(move_up($_REQUEST['key']))
+    {
+      $_SESSION['paddle_dirty']=true;
+    }
+  }
+
+  redirect('index.php');
+  exit;
+}
+
+if(isset($_REQUEST['dn']))
+{
+  if(isset($_SESSION['paddle'][$_REQUEST['key']]))
+  {
+    if(move_down($_REQUEST['key']))
+    {
+      $_SESSION['paddle_dirty']=true;
+    }
+  }
+
+  redirect('index.php');
+  exit;
+}
+
+if(isset($_REQUEST['del']))
+{
+  if(isset($_SESSION['paddle'][$_REQUEST['key']]))
+  {
+    del_stop($_REQUEST['key']);
+  }
+
+  $_SESSION['paddle_dirty']=true;
+  redirect('index.php');
+  exit;
+}
+
+if(isset($_REQUEST['commit_paddle']))
+{
+  if(commit_test())
+  {
+    echo '<h1><center>Replace this paddle?</center></h1>';
+    compare_paddle(load_paddle($_SESSION['current_paddle']),"Old",$_SESSION['paddle'],"New");
+    echo "<tr><td colspan=\"7\"><center><big><a href=\"index.php\">(Cancel)</a> <a href=\"index.php?commit_paddleconf\">(OK)</a></big></center></td></tr>";
+    exit;
+  }
+}
+else if(isset($_REQUEST['commit_paddleconf']))
+{
+  if(commit_paddle())
+  {
+    unset($_SESSION['paddle']);  
+    unset($_SESSION['paddle_list']);
+    redirect('index.php');
+  }
+}
+
+if(isset($_REQUEST['revert_paddle']))
+{
+  echo '<h1><center>Replace this paddle?</center></h1>';
+  compare_paddle($_SESSION['paddle'],"New",load_paddle($_SESSION['current_paddle']),"Old");
+  echo "<tr><td colspan=\"7\"><center><big><a href=\"index.php\">(Cancel)</a> <a href=\"index.php?revert_paddleconf\">(OK)</a></big></center></td></tr>";
+  exit;
+}
+else if(isset($_REQUEST['revert_paddleconf']))
+{
+  unset($_SESSION['paddle']);
+  unset($_SESSION['paddle_list']);  
+  redirect('index.php');
+}
+
+
+if(isset($_REQUEST['addstop']))
+{
+  $s1=$_REQUEST['stopid_1'];
+  $s2=$_REQUEST['stopid_2'];
+  $arr=$_REQUEST['arrival'];
+  
+  $rte=$_REQUEST['route'];
+  $trp=$_REQUEST['trip'];
+  $stg=$_REQUEST['stage'];
+  $stp=$_REQUEST['stop'];
+  
+  
+  $okay=true;
+  
+  if( ($s1 < 0) && ($s2 < 0) )
+  {
+    $okay=false;
+    $errstring .= "You must select a stop to add to this paddle.<br>";
+  }
+
+  if( ($s1 >= 0) && ($s2 >= 0) )
+  {
+    if($s1 != $s2)
+    {
+      $okay=false;
+      $errstring .= "Selected stops conflict.  Please select either by number OR by name.<br>";
+    }
+  }
+  
+  if(!preg_match('/[\d]+/',$rte))
+  {
+    $okay=false;
+    $errstring .= "Route must be numeric.<br>";
+  }
+  
+  if(!preg_match('/[\d]+/',$trp))
+  {
+    $okay=false;
+    $errstring .= "Trip must be numeric.<br>";
+  }
+  
+  if(!preg_match('/[\d]+/',$stp))
+  {
+    $okay=false;
+    $errstring .= "Stop must be numeric.<br>";
+  }
+
+  if(!preg_match('/[\d]+/',$stg))
+  {
+    $stg=0;
+  }
+  
+  if( strlen($arr) > 0)
+  {
+    $mats=array();
+    if(!preg_match('/([\d][\d]?):([\d][\d])/',$arr,$mats))
+    {
+      $okay=false;
+      $errstring .= "Arrival time must be blank, or in the form HH:MM using 24 hour time.<br>";
+    }
+    else
+    {	
+      if( ($mats[1] > 23) || ($mats[2] > 59) )
+      {
+        $okay=false;
+        $errstring .= "Arrival time must be blank, or in the form HH:MM using 24 hour time.<br>";
+      }
+    }
+  }
+  
+  if($okay)
+  {
+    if($s1 >= 0)
+    {
+      $stopid=$s1;
+    }
+    else
+    {
+      $stopid=$s2;
+    }
+    
+    $_SESSION['paddle'][]=array( 'id' => $_SESSION['current_paddle'], 'arrival' => $_REQUEST['arrival'], 'route' => $rte, 'stop' => $stp, 'trip' => $trp, 'stage' => $stg, 'stopid' => $stopid);    
+    $_SESSION['paddle_dirty']=true;
+    redirect('index.php');
+  }
+}
+
+if(isset($_REQUEST['edit_done']))
+{
+  $s1=$_REQUEST['stopid_1'];
+  $s2=$_REQUEST['stopid_2'];
+  $arr=$_REQUEST['arrival'];
+  
+  $rte=$_REQUEST['route'];
+  $trp=$_REQUEST['trip'];
+  $stg=$_REQUEST['stage'];
+  $stp=$_REQUEST['stop'];
+  
+  
+  $okay=true;
+  
+  if( ($s1 < 0) && ($s2 < 0) )
+  {
+    $okay=false;
+    $errstring .= "You must select a valid stop.<br>";
+  }
+
+  if( ($s1 >= 0) && ($s2 >= 0) )
+  {
+    if($s1 != $s2)
+    {
+      $okay=false;
+      $errstring .= "Selected stops conflict.  Please select either by number OR by name.<br>";
+    }
+  }
+  
+  if(!preg_match('/[\d]+/',$rte))
+  {
+    $okay=false;
+    $errstring .= "Route must be numeric.<br>";
+  }
+  
+  if(!preg_match('/[\d]+/',$trp))
+  {
+    $okay=false;
+    $errstring .= "Trip must be numeric.<br>";
+  }
+  
+  if(!preg_match('/[\d]+/',$stp))
+  {
+    $okay=false;
+    $errstring .= "Stop must be numeric.<br>";
+  }
+
+  if(!preg_match('/[\d]+/',$stg))
+  {
+    $stg=0;
+  }
+  
+  if( strlen($arr) > 0)
+  {
+    $mats=array();
+    if(!preg_match('/([\d][\d]?):([\d][\d])/',$arr,$mats))
+    {
+      $okay=false;
+      $errstring .= "Arrival time must be blank, or in the form HH:MM using 24 hour time.<br>";
+    }
+    else
+    {	
+      if( ($mats[1] > 23) || ($mats[2] > 59) )
+      {
+        $okay=false;
+        $errstring .= "Arrival time must be blank, or in the form HH:MM using 24 hour time.<br>";
+      }
+    }
+  }
+  
+  if($okay)
+  {
+    if($s1 >= 0)
+    {
+      $stopid=$s1;
+    }
+    else
+    {
+      $stopid=$s2;
+    }
+    
+    $_SESSION['paddle'][$_REQUEST['key']]=array( 'id' => $_SESSION['current_paddle'], 'arrival' => $_REQUEST['arrival'], 'route' => $rte, 'stop' => $stp, 'trip' => $trp, 'stage' => $stg, 'stopid' => $stopid);    
+    $_SESSION['paddle_dirty']=true;
+    redirect('index.php');
+  }
+}
+
+function generate_edit_line($dflt)
+{
+
+  global $stopsid;
+  global $stops;
+  global $errstring;
+  
+  if(strlen($errstring) > 0)
+  {
+    echo "<tr><td colspan=\"8\" style=\"color: red;\"><center><big>$errstring</big></center></td></tr>";
+  }
+
+  $myreq=$_REQUEST;
+  
+  if(!isset($myreq['stopid_1']))
+  {
+if (!array_key_exists('arrival',$dflt)) { $dflt['arrival'] = null; }
+if (!array_key_exists('route',$dflt)) { $dflt['route'] = null; }
+if (!array_key_exists('trip',$dflt)) { $dflt['trip'] = null; }
+if (!array_key_exists('stage',$dflt)) { $dflt['stage'] = null; }
+if (!array_key_exists('stop',$dflt)) { $dflt['stop'] = null; }
+if (!array_key_exists('stopid',$dflt)) { $dflt['stopid'] = null; }
+echo $dflt['arrival'];
+
+    $myreq['arrival']=$dflt['arrival'];
+    $myreq['route']=$dflt['route'];
+    $myreq['trip']=$dflt['trip'];
+    $myreq['stage']=$dflt['stage'];
+    $myreq['stop']=$dflt['stop'];
+    $myreq['stopid_1']=$dflt['stopid'];
+    $myreq['stopid_2']=$dflt['stopid'];
+  }
+  
+  echo "<tr><form name=\"frm\"><td><input type=\"text\" size=\"8\" name=\"arrival\" value=\"${myreq['arrival']}\"></td>";
+
+  echo "<td><input type=\"text\" size=\"4\" name=\"route\" value=\"${myreq['route']}\"></td>";
+  echo "<td><input type=\"text\" size=\"4\" name=\"trip\" value=\"${myreq['trip']}\"></td>";
+  echo "<td><input type=\"text\" size=\"4\" name=\"stage\" value=\"${myreq['stage']}\"></td>";
+  echo "<td><input type=\"text\" size=\"4\" name=\"stop\" value=\"${myreq['stop']}\"></td>";
+
+  echo "<td colspan=\"2\">";
+  //--------------sub table that lives in the colspanned cell containing stop ID and stop name---
+  echo '<center><table>';
+  echo '<tr><td><center>By number:</center></td><td><b>or</b></td><td><center>By name:</center></td></tr>';
+  echo '<tr><td>';  
+    generate_stop_name_select($stopsid,'stopid_1',true,$myreq['stopid_1']);
+  echo '</td><td></td><td>';
+    generate_stop_name_select($stops,'stopid_2',false,$myreq['stopid_2']);
+  echo '</td></tr>';
+  echo '</table></center>';
+  //---------------------------------------------------------------------------------------------
+  if(isset($_REQUEST['edit']))
+  {
+    echo "</td><td><input type=\"hidden\" name=\"edit\" value=\"1\"><input type=\"hidden\" name=\"key\" value=\"${_REQUEST['key']}\"><input type=\"submit\" name=\"edit_done\" value=\"Done\"> <a href=\"index.php\">(Cancel)</a></td></form></tr>";
+  }
+  else
+  {
+    echo '</td><td><input type="submit" name="addstop" value="Add Stop to Paddle"></td></form></tr>';
+  }
+}
+
+
+//--------------------------------------------If the paddle is unmodified from its last saved state, allow the user to browse to a different one,
+//------------------------------------------create a new one, etc...
+if(isset($_REQUEST['submitadv']))
+{
+  $_SESSION['paddle']=array();
+  $lines=preg_split("/[\r\n]+/",$_REQUEST['advtxt']);
+  
+  foreach($lines as $line)
+  {
+    $mats=array();
+    if(preg_match("/([\d]+:[\d]+)[\s]+([\d]+)[\s]+([\d]+)[\s]+([\d]+)[\s]+([\d]+)[\s]+([\d]+)/",$line,$mats))
+    {
+      //echo "<pre>";
+      //print_r($mats);
+      //echo "</pre>";
+      $_SESSION['paddle'][]=array('arrival' => $mats[1], 'route' => $mats[2], 'trip' => $mats[3], 'stage' => $mats[4], 'stop' => $mats[5], 'stopid' => $mats[6]);
+    }
+  }
+  
+  $_SESSION['paddle_dirty']=true;
+  
+  //echo "<pre>";
+  //print_r($lines);
+  //echo "</pre>";
+  //exit;
+}
+
+if(!$_SESSION['paddle_dirty'])
+{
+                 
+  if(!isset($_SESSION['paddle_list']))
+  { 
+    $_SESSION['paddle_list']=paddle_list();
+  }
+
+  if( isset($_REQUEST['selectpaddle']) && ($_REQUEST['selectpaddle'] != $_SESSION['current_paddle']) )
+  {                                        
+    $_SESSION['current_paddle']=$_REQUEST['selectpaddle'];
+    unset($_SESSION['paddle']);
+    redirect('index.php');
+    exit;
+  }
+  else if( isset($_REQUEST['createnewpaddle']) )
+  {
+    $newpaddle=$_REQUEST['newpaddle'];
+    
+    if(preg_match('/[\d]+/', $newpaddle) && (intval($newpaddle > 0)) && !in_array($newpaddle,$_SESSION['paddle_list']) )
+    {//the above test insures that the new paddle number is unique, numeric, and non-zero
+      
+      $_SESSION['paddle_list'][]=$newpaddle;
+      $_SESSION['current_paddle']=$newpaddle;
+      unset($_SESSION['paddle']);
+      redirect('index.php');
+      exit;
+    }
+    else
+    {
+      echo '<h3 style="color: red;"><center>New Paddle number must be unique and non-zero.</center></h3>';
+    }
+    
+  }
+  
+  
+  echo '<form name="jump1">';
+  echo '<center><big>';
+  echo 'Select an existing paddle: ';
+  echo '<select name="myjumpbox" OnChange="location.href=jump1.myjumpbox.options[selectedIndex].value">';
+
+  echo '<option value="index.php?selectpaddle=-1">Select a Paddle</option>';
+
+  foreach($_SESSION['paddle_list'] as $paddle)
+  {
+    if($paddle == $_SESSION['current_paddle'])
+    {
+      echo "<option value=\"index.php?selectpaddle=$paddle\" selected>$paddle</option>";
+    }
+    else
+    {
+      echo "<option value=\"index.php?selectpaddle=$paddle\">$paddle</option>";
+    }
+  }  
+
+  echo '</select>';
+  
+  echo ' or create a new one: ';
+  echo '<input type="text" name="newpaddle" size="6"> <input type="submit" name="createnewpaddle" value="Create New Paddle">';
+  
+  echo '</form>';
+  echo '</big></center>';
+  echo '<hr>';
+}
+
+
+
+if(isset($_REQUEST['advanced_edit']))
+{
+  $accum="Time  Route  Trp  Stg  Stp  StopID  Stopname\n";
+  foreach($_SESSION['paddle'] as $key => $record)
+  {
+    $accum .= sprintf("%6s %5d %3d %3d %3d %5d       %s\n",$record['arrival'],$record['route'],$record['trip'],$record['stage'],$record['stop'],$record['stopid'],$stops[$record['stopid']]['name']);
+  }
+  
+  echo '<form method="POST" action="index.php">';
+  echo '<textarea rows="40" cols="90" name="advtxt">';
+  echo htmlspecialchars($accum);  
+  echo '</textarea>';
+  echo '<input type="submit" name="submitadv" value="Apply Change">';
+  echo '</form>';
+}
+else if($_SESSION['current_paddle'] > 0)
+{	//only draw the current paddle edit UI if we actually have a valid paddle selected
+
+  //--------------------------------------------------------------BELOW DEALS WITH DISPLAYING THE MAIN PADDLE MANAGEMENT SCREEN-------------
+  echo '<center><table width="90%" border="1">';
+  echo '<tr><td colspan="8"><center><a href="index.php?advanced_edit">Advanced Edit</a></center></td></tr>';
+  echo "<tr><th colspan=\"8\"><center>Paddle Number ${_SESSION['current_paddle']}</center></th></tr>";
+  echo '<tr><th>Scheduled<br>Arrival</th><th>Route #</th><th>Trip #</th><th>Stage #</th><th>Stop #</th><th>Unique<br>Stop ID</th><th>Stop<br>Name</th><th>Actions</th></tr>';
+
+  $count=0;
+  foreach($_SESSION['paddle'] as $key => $record)
+  {
+    $stopname=$stops[$record['stopid']]['name'];
+    $stopid=$record['stopid'];
+    $arrival=$record['arrival'];
+  
+    $route=$record['route'];
+    $trip=$record['trip'];
+    $stage=$record['stage'];
+    $stop=$record['stop'];
+
+    $upurl="index.php?up&key=$key";
+    $dnurl="index.php?dn&key=$key";
+    $delurl="index.php?del&key=$key";
+    $editurl="index.php?edit&key=$key";
+
+    if( ($count % 2) == 1)
+    {
+      $bkgr="#E0FFE0";
+    }
+    else
+    {
+      $bkgr="#FFE0E0";
+    }
+    
+    if(isset($_REQUEST['edit']) && ($_REQUEST['key'] == $key) )
+    {
+      generate_edit_line($record);
+    }
+    else
+    {
+      if($stopid >= 0)
+      {                                  
+        echo "<tr bgcolor=\"$bkgr\"><td>$arrival</td><td>$route</td><td>$trip</td><td>$stage</td><td>$stop</td><td>$stopid</td><td>$stopname</td><td><a href=\"$upurl\">(Move Up)</a> <a href=\"$dnurl\">(Move Down)</a> <a href=\"$delurl\">(Delete)</a> <a href=\"$editurl\">(Edit)</a></td></tr>";
+      }
+      else
+      {
+        echo "<tr bgcolor=\"$bkgr\"><td colspan=7><center><i>This stop has been deleted.</i></center></td><td><a href=\"$upurl\">(Move Up)</a> <a href=\"$dnurl\">(Move Down)</a> <a href=\"$delurl\">(Delete)</a> </td></tr>";
+      }
+    }
+  
+    $count++;
+  }
+
+  if(!isset($_REQUEST['edit']))
+  {
+    generate_edit_line(array());
+  }
+
+  if($_SESSION['paddle_dirty'])
+  {
+    echo '<tr bgcolor="#FFA0A0"><td colspan="8"><center><big><a href="index.php?commit_paddle">(Commit Changes to Paddle)</a> <a href="index.php?revert_paddle">(Revert to Saved Paddle)</a></big></center></td></tr>';
+  }
+
+  echo '</table></center>';
+}
+?>

+ 408 - 0
server/ui/fleet-management/editstops.php

@@ -0,0 +1,408 @@
+<?php
+
+function get_paddle_dependency($stopid)
+{
+global $sql;
+  $safenum=mysqli_escape_string($sql,$stopid);
+  $qry="SELECT id FROM paddles WHERE stopid='$safenum' GROUP BY id";
+  $res=mysqli_query($sql,$qry);
+  
+  $accum=array();
+  
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[]=$row['id'];
+  }
+  
+  return $accum;
+}
+
+function changed_stops()
+{
+  $accum=array();
+  
+  $temp=load_stops();
+  
+  foreach($_SESSION['stops'] as $key => $record)
+  {
+    if(isset($temp[$key]))
+    {
+      if(array_diff($temp[$key],$record))
+      {
+        $accum[$key]=$record;
+        $accum[$key]['comment'] = "Modified";
+      }
+    }
+    else
+    {
+      $accum[$key]=$record;
+      $accum[$key]['comment'] = "Added";
+    }
+  }
+  
+  foreach($temp as $key => $record)
+  {
+    if(!isset($_SESSION['stops'][$key]))
+    {
+      $accum[$key]=$record;
+      $accum[$key]['comment'] = "Deleted";
+    }
+  }
+  
+  return $accum;
+}
+
+function load_stops()
+{
+global $sql;
+
+  $qry="SELECT id,latitude,longitude,name FROM stops";
+
+  $res=mysqli_query($sql, $qry);
+  
+  $accum=array();
+    
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[$row['id']]=$row;
+  }
+  
+  return $accum;
+}
+
+function commit_stops()
+{
+global $sql;
+  mysqli_query($sql, "BEGIN");
+  mysqli_query($sql, "DELETE FROM stops");
+  
+  foreach($_SESSION['stops'] as $record)
+  {
+    $escrec=escape_array($record);
+
+    mysqli_query($sql, "INSERT INTO stops SET id='${escrec['id']}', latitude='${escrec['latitude']}', longitude='${escrec['longitude']}', name='${escrec['name']}'");  
+  }
+
+  mysqli_query($sql, "update paddles set stopid=-1 where (select count(id) from stops where stops.id = paddles.stopid) = 0");
+  
+  mysqli_query($sql, "COMMIT");
+  
+  $_SESSION['stops_dirty']=false;
+}
+
+function begin_stop_table($sortheaders)
+{
+  if($sortheaders)
+  {
+    echo '<center><table width="80%" border="1"><tr><th><a href="index.php?sortby=id&type=num">Stop ID</a></th><th>Latitude</th><th>Longitude</th><th><a href="index.php?sortby=name&type=str">Stop Name</a></th><th>Action</th></tr>';
+  }
+  else
+  {
+    echo '<center><table width="80%" border="1"><tr><th>Stop ID</th><th>Latitude</th><th>Longitude</th><th>Stop Name</th><th>Action</th></tr>';
+  }
+}
+
+function stop_compare($a, $b)
+{
+  if(isset($_SESSION['stop_sortby']))
+  {
+    $sortby=$_SESSION['stop_sortby'];
+  }
+  else
+  {
+    $sortby='name';
+  }
+  
+  $sorttype="str";
+  
+  if(isset($_SESSION['stop_sorttype']))
+  {
+    $sorttype=$_SESSION['stop_sorttype'];
+  }
+  
+  if(!strcmp($sorttype,"str"))
+  {
+    return strcmp($a[$sortby],$b[$sortby]);
+  }
+  else if(!strcmp($sorttype,"num"))
+  {
+    if(floatval($a[$sortby]) == floatval($b[$sortby])) return 0;
+    return ( floatval($a[$sortby]) < floatval($b[$sortby]) ) ? -1 : 1;
+  }
+   
+  return 0; 
+}
+
+function sort_stops()
+{
+  uasort($_SESSION['stops'],'stop_compare');  
+}
+
+//--------------------------------------------------------HANDLE LOADING STOPS IF THEY AREN'T
+if(!isset($_SESSION['stops']))
+{
+  $_SESSION['stops']=load_stops(); 
+  sort_stops();
+  $_SESSION['stops_dirty']=false;
+}
+
+//--------------------------------------------------------HANDLE SORTING STOP LIST DISPLAY
+
+if(isset($_REQUEST['sortby']))
+{
+  $_SESSION['stop_sortby']=$_REQUEST['sortby'];
+  
+  if(isset($_REQUEST['type']))
+  {
+    $_SESSION['stop_sorttype']=$_REQUEST['type'];
+  }
+
+  sort_stops();
+  redirect('index.php');
+}
+
+//-----------------------------------------------------BELOW HANDLES ADDING A STOP------------------------
+
+if(isset($_REQUEST['addstop']))
+{
+  $okay=true;
+
+  $rec=array( 'id' => $_REQUEST['num'], 'latitude' => $_REQUEST['lat'],  'longitude' => $_REQUEST['lon'], 'name' => $_REQUEST['name']);
+  
+  if(!preg_match('/[\d]+/',$rec['id']))
+  {
+    $okay=false;
+    echo "Driver Number must be entirely numeric.<br>";
+  }
+
+  foreach($_SESSION['stops'] as $record)
+  {
+    if($record['id'] == $_REQUEST['num'])
+    {
+      $okay=false;
+      echo "Stop number must be unique (not already exist in the database).<br>";
+      break;
+    }
+  }
+
+  if( (!preg_match('/[+-]?[\d]+[\.]?[\d]+/',$rec['latitude'])) || (!preg_match('/[+-]?[\d]+[\.]?[\d]+/',$rec['longitude'])) )
+  {
+    $okay=false;
+    echo "Latitude and Longitude must be valid GPS coordinates.<br>";
+  }
+
+  if($okay)
+  {
+    $_SESSION['stops'][$_REQUEST['num']] = $rec; 
+    $_SESSION['stops_dirty']=true;
+    sort_stops();
+    redirect('index.php');
+  }
+  else
+  {
+    begin_stop_table(false);
+    echo "<tr><form><td><input type=\"text\" name=\"num\" value=\"${rec['id']}\"></td><td><input type=\"text\" name=\"lat\" value=\"${rec['latitude']}\"></td><td><input type=\"text\" name=\"lon\" value=\"${rec['longitude']}\"></td><td><input type=\"text\" size=\"32\" name=\"name\" value=\"${rec['name']}\"></td><td><input type=\"submit\" name=\"addstop\" value=\"Add a Stop\"></td></form></tr>";
+    echo '</table></center>';
+    exit;
+  }
+  
+}
+
+//-----------------------------------------------------BELOW HANDLES EDITING A STOP------------------------
+
+if(isset($_REQUEST['editstop']))
+{
+  $okay=true;
+
+  if(isset($_REQUEST['editsub']))
+  {
+    $rec=array( 'id' => $_REQUEST['id'], 'latitude' => $_REQUEST['lat'],  'longitude' => $_REQUEST['lon'], 'name' => $_REQUEST['name']);
+  }
+  else
+  {
+    $rec=$_SESSION['stops'][$_REQUEST['id']];
+    $okay=false;
+  }
+  
+  if( (!preg_match('/[+-]?[\d]+[\.]?[\d]+/',$rec['latitude'])) || (!preg_match('/[+-]?[\d]+[\.]?[\d]+/',$rec['longitude'])) )
+  {
+    $okay=false;
+    echo "Latitude and Longitude must be valid GPS coordinates.<br>";
+  }
+
+  if($okay)
+  {
+    $_SESSION['stops'][$rec['id']] = $rec; 
+    
+    if(count(changed_stops()) > 0)
+    {
+      $_SESSION['stops_dirty']=true;
+      sort_stops();
+    }
+    
+    redirect('index.php');
+  }
+  else
+  {
+    begin_stop_table(false);
+    echo "<tr><form><td><input type=\"hidden\" name=\"editstop\"><input type=\"hidden\" name=\"id\" value=\"${rec['id']}\">${rec['id']}</td><td><input type=\"text\" name=\"lat\" value=\"${rec['latitude']}\"></td><td><input type=\"text\" name=\"lon\" value=\"${rec['longitude']}\"></td><td><input type=\"text\" size=\"32\" name=\"name\" value=\"${rec['name']}\"></td><td><input type=\"submit\" name=\"editsub\" value=\"Submit\"></td></form></tr>";
+    echo '</table></center>';
+    exit;
+  }
+  
+}
+//-----------------------------------------------------HANDLE THE COMMIT STOPS OPERATION-------------------
+
+if(isset($_REQUEST['commit_stops']))
+{
+  $changes=changed_stops();
+  
+  begin_stop_table(false);
+    
+  $count=0;
+  foreach($changes as $record)
+  {
+    $num=$record['id'];
+              
+    if( ($count % 2) == 1)
+    {
+      $bkgr="#E0FFE0";
+    }
+    else
+    {
+      $bkgr="#FFE0E0";
+    }
+
+    echo "<tr bgcolor=\"$bkgr\"><td>$num</td><td>${record['latitude']}</td><td>${record['longitude']}</td><td>${record['name']}</td><td>${record['comment']}</td></tr>";    
+    $count++;
+  }
+                                                            
+  echo "<tr><td colspan=\"5\"><center><big>Really apply these $count changes?</big></center></td></tr>";
+  echo "<tr><td colspan=\"5\"><center><big><a href=\"index.php\">(Cancel)</a> <a href=\"index.php?commit_stopsconf\">(OK)</a></big></center></td></tr>";
+                                                                
+                                                                
+  echo '</table></center>';
+  exit;
+}
+else if(isset($_REQUEST['commit_stopsconf']))
+{
+  commit_stops();
+  unset($_SESSION['stops']);
+  redirect('index.php');
+}
+//-----------------------------------------------------HANDLE THE REVERT STOPS OPERATION-------------------
+
+if(isset($_REQUEST['revert_stops']))
+{
+  $changes=changed_stops();
+  
+  begin_stop_table(false);
+    
+  $count=0;
+  foreach($changes as $record)
+  {
+    $num=$record['id'];
+              
+    if( ($count % 2) == 1)
+    {
+      $bkgr="#E0FFE0";
+    }
+    else
+    {
+      $bkgr="#FFE0E0";
+    }
+
+    echo "<tr bgcolor=\"$bkgr\"><td>$num</td><td>${record['latitude']}</td><td>${record['longitude']}</td><td>${record['name']}</td><td>${record['comment']}</td></tr>";    
+    $count++;
+  }
+                                                            
+  echo "<tr><td colspan=\"5\"><center><big>Really lose these $count changes?</big></center></td></tr>";
+  echo "<tr><td colspan=\"5\"><center><big><a href=\"index.php\">(Cancel)</a> <a href=\"index.php?revert_stopsconf\">(OK)</a></big></center></td></tr>";
+                                                                
+                                                                
+  echo '</table></center>';
+  exit;
+}
+else if(isset($_REQUEST['revert_stopsconf']))
+{
+  unset($_SESSION['stops']);
+  redirect('index.php');
+}
+
+//-----------------------------------------------------HANDLE THE DELETE STOP OPERATION--------------------
+if(isset($_REQUEST['delstop']))
+{
+
+  begin_stop_table(false);
+  
+  $bkgr="#E0FFE0";
+  $record=$_SESSION['stops'][$_REQUEST['id']];
+  echo '<tr><td colspan="5"><center><big>Really delete this stop?</big></center></td></tr>';
+  echo "<tr bgcolor=\"$bkgr\"><td>${record['id']}</td><td>${record['latitude']}</td><td>${record['longitude']}</td><td>${record['name']}</td><td>${record['comment']}</td></tr>";    
+
+  $deps=get_paddle_dependency($_REQUEST['id']);
+  $count=count($deps);
+   
+  if($count > 0)
+  {
+    echo '<tr><td colspan="5" style="color: red"><big>';
+    echo "Deleting this stop will alter the following $count paddles: <b>";
+    $first=true;
+    foreach($deps as $val)
+    {
+      if($first)
+      {
+        echo "$val";
+        $first=false;
+      }
+      else
+      {
+        echo ", $val";
+      }
+    }
+    echo "</b></big></td></tr>";
+  }  
+  echo "<tr><td colspan=\"5\"><center><big><a href=\"index.php\">(Cancel)</a> <a href=\"index.php?delstopconf&id=${_REQUEST['id']}\">(OK)</a></big></center></td></tr>";
+  echo '</table></center>';
+  exit;
+}
+else if(isset($_REQUEST['delstopconf']))
+{
+  unset($_SESSION['stops'][$_REQUEST['id']]);
+  $_SESSION['stops_dirty']=true;
+  redirect('index.php');
+}
+//-----------------------------------------------------BELOW GENERATES THE MAIN SCREEN FOR STOP MANAGEMENT
+
+begin_stop_table(true);
+  
+$count=0;
+foreach($_SESSION['stops'] as $record)
+{
+    $num=$record['id'];
+    $delurl="index.php?delstop&id=$num";
+    $editurl="index.php?editstop&id=$num";
+
+    if( ($count % 2) == 1)
+    {
+      $bkgr="#E0FFE0";
+    }
+    else
+    {
+      $bkgr="#FFE0E0";
+    }
+      
+    echo "<tr bgcolor=\"$bkgr\"><td>$num</td><td>${record['latitude']}</td><td>${record['longitude']}</td><td>${record['name']}</td><td><a href=\"$delurl\">(Delete)</a> <a href=\"$editurl\">(Edit)</a></td></tr>";    
+    $count++;
+}
+  
+echo '<tr><form><td><input type="text" name="num"></td><td><input type="text" name="lat"></td><td><input type="text" name="lon"></td><td><input type="text" size="32" name="name"></td><td><input type="submit" name="addstop" value="Add a Stop"></td></form></tr>';
+  
+if($_SESSION['stops_dirty'])
+{
+  echo '<tr bgcolor="#FFA0A0"><td colspan="4"><center><big><a href="index.php?commit_stops">(Commit Changes to Stops)</a> <a href="index.php?revert_stops">(Revert to Saved Stops)</a></big></center></td></tr>';
+}
+  
+echo '</table></center>';
+
+?>

+ 2 - 0
server/ui/fleet-management/example.csv

@@ -0,0 +1,2 @@
+seq_num,note,equip_num,driver,paddle,route,trip,stop,ride_time,latitude,longitude,action,rule,ruleparam,reason,credential,logical_card_id,cash_value,stop_name
+929934820,test annotation,123,123,123,123,123,123,="2015-10-02 17:50",4227.51378,-7625.79496,ACCEPT,CASH-ADULT,STUDENT,Adult Cash Fare,="123:45",123,123,Example stop name

+ 120 - 0
server/ui/fleet-management/export_bus_files.php

@@ -0,0 +1,120 @@
+#!/usr/bin/php
+<?php
+
+//echo getcwd() . "\n";
+//exit;
+
+include 'connect.php';
+
+//----------------------------extract the drivers
+
+$res=mysqli_query($sql,'SELECT id, pin, name FROM drivers');
+
+$f=fopen("drivers.txt","wb");
+fwrite($f,"#driver\tpin\tname\n");
+
+while($row=mysqli_fetch_assoc($res))
+{
+  fwrite($f,sprintf("%d\t%s\t%s\n",$row['id'], $row['pin'], $row['name']));
+}
+
+fclose($f);
+
+//----------------------------extract the paddles
+$res=mysqli_query($sql,'SELECT live_paddles.*, live_stops.latitude, live_stops.longitude, live_stops.name FROM live_paddles,live_stops WHERE live_paddles.stopid=live_stops.id ORDER BY live_paddles.id,slot;');
+
+$paddle=array();
+while($row=mysqli_fetch_assoc($res))
+{
+  $paddle[]=$row;  
+}
+
+for($i=0;$i < count($paddle); $i++)
+{
+  if(strlen($paddle[$i]['arrival']) == 0)
+  {
+    
+    for($j=$i;$j < count($paddle); $j++)
+    {
+      if(strlen($paddle[$j]['arrival'] > 0))
+      {
+        $firsttime=array();
+        preg_match('/([\d]+):([\d]+)/',$lastarr,$firsttime);
+
+        $lasttime=array();
+        preg_match('/([\d]+):([\d]+)/',$paddle[$j]['arrival'],$lasttime);
+      
+        $ft_min=$firsttime[2] + ($firsttime[1] * 60);
+        $lt_min=$lasttime[2] + ($lasttime[1] * 60);
+      
+        if($lt_min < $ft_min)
+          $lt_min += (60 * 24);
+      
+      
+        $timedist=$lt_min - $ft_min;
+        $stopdist=1 + ($j - $i);
+        
+        $rate=floatval($timedist) / floatval($stopdist);
+        $t=$i-1;
+        echo "Interpolation plan: From $ft_min to $lt_min in $stopdist stops ( $i >= k < $j ) at $rate/stop\n";
+        
+        for($k=0; $k < $stopdist; $k++)
+        {
+          $temp=$ft_min + ($rate * ($k+1));
+          $hours=(intval($temp) / 60) % 24;
+          $mins=intval($temp) % 60;
+
+          $arr=sprintf("%02d:%02d\n",$hours,$mins);
+          $paddle[$i + $k]['arrival']=$arr;
+        }
+        break;
+      }
+    }
+  }
+  else
+  {
+     $lastarr=$paddle[$i]['arrival']; 
+  }
+}
+
+$current=false;
+$f=false;
+
+foreach($paddle as $row)
+{
+  if($current != $row['id'])
+  {
+    if($f)
+      fclose($f);
+      
+    $current=$row['id'];
+    echo "Constructing paddle $current.paddle:\n";
+    $f=fopen($current . ".paddle","wb");
+
+    fwrite($f,sprintf("#Paddle %d generated %s\n",$current,strftime("%D %T")));
+    fwrite($f,"#scheduled arrival  |     location       |      stop data\n");
+    fwrite($f,"#hour   mintute latitude        longitude       route   trip    stop    name\n");
+  }
+
+  if(!$f)
+  {
+    die("Cannot open file $current.paddle!\n");
+  }
+  
+  $tm=array();
+  preg_match('/([\d]+):([\d]+)/',$row['arrival'],$tm);
+  
+  if(!isset($tm[1]))
+  {
+    $tm[1]='0';
+    $tm[2]='0';  
+  }
+  
+  fwrite($f,sprintf("%d\t%d\t%f\t%f\t%d\t%d\t%d\t%s\n",
+     $tm[1],$tm[2],$row['latitude'],$row['longitude'],$row['route'],$row['trip'],$row['stop'],$row['name'])); 
+}
+
+if($f)
+  fclose($f);
+
+?>

+ 62 - 0
server/ui/fleet-management/index.php

@@ -0,0 +1,62 @@
+<?php
+
+$sql;
+
+$okaydest=array('mainmenu' => 'mainmenu.php', 'editdrivers' => 'editdrivers.php', 'editstops' => 'editstops.php', 'editpaddles' => 'editpaddles.php', 'manageconfig' => 'manageconfig.php');
+
+include 'connect.php';
+include 'util.php';
+
+header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+header("Cache-Control: no-cache");
+header("Pragma: no-cache");
+
+session_start();
+
+if(array_key_exists('goto',$_REQUEST))
+{
+  $dest=$_REQUEST['goto'];
+     
+  if(array_key_exists($dest,$okaydest))
+  {
+    
+    if( $_SESSION['stops_dirty'] || $_SESSION['drivers_dirty'] || $_SESSION['paddle_dirty'])
+    {
+      echo '<center><h1 style="color: red">You have unsaved changes.  Select revert or commit before returning to the main menu.</h1></center>';
+    }
+    else
+    {
+      unset($_SESSION['stops']);
+      unset($_SESSION['drivers']);
+      unset($_SESSION['paddle']);
+      unset($_SESSION['paddle_list']);
+      
+      $_SESSION['page']=$dest;
+      $_SESSION['pageaddr']=$okaydest[$dest];
+      redirect('index.php');
+    }
+  }
+  else
+  {
+    redirect('index.php');
+  }
+}
+
+if(!isset($_SESSION['page']))
+{
+  $_SESSION['page']='mainmenu';
+  $_SESSION['pageaddr']=$okaydest['mainmenu'];
+}                                                        
+
+if(isset($_SESSION['pageaddr']))
+{
+  include $_SESSION['pageaddr'];
+}
+          
+echo '<div style="background: #E0E0FF;">';
+echo '<table width="80%" align="center"><tr>';
+echo "<td><a href=\"index.php?goto=mainmenu\">Return to Main Menu...</a></td>";
+echo '</tr></table>';
+echo '</div>';
+                      
+?>

+ 8 - 0
server/ui/fleet-management/mainmenu.php

@@ -0,0 +1,8 @@
+<big>
+<a href="index.php?goto=editdrivers">Edit Drivers</a><br />
+<a href="index.php?goto=editstops">Edit Stops</a><br />
+<a href="index.php?goto=editpaddles">Edit Paddles</a><br />
+<a href="bulk_update.php">Bulk Update</a><br />
+<a href="annotation.php">Upload Annotations</a><br />
+<a href="index.php?goto=manageconfig">Manage Configurations</a><br />
+</big>

+ 146 - 0
server/ui/fleet-management/manageconfig.php

@@ -0,0 +1,146 @@
+<?php
+
+function commit_sandbox_to_live($comment)
+{
+global $sql;
+  $safever=mysqli_escape_string($sql,date('Y-m-d H:i:s') . " " . $comment);
+
+  mysqli_query($sql,"BEGIN");
+ 
+  mysqli_query($sql,"INSERT INTO old_paddles SELECT '$safever' AS verstring, paddles.* FROM paddles");
+  mysqli_query($sql,"DELETE FROM live_paddles");
+  mysqli_query($sql,"INSERT INTO live_paddles SELECT paddles.* FROM paddles");
+
+  mysqli_query($sql,"INSERT INTO old_stops SELECT '$safever' AS verstring, stops.* FROM stops");
+  mysqli_query($sql,"DELETE FROM live_stops");
+  mysqli_query($sql,"INSERT INTO live_stops SELECT stops.* FROM stops");
+  
+  mysqli_query($sql,"COMMIT");
+
+  $tmp=fopen("/tmp/new_paddles_exist","wb");
+  if($tmp) fclose($tmp);
+
+}
+
+function version_list()
+{
+global $sql;
+  $accum=array();
+  
+  $res=mysqli_query($sql,"SELECT verstring FROM old_stops GROUP BY verstring ORDER BY verstring DESC");
+
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[]=$row['verstring'];
+  }
+
+  return $accum;
+}
+
+function load_sandbox_from_old($version)
+{
+global $sql;
+  $safever=mysqli_escape_string($sql,$version);
+  mysqli_query($sql,"BEGIN");
+  
+  $res=mysqli_query($sql,"SELECT COUNT(id) AS num FROM old_stops WHERE verstring='$safever'");
+  $row=mysqli_fetch_assoc($res);
+  if($row['num'] < 1)
+  {
+    mysqli_query($sql,"ROLLBACK");
+    return false;
+  }
+  
+  $res=mysqli_query($sql,"SELECT COUNT(id) AS num FROM old_paddles WHERE verstring='$safever'");
+  $row=mysqli_fetch_assoc($res);
+  if($row['num'] < 1)
+  {
+    mysqli_query($sql,"ROLLBACK");
+    return false;
+  }
+  
+  mysqli_query($sql,"DELETE FROM paddles");  
+  mysqli_query($sql,"INSERT INTO paddles SELECT id, slot, arrival, route, trip, stage, stop, stopid FROM old_paddles WHERE verstring='$safever'");
+
+  mysqli_query($sql,"DELETE FROM stops");  
+  mysqli_query($sql,"INSERT INTO stops SELECT id, latitude, longitude, name FROM old_stops WHERE verstring='$safever'");
+ 
+  mysqli_query($sql,"COMMIT"); 
+  
+  return true;
+}
+
+$vers=version_list();
+
+if(isset($_REQUEST['commit_confirm']))
+{
+  if( strlen(trim($_REQUEST['comment'])) < 1)
+  {
+    echo "You must supply a comment.<br>";
+  } 
+  else
+  {
+    commit_sandbox_to_live($_REQUEST['comment']);
+    echo "Changes Committed to live busses.<br>";
+    echo '<a href="index.php?goto=mainmenu>Return to Main Menu</a>';
+    exit();
+  } 
+}
+
+if(isset($_REQUEST['commit_to_live']))
+{
+  echo "<h1>Are you sure you want to commit your current paddles?  Doing so will place them in the live database and they will go into effect as the busses synchronize.</h1>";
+  echo "<h2><a href=\"index.php\">Do Not Commit</a></h2>";
+  echo '<form method="POST">Enter your comment for this revision: <input type="text" size="32" name="comment"> <input type="submit" name="commit_confirm" value="Commit Changes"></form><br>'; 
+}
+
+
+if(isset($_REQUEST['load_old_confirm']))
+{
+  $found=false;
+  
+  foreach($vers as $ver)
+  {
+    if(!strcmp($ver,$_REQUEST['version']))
+    {
+      $found=true;
+      break;
+    }
+  }
+  
+  if(!$found)
+  {
+    echo "Could not find saved data version \"${_REQUEST['version']}\"!<br>";
+  }
+  else
+  {
+    load_sandbox_from_old($_REQUEST['version']);
+    echo "Previous configuration \"${_REQUEST['version']}\" loaded.<br>";
+    echo '<a href="index.php?goto=mainmenu>Return to Main Menu</a>';
+    exit();
+  }
+}
+
+if(isset($_REQUEST['load_old']))
+{
+  echo '<form method="POST">';
+  echo '<p style="color: red;">Loading saved data will wipe out any stops and paddles currently in the editor.</p>';
+  echo 'Please select an old configuration to load into the editor: <select name="version"><option value="">Select</option>';
+  foreach($vers as $ver)
+  {
+    echo "<option value=\"$ver\">$ver</option>";
+  }
+  echo "</select> ";
+  echo '<input type="submit" name="load_old_confirm" value="Load">';
+  echo '</form>';
+  
+  echo '</form>';
+}
+
+echo '<hr>';
+
+echo '<a href="index.php?commit_to_live">Commit current Paddles and Stops to the <b>live server.</b></a>';
+echo '<br>';
+echo '<a href="index.php?load_old">Load Old Configuration into editor</a>';
+
+?>

+ 129 - 0
server/ui/fleet-management/util.php

@@ -0,0 +1,129 @@
+<?php
+
+function getval($qry)
+{
+global $sql;
+  $res=mysqli_query($sql, $qry);
+  $row=mysqli_fetch_array($res,MYSQL_NUM);
+  return $row[0];
+}
+
+function login($username, $password)
+{
+global $sql;
+  $safeuser=mysqli_escape_string($sql, $username);
+  $combo=mysqli_escape_string($sql, $username . $password);
+  
+  $qry="SELECT id FROM users WHERE uname='$safeuser' AND secret=SHA1('$combo')";
+  
+  $res=mysqli_query($sql, $qry);
+  
+  $row=mysqli_fetch_assoc($res);
+  
+  if(isset($row['id']))
+  {
+    $_SESSION['uid']=$row['id'];
+    $_SESSION['uname']=$username;
+    return true;
+  }
+  else
+  {
+    session_unset();
+    return false;
+  }
+
+}
+
+
+function logout()
+{
+  session_unset();
+}
+
+function redirect($extra)
+{
+  $host  = $_SERVER['HTTP_HOST'];
+  $uri  = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
+
+/* DEBUG */
+  header("Location: http://$host$uri/$extra");
+
+  //header("Location: https://$host$uri/$extra");
+  exit;
+}
+                    
+
+function generate_select_box($name,$current,$arr,$keyandval=true,$onchange="")
+{
+  if(strlen($onchange) > 0)
+  {
+    echo "<select name=\"$name\" onChange=\"$onchange\">\n";
+  }
+  else
+  {
+    echo "<select name=\"$name\">\n";
+  }
+  
+  foreach($arr as $k => $v)
+  {
+    if($keyandval)
+    {
+      $disp="$k ($v)";
+    }
+    else
+    {
+      $disp=$v;
+    }
+        
+    if( (($k == $current) && $keyandval) || (!$keyandval && ($v == $current)) )
+    {
+      echo "<option selected value=\"$k\">$disp</option>\n";  
+    }
+    else
+    {
+      echo "<option value=\"$k\">$disp</option>\n";  
+    }
+  }
+  
+  echo "</select>\n";
+}
+
+function escape_array($arr)
+{
+global $sql;
+  $accum=array();
+  
+  foreach($arr as $k => $v)
+  {
+    $accum[$k]=mysqli_escape_string($sql, $v);
+  }
+  
+  return $accum;
+}
+
+function dbgprint($v)
+{
+  echo "<pre>";
+  print_r($v);
+  echo "</pre>";
+}
+
+function get_userlist()
+{
+global $sql;
+  $res=mysqli_query($sql, "SELECT id, uname FROM users");
+
+  if(!$res)
+    return array();
+    
+  $accum=array();
+  
+  while($row=mysqli_fetch_assoc($res))
+  {
+    $accum[]=$row;
+  }
+  
+  return $accum;  
+}
+
+?>