Changeset - 2afaa65abd5e
[Not reviewed]
default
0 3 0
Nathan Brink (binki) - 15 years ago 2011-03-21 23:30:34
ohnobinki@ohnopublishing.net
Provide a mechanism for validating users' inputs in PHP and letting users mend their collectively sinful ways.
3 files changed with 126 insertions and 5 deletions:
0 comments (0 inline, 0 general)
inc/class.schedule.php
Show inline comments
 
@@ -87,57 +87,70 @@ class Schedule
 
    $this->storage = array();
 
    $this->title = "SlatePermutate - Scheduler";
 
    $this->parent_id = $parent;
 

	
 
    /* mark this as an upgraded Schedule class. See __wakeup() */
 
    $this->nclasses = -1;
 
  }
 

	
 
  //--------------------------------------------------
 
  // Mutators and Accessors
 
  //--------------------------------------------------
 
  public function getName()
 
  {
 
    return $this->scheduleName;
 
  }    
 

	
 
  //--------------------------------------------------
 
  // Adds a new class to the schedule.
 
  //--------------------------------------------------
 
  function addCourse($n)
 
  {
 
    $this->courses[] = new Course($n);
 
  }
 

	
 
  //--------------------------------------------------
 
  // Adds a section to the desired class.
 
  //--------------------------------------------------
 
  /**
 
   * \brief
 
   *   Adds a section to this semester after finding the class.
 
   *
 
   * \return
 
   *   NULL on success, a string on error which is a message for the
 
   *   user and a valid XHTML fragment.
 
   */
 
  function addSection($course_name, $letter, $time_start, $time_end, $days, $synonym = NULL, $faculty = NULL, $location = NULL, $type = 'lecture')
 
  {
 
    if (empty($letter) && (empty($time_start) || !strcmp($time_start, 'none')) && (empty($time_end) || !strcmp($time_end, 'none')) && empty($days)
 
	&& empty($synonym) && empty($faculty) && empty($location) && (empty($type) || !strcmp($type, 'lecture')))
 
      return;
 

	
 
    /* reject invalid times */
 
    if (!strcmp($time_start, 'none') || !strcmp($time_end, 'none')
 
	|| $time_start > $time_end)
 
      {
 
	return 'Invalid time specifications for ' . htmlentities($course_name) . '-' . htmlentities($letter)
 
	  . '. Start time: <tt>' . htmlentities($time_start) . '</tt>. End time: <tt>' . htmlentities($time_end) . '</tt>.';
 
      }
 

	
 
    foreach ($this->courses as $course)
 
      if (!strcmp($course_name, $course->getName()))
 
	{
 
	  $section = $course->section_get($letter);
 
	  if (!$section)
 
	    {
 
	      $section = new Section($letter, array(), $synonym, $faculty);
 
	      $course->section_add($section);
 
	    }
 
	  $section->meeting_add(new SectionMeeting($days, $time_start, $time_end, $location, $type));
 

	
 
	  return;
 
	}
 

	
 
    error_log('Could not find class when parsing schedule from postData: ' . $course_name);
 
    echo 'Could not find class: ' . $course_name . "<br />\n";
 
  }
 

	
 
  //--------------------------------------------------
 
  // Finds all of the possible permutations and stores
 
  // the results in the storage array.
 
  //--------------------------------------------------
 
	function findPossibilities()
 
	{
input.php
Show inline comments
 
@@ -8,74 +8,108 @@
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * SlatePermutate is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU Affero General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU Affero General Public License
 
 * along with SlatePermutate.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
include_once 'inc' . DIRECTORY_SEPARATOR . 'class.schedule.php';
 
include_once 'inc' . DIRECTORY_SEPARATOR . 'class.course.inc';
 
include_once 'inc' . DIRECTORY_SEPARATOR . 'class.section.php';
 
include_once 'inc' . DIRECTORY_SEPARATOR . 'class.page.php';
 
require_once('inc' . DIRECTORY_SEPARATOR . 'schedule_store.inc');
 

	
 
$scripts = array('jQuery', 'jQueryUI', 'qTip2','schedInput');
 
$inputPage = page::page_create('Scheduler', $scripts, FALSE);
 

	
 
$schedule_store = FALSE;
 
$sch = FALSE;
 
$fix_errors = FALSE;
 
$school = $inputPage->get_school();
 

	
 
$parent_schedule_id = NULL;
 
if (isset($_REQUEST['s']))
 
  {
 
    $schedule_store = schedule_store_init();
 
    $parent_schedule_id = (int)$_REQUEST['s'];
 
    $sch = schedule_store_retrieve($schedule_store, $parent_schedule_id);
 
  }
 
elseif (!empty($_REQUEST['e']))
 
  {
 
    /*
 
     * Read an errorful schedule out of $_POST, this $_POST is created
 
     * by process.php when the originally sinful user produces bad
 
     * data.
 
     */
 
    $errors_fix = TRUE;
 
    $parent_schedule_id = (int)$_POST['postData']['parent_schedule_id'];
 
  }
 

	
 
$my_hc = 'var slate_permutate_example_course_id = \'' . str_replace('\'', '\\\'', school_example_course_id($inputPage->get_school())) . '\';
 

	
 
jQuery(document).ready(
 
  function()
 
  {
 
    var class_last = 0;
 

	
 
';
 
if ($sch)
 
{
 
  $nclasses = $sch->nclasses_get();
 
  for ($class_key = 0; $class_key < $nclasses; $class_key ++)
 
    {
 
      $my_hc .= input_class_js($sch->class_get($class_key), '    ');
 
    }
 
}
 
elseif ($errors_fix)
 
  {
 
    foreach ($_POST['postData'] as $course)
 
      if (is_array($course))
 
	{
 
	  if (empty($course['name']))
 
	    $my_hc .= '    class_last = add_class();' . PHP_EOL;
 
	  else
 
	    $my_hc .= '    class_last = add_class_n(\'' . htmlentities($course['name'], ENT_QUOTES) . '\');' . PHP_EOL;
 
	  foreach ($course as $section)
 
	    if (is_array($section))
 
	      $my_hc .= '    add_section_n(class_last, \'' . htmlentities($section['letter'], ENT_QUOTES) . '\', \''
 
		. htmlentities($section['synonym'], ENT_QUOTES) . '\', \'' . htmlentities($section['start'], ENT_QUOTES) . '\', \''
 
		. htmlentities($section['end'], ENT_QUOTES) . '\', '
 
		. json_encode(array('m' => !empty($section['days'][0]), 't' => !empty($section['days'][1]), 'w' => !empty($section['days'][2]),
 
				    'h' => !empty($section['days'][3]), 'f' => !empty($section['days'][4]),
 
				    's' => !empty($section['days'][5])))
 
		. ', \'' . htmlentities($section['professor'], ENT_QUOTES) . '\', \''
 
		. htmlentities($section['location'], ENT_QUOTES) . '\', \''
 
		. htmlentities($section['type'], ENT_QUOTES) . '\');' . PHP_EOL;
 
	  $my_hc .= PHP_EOL;
 
	}
 
  }
 
else
 
  {
 
    $default_courses = school_default_courses($school);
 
    foreach ($default_courses as $default_class)
 
      $my_hc .= input_class_js($default_class, '    ');
 
  }
 
$my_hc .= '    class_last = add_class();' . PHP_EOL;
 
if ($qtips_always || !isset($_SESSION['saw_qtips']))
 
  {
 
    $my_hc .= '    addTips();' . PHP_EOL;
 
    $_SESSION['saw_qtips'] = TRUE;
 
  }
 
$my_hc .= '  });
 
';
 

	
 
$inputPage->headcode_add('scheduleInput', $inputPage->script_wrap($my_hc), TRUE);
 

	
 
$inputPage->head();
 

	
 
/*
 
 * Force a student to choose a school or declare he's a generic
 
 * student before displaying the input form. To do this, we need
 
 * another variable in $_SESSION: $_SESSION['school_chosen'].
 
 */
 
@@ -114,49 +148,62 @@ if (!empty($_REQUEST['selectsemester']))
 
  $inputPage->showSemesters();
 
  $inputPage->foot();
 
  exit;
 
  }
 

	
 
$inputPage->showSavedScheds($_SESSION);
 
?>
 
<p>
 
  Welcome to SlatePermutate<?php $inputPage->addressStudent(', ', '', FALSE); ?>!
 
  <?php if (school_has_auto($inputPage->get_school())): ?>
 
  To get started, enter in some a course identifier (e.g., <em>
 
  <?php echo school_example_course_id($inputPage->get_school()); ?></em>)
 
  and click the autosuggestion to automatically load available sections
 
  for each class.
 
  <?php else: ?>
 
  To get started, enter a course number and add some sections to it.
 
  Then specify each section's letter/number and what times it meets,
 
  add more courses, and click &ldquo;Find a Schedule&rdquo;.
 
  <!--'-->
 
  <?php endif; ?>
 
</p>
 

	
 
<form method="post" action="process.php" id="scheduleForm">
 
<p class="nospace" style="border-left: 5px solid #999; padding-left: 5px!important; padding-top: 5px!important;"><label>Schedule Name</label><br />
 
  <input id="scheduleName" style="margin-bottom: 1em;" class="defText required" type="text" size="25" title="<?php echo $inputPage->semester['name'] ?>" name="postData[name]" <?php if ($sch) echo 'value="' . htmlentities($sch->getName(), ENT_QUOTES) . '"'; ?> />
 
<input
 
    id="scheduleName"
 
    style="margin-bottom: 1em;"
 
    class="defText required"
 
    type="text"
 
    size="25"
 
    title="<?php echo $inputPage->semester['name'] ?>"
 
    name="postData[name]"
 
    <?php
 
      if ($sch)
 
        echo 'value="' . htmlentities($sch->getName(), ENT_QUOTES) . '"';
 
      elseif ($errors_fix)
 
        echo 'value="' . htmlentities($_POST['postData']['name'], ENT_QUOTES) . '"';
 
    ?> />
 
  <?php if (!empty($parent_schedule_id)): ?>
 
  <input type="hidden" name="postData[parent_schedule_id]" value="<?php echo htmlentities($parent_schedule_id); ?>" />
 
  <?php endif; ?>
 
</p>
 

	
 
<table id="container">
 
  <tr>
 
    <td>
 
      <table id="jsrows">
 
	<!-- Allow CSS to apply to entire rows at a time. -->
 
	<colgroup>
 
	  <col />
 
	  <col />
 
	  <col />
 
	  <col />
 
	  <col />
 
	  <col />
 
	  <col />
 
	  <col />
 
	  <col />
 
	  <col />
 
	  <col class="saturday<?php if (school_has_auto($inputPage->get_school())) echo ' collapsed';?>" />
 
	  <col />
 
	  <col />
process.php
Show inline comments
 
@@ -51,48 +51,71 @@ function arrayToDays($array, $mode = 'nu
 
		{
 
			if(isset($array[$i]) && $array[$i] == $key){
 
				$outString .= $days[$i];
 
				if($pretty)
 
					$outString .= ', ';
 
			}
 
		}
 
		if($pretty){
 
			$outString = substr($outString,0,strlen($outString) - 2); // Remove last comma and space
 
			$outString = substr($outString,0, strrpos( $outString, ' ')) . ' and' . substr($outString, strrpos( $outString, ' '), strlen($outString));
 
		}
 
	}
 
	else {
 
		for($i=0; $i < 6; $i++)
 
			if(isset($array[$i]))
 
				$outString = $days[$i];
 
	}
 
	return $outString;
 
}
 

	
 
function prettyTime($time){
 
	return substr($time,0,strlen($time)-2) . ":" . substr($time,strlen($time)-2, strlen($time));
 
}
 

	
 
/**
 
 * \brief
 
 *   Convert a multidimensional array to a set of <input />s.
 
 *
 
 * Currently just echos out the <input />s as they are created.
 
 *
 
 * \param $array
 
 *   The array to make into a set of <input />s.
 
 * \param $base
 
 *   The string to prefix. Normally the name of the array variable.
 
 * \param $blankness
 
 *   A string to insert at the beginning of each line before an <input
 
 *   /> for indentation's sake.
 
 */
 
function array_to_form($array, $base = '',  $blankness = '        ')
 
{
 
  foreach ($array as $key => $val)
 
    if (is_array($val))
 
      array_to_form($val, $base . '[' . $key . ']', $blankness);
 
    else
 
      echo $blankness . '<input name="' . htmlentities($base . '[' . $key . ']') . '" value="' . htmlentities($val) . '" type="hidden" />' . PHP_EOL;
 
}
 

	
 
/*
 
 * The below code relies on sessions being started already.
 
 */
 
page::session_start();
 

	
 
$DEBUG = FALSE;
 
if (isset($_GET['debug']))
 
  $DEBUG = $_GET['debug'];
 

	
 
$schedule_store = schedule_store_init();
 

	
 
if(!$DEBUG)
 
  {
 
    $s = FALSE;
 
    if (isset($_GET['s']))
 
      $s = $_GET['s'];
 

	
 
    if($s !== FALSE)
 
      {
 
	$savedSched = schedule_store_retrieve($schedule_store, $s);
 
	if ($savedSched)
 
	  $savedSched->writeoutTables($schedule_store);
 
	else
 
	  page::show_404('Unable to find a saved schedule with an ID of ' . $s . '.');
 
@@ -117,68 +140,106 @@ if(!$DEBUG)
 
      }
 
    else
 
      {
 
	/*
 
	 * we probably have input from the user and should interpret
 
	 * it as a schedule to permutate. Then we should redirect the
 
	 * user to the canonical URL for that schedule.
 
	 */
 
	$name = '';
 
	if (!empty($_POST['postData']['name']))
 
	  $name = $_POST['postData']['name'];
 

	
 
	$parent_schedule_id = NULL;
 
	if (!empty($_POST['postData']['parent_schedule_id']))
 
	  {
 
	    $parent_schedule_id = (int)$_POST['postData']['parent_schedule_id'];
 
	    $parent_schedule = schedule_store_retrieve($schedule_store, $parent_schedule_id);
 
	    /* Detect bad parent_schedule reference. */
 
	    if (empty($parent_schedule))
 
	      $parent_schedule_id = NULL;
 
	  }
 

	
 
	$allClasses = new Schedule($name, $parent_schedule_id);
 

	
 
	$errors = array();
 
		foreach($_POST['postData'] as $class)
 
		{
 
		  /*
 
		   * Only add classes if the user added at least one
 
		   * section to the class. We know that $class['name']
 
		   * is not a section, so count() needs to be > 1 and
 
		   * we need to skip over 'name' in our loop.
 
		   */
 
			if(is_array($class) && count($class) > 1)
 
			{
 
				$allClasses->addCourse($class['name']);
 
		
 
				foreach($class as $section)
 
				  /* Skip the section name, which isn't a section */
 
					if(is_array($section))
 
					  {
 
					    $allClasses->addSection($class['name'], $section['letter'], $section['start'], $section['end'], arrayToDays($section['days'], 'alpha'), $section['synonym'], $section['professor'], $section['location'], $section['type']);
 
					    $error_string = $allClasses->addSection($class['name'], $section['letter'], $section['start'], $section['end'], arrayToDays($section['days'], 'alpha'), $section['synonym'], $section['professor'], $section['location'], $section['type']);
 
					    if ($error_string !== NULL)
 
					      $errors[] = $error_string;
 
					  }
 
			}
 
		}
 

	
 
		/*
 
		 * Tell the user that his input is erroneous and
 
		 * require him to fix it.
 
		 */
 
		if (count($errors))
 
		  {
 
		    $error_page = new Page('Process Schedule — Errors');
 

	
 
		    echo '        <p>' . PHP_EOL
 
		      . '          You have the following errors in your input:' . PHP_EOL
 
		      . '        </p>' . PHP_EOL
 
		      . '        <ul>' . PHP_EOL;
 
		    foreach ($errors as $error)
 
		      echo '          <li>' . $error . '</li>' . PHP_EOL;
 
		    echo '        </ul>' . PHP_EOL
 
		      . '        <h3>Solving Errors</h3>' . PHP_EOL
 
		      . '        <ul>' . PHP_EOL
 
		      . '          <li>Most importantly, click the <em>Fix</em> button below to return to the schedule editing page to resolve these errors. Hitting your browser\'s <em>Back</em> button will cause your input to be lost.</li>' . PHP_EOL
 
		      . '          <li>Ensure that no section\'s start or end times are left blank. Any blank start or end times are shown as <tt>none</tt> in the above error output.</li>' . PHP_EOL
 
		      . '          <li>Ensure that a section\'s end time is later in the day than its start time.</li>' . PHP_EOL
 
		      . '          <li>If you are having trouble resolving these issues, please feel free to <a href="feedback.php">leave us feedback</a>. Be sure to describe your problem with as much detail as possible; otherwise we may only be able to make conjectures about the errors instead of finding and fixing any bugs. Thanks! <em>(To provide us with the most reliable data, save this webpage onto disk and paste the entire (X)HTML source into the feedback form.)</em></li>' . PHP_EOL
 
		      . '        </ul>' . PHP_EOL;
 

	
 
		    /* Regurgitate the postData into a <form /> */
 
		    echo '        <form action="input.php" method="post">' . PHP_EOL
 
		      . '          <input name="e" value="1" type="hidden" />' . PHP_EOL;
 
		    array_to_form($_POST['postData'], 'postData', '          ');
 
		    echo '          <button type="submit" class="gray">Fix Errors!</button>' . PHP_EOL
 
		      . '        </form>' . PHP_EOL;
 

	
 
		    $error_page->foot();
 
		    exit;
 
		  }
 

	
 
		$allClasses->findPossibilities();
 
		if (!isset($_SESSION['saved']))
 
		  $_SESSION['saved'] = array();
 
		$schedule_id = schedule_store_store($schedule_store, $allClasses);
 
		if ($schedule_id != NULL)
 
		  $_SESSION['saved'][$schedule_id] = $allClasses->getName();
 

	
 
		$process_php_s = '';
 
		if (!$clean_urls)
 
		  $process_php_s = 'process.php?s=';
 
		page::redirect($process_php_s . $schedule_id);
 
		exit;
 
      }
 
  }
 
else
 
  {
 
	echo '<pre>DEBUG OUTPUT: <br /><br />';
 
	foreach($_POST['postData'] as $class) {
 
		echo 'Class: ' . $class['name'] . '<br />';
 
		foreach($class as $section)
 
			if(is_array($section))
 
			{
 
				echo '---- Section that starts at ' . prettyTime($section['start']) . ' and ends at ' . prettyTime($section['end']) . '. This class meets on ';
 
				echo arrayToDays($section['days'],'long',true) . '.<br />';
0 comments (0 inline, 0 general)