Changeset - bc7ff69ca589
[Not reviewed]
default
0 4 2
Nathan Brink (binki) - 13 years ago 2012-04-28 18:00:12
ohnobinki@ohnopublishing.net
Add support for automatically registering for courses when using older WebAdvisor 2.x installations, particularly at Calvin College.
6 files changed with 335 insertions and 7 deletions:
0 comments (0 inline, 0 general)
ajax.php
Show inline comments
 
@@ -94,10 +94,10 @@ if (isset($_REQUEST['school_registration
 
	  }
 
      }
 

	
 
    $html = school_registration_html($page, $school, $courses);
 
    if (empty($html))
 
      slate_permutate_json_error('School\'s registration information producer returned no data.');
 
    slate_permutate_json_success(array('html' => $html));
 
    slate_permutate_json_success(is_array($html) ? $html : array('html' => $html));
 
  }
 

	
 
slate_permutate_json_error('Unrecognized command.');
school.d/calvin.inc
Show inline comments
 
@@ -29,12 +29,13 @@ function calvin_info()
 
	       'domains' => array(
 
				  'calvin.edu',
 
				  ),
 
	       'example_course_id' => 'CS-232',
 
	       'registration_url' => 'https://portal.calvin.edu/Pages/WebAdvisor.aspx?title=Express+Registration&PID=ST-WERG',
 
	       'student_address' => 'Knight',
 
	       'webadvisor_url' => 'https://resources.calvin.edu/selfservice/WebAdvisor',
 
	       );
 
}
 

	
 
function calvin_instructions_html()
 
{
 
  return <<<EOF
school.d/default.inc
Show inline comments
 
@@ -71,20 +71,35 @@ function default_registration_html(Page 
 
  else
 
    {
 
      $link_url = $school['url'];
 
      $link_text = $school['name'] . '\'s website';
 
    }
 

	
 
  $synonyms = array();
 
  foreach ($courses as $course)
 
    foreach ($course as $course_slot)
 
      foreach ($course_slot as $section)
 
        $synonyms[] = $section->getSynonym();
 

	
 
  $html = ''
 
    . '  <p>' . PHP_EOL
 
    . '    Enter these codes into ' . htmlentities($school['name']) . '\'s online course registration' . PHP_EOL
 
    . '    system (<a href="' . htmlentities($link_url, ENT_QUOTES) . '" target="_blank">' . htmlentities($link_text) . '</a>)' . PHP_EOL
 
    . '    to register for classes:' . PHP_EOL
 
    . '  </p>' . PHP_EOL
 
    . '  <ul class="synonym-list">' . PHP_EOL;
 
  foreach ($courses as $course)
 
    foreach ($course as $course_slot)
 
      foreach ($course_slot as $section)
 
        $html .= '    <li>' . htmlentities($section->getSynonym()) . '</li>' . PHP_EOL;
 
  foreach ($synonyms as $synonym)
 
    $html .= '    <li>' . htmlentities($synonym) . '</li>' . PHP_EOL;
 
  $html .= '  </ul>' . PHP_EOL;
 
  return $html;
 
  $ret = array('html' => $html);
 

	
 
  if (!empty($school['webadvisor_url']))
 
    {
 
      $webadvisor_register_url = 'webadvisor.php?school=' . $school['id'] . '&sections=' . implode(',', $synonyms);
 
      $ret['html'] = ''
 
	. '  <p><a href="' . htmlentities($webadvisor_register_url, ENT_QUOTES) . '">Automatically register</a></p>' . PHP_EOL
 
	. $ret['html'];
 
      $ret['location'] = page::uri_resolve($webadvisor_register_url);
 
    }
 

	
 
  return $ret;
 
}
scripts/displayTables.js
Show inline comments
 
@@ -121,13 +121,18 @@ jQuery(document).ready( function()
 
	var tab_fragment_i = /-([^-]+)$/.exec(jQuery('#the-tabs li:eq(' + tab_i + ') a').attr('href'))[1];
 
        var tab_course_data_json_selector = '#tabs-' + tab_fragment_i + ' .course-data';
 
	
 
        var tab_course_data_json = jQuery(tab_course_data_json_selector).text();
 
        var tab_course_data = eval('(' + tab_course_data_json + ')');
 

	
 
	slate_permutate_load(jQuery('#regDialog-content'), {school_registration_html: true, courses: tab_course_data});
 
	  slate_permutate_load(jQuery('#regDialog-content'), {school_registration_html: true, courses: tab_course_data},
 
			       function(target, data) {
 
				   target.html(data.html);
 
				   if (data.location)
 
				       document.location.href = data.location;
 
			       });
 

	
 
        jQuery("#regDialog").dialog('open');
 

	
 
	
 
	
 
	return false;
scripts/webadvisor_tokenidx.js
Show inline comments
 
new file 100644
 
/*
 
 * Assumes that WebAdvisor_scripts.js for WebAdvisor-2.x is loaded,
 
 * displayFormHTML() or something was called and thus
 
 * readURLParameters() was called. We attempt to extract TOKENIDX and
 
 * asynchronously inform slate_permutate about it. We currently assume
 
 * we're on a login form too.
 
 */
 

	
 
var slate_permutate_input_login;
 

	
 
(function() {
 
		var slate_permutate_onload = function() {
 

	
 
				/*
 
				 * Override the login form's submission handler to catch the
 
				 * case where we're still trying to load the TOKENIDX or
 
				 * something else.
 
				 */
 
				var inputs = document.getElementsByTagName('input');
 
				for (var i = 0; i < inputs.length; i ++)
 
				{
 
						slate_permutate_input_login = inputs.item(i);
 
						if (slate_permutate_input_login.getAttribute('name') == 'SUBMIT2')
 
								break;
 
				}
 
				slate_permutate_input_login.setAttribute('value', 'Discovering TOKENIDX...');
 
				slate_permutate_input_login.setAttribute('disabled', 'disabled');
 

	
 
				/*
 
				 * Discover the TOKENIDX if it's available.
 
				 */
 
				if (containsParameter(g_tokenIdx))
 
				{
 
						var TOKENIDX = getURLParameter(g_tokenIdx);
 
						var myscript = document.createElement('script');
 
						myscript.setAttribute('type', 'text/javascript');
 
						myscript.setAttribute('src', decodeURIComponent(getURLParameter('SP_CALLBACK')) + 'callback=slate_permutate_token_callback&TOKENIDX=' + TOKENIDX);
 
						document.getElementsByTagName('head').item(0).appendChild(myscript);
 
				}
 
				else
 
				{
 
						alert('Unable to discover TOKENIDX. You must register manually.');
 
				}
 
		}
 

	
 
		/*
 
		 * Register to run after either of getWindowHTML(),
 
		 * setWindowHTML(), or displayFormHTML() have been run. These are
 
		 * run after onload="", so they are required if we're to wait for
 
		 * the DOM to load...
 
		 */
 
		var funcs = ['getWindowHTML', 'setWindowHTML', 'displayFormHTML'];
 
		for (var i = 0; i < funcs.length; i ++)
 
		{
 
				var func = window[funcs[i]];
 
				window[funcs[i]] = function() {
 
						func();
 
						slate_permutate_onload();
 
				};
 
		}
 
})();
 

	
 
function slate_permutate_token_callback(result)
 
{
 
		if (result)
 
		{
 
				slate_permutate_input_login.setAttribute('value', 'LOG IN');
 
				slate_permutate_input_login.removeAttribute('disabled');
 
		}
 
}
webadvisor.php
Show inline comments
 
new file 100644
 
<?php /* -*- mode: php; -*- */
 
/*
 
 * Copyright 2012 Nathan Phillip Brink
 
 *
 
 * This file is part of SlatePermutate.
 
 *
 
 * SlatePermutate is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * 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/>.
 
 */
 

	
 
require_once('inc/class.page.php');
 

	
 
/*
 
 * Handle the scripts/webadvisor_tokenidx.js making a TOKENIDX
 
 * callback, storing that TOKENIDX in our SESSION for later use.
 
 */
 
if (!empty($_GET['TOKENIDX']))
 
  {
 
    page::session_start();
 

	
 
    $_SESSION['webadvisor_TOKENIDX'] = $_GET['TOKENIDX'];
 

	
 
    $result = 'received ' . $_GET['TOKENIDX'];
 

	
 
    header('Content-Type: text/javascript; charset=utf-8');
 

	
 
    if ($jsonp = !empty($_GET['callback']))
 
      echo $_GET['callback'] . '(';
 
    echo json_encode($result);
 
    if ($jsonp)
 
      echo ");\n";
 
    exit;
 
  }
 

	
 
$page = page::page_create('WebAdvisor');
 
$school = $page->get_school();
 

	
 
if (empty($school['webadvisor_url']))
 
  {
 
    if (!empty($school['registration_url']) && preg_match(',(.*/WebAdvisor),', $school['registration_url'], $matches))
 
      $school['webadvisor_url'] = $matches[1];
 
    else
 
      $school['webadvisor_url'] = $school['url'] . 'WebAdvisor';
 
  }
 

	
 
/**
 
 * \brief
 
 *   Calculate the URI necessary for logging into WebAdvisor.
 
 *
 
 * \param $school
 
 *   The school.
 
 * \param $dest
 
 *   The URI to visit after the user has logged into WebAdvisor and
 
 *   the TOKENIDX has been communicated to $tokenidx_callback.
 
 * \param $tokenidx_callback
 
 *   A JSONP-compatible callback which must be passed the TOKENIDX
 
 *   parameter the WebAdvisor is using. Treat as if is terminated with
 
 *   a `?' -- i.e., just append the querystring without the `?' to
 
 *   this URI when constructing the callback. To use, for example, in
 
 *   JavaScript you may create a DOMElement 'script' with attributes
 
 *   type="text/javascript" and
 
 *   src="$tokenidx_callback?callback=jsonp_callback&TOKENIDX=<detected
 
 *   TOKENIDX>". When jsonp_callback gets called, your script knows
 
 *   that $dest may be returned to. Don't forget to allow the user to
 
 *   log in first. This is normally done by setting SP_CALLBACK GET
 
 *   variable to this value inserting the
 
 *   scripts/webadvisor_tokenidx.js script into the WebAdvisor login
 
 *   page using cross-site-scripting HTML injection such as through
 
 *   the ERROR GET parameter.
 
 * \return
 
 *   Just ensure that $tokenidx_callback gets called; do not return
 
 *   except by redirecting to $dest.
 
 */
 
function webadvisor_login($page, array $school, $dest, $tokenidx_callback)
 
{
 
  if (strpos($dest, '?') !== FALSE)
 
    $dest .= '&';
 
  else
 
    $dest .= '?';
 
  $dest .= 'from_webadvisor';
 

	
 
  $webadvisor_login_func = $school['id'] . '_webadvisor_login';
 
  if (function_exists($webadvisor_login_func))
 
    $webadvisor_login_func($school, $dest);
 

	
 
  /*
 
   * The hack we are using is that somehow TOKENIDX=&SS=LGRQ&URL=<URI>
 
   * will both initialize the user's browser with a token cookie and
 
   * then redirect to URL. Trying to use the proper way of loading the
 
   * LGRQ (using TYPE=P&PID=UT-LGRQ&PROCESS=-XUTAUTH01) doesn't work
 
   * because it drops and ignores our URL parameter, leaving the user
 
   * at the KV site. No other URL I've fiddled with seems to be able
 
   * to do this combination of logging in and returning the user to us
 
   * or a URI of our choosing. Once the user's browser has been
 
   * initialized with a TOKENIDX, loading the page
 
   * SS=LGRQ&URL=<URI>&ERROR=<XSS> will preserve the ERROR=<XSS>
 
   * necessary for our XSS and insert it into the login page.
 
   *
 
   * HOWEVER, if the browser already has a TOKENIDX-related cookie,
 
   * then visiting TOKENIDX=&SS=LGRQ&URL=<URL> will cause WebAdvisor
 
   * to keep redirecting to itself infinitely. Similarly, if the
 
   * browser does not yet have a TOKENIDX-related cookie,
 
   * SS=LGRQ&URL=<URL> will redirect the user to URL without giving
 
   * the user a cookie. Thus, our strategy is:
 
   *
 
   * 1. Send the user to
 
   *    SS=LGRQ&URL=<URL>&SP_CALLBACK=<SP_CALLBACK>&ERROR=<XSS>. In
 
   *    this case, the URL will be set to have `from_webadvisor' as a
 
   *    GET parameter and ERROR will be set to the appropriate XSS for
 
   *    the normal login form. Thus, if the user does not have a
 
   *    token, he will be directed here and sent to step #2 to get a
 
   *    token. Otherwise, the user will have a jump start (already
 
   *    having TOKENIDX cookies) and communicate his token to us while
 
   *    logging in.
 
   *
 
   * 2. If webadvisor.php is called with from_webadvisor, that means
 
   *    one of two things. It might mean that webadvisor_tokenidx.js
 
   *    was called successfully and we have the webadvisor TOKENIDX
 
   *    stored in our session. In that case, the user's browser
 
   *    already had a WebAdvisor TOKENIDX before we did #1; also, this
 
   *    function won't be called in that case because this function is
 
   *    only called if TOKENIDX is unknown. Thus, we don't know the
 
   *    TOKENIDX, meaning that we need to request that the WebAdvisor
 
   *    installation allocate a TOKENIDX for the user and _then_
 
   *    proceed directly to the login page to send us TOKENIDX.
 
   */
 

	
 
  $login_form_uri = $school['webadvisor_url'] . '?SS=LGRQ&URL=' . rawurlencode($dest)
 
    . '&SP_CALLBACK=' . rawurlencode($tokenidx_callback)
 
    . '&ERROR=' . rawurlencode('<script type="text/javascript" src="' . htmlentities(page::uri_resolve('scripts/webadvisor_tokenidx.js'), ENT_QUOTES) . '"></script>');
 

	
 
  if (isset($_GET['from_webadvisor']))
 
    /*
 
     * Case 2, infer that browser needs TOKENIDX cookies _and_ that
 
     * the following URI won't cause endless looping
 
     * (hopefully). Unfortunately, this process is not reentrant.
 
     */
 
    redir($school['webadvisor_url'] . '?TOKENIDX=&SS=LGRQ&URL=' . rawurlencode($login_form_uri));
 

	
 
  /*
 
   * Case 1, assume that the user has a TOKENIDX cookie _but_ make
 
   * provisions ($dest has from_webadvisor in it) for needing to
 
   * allocate that cookie.
 
   */
 
  redir($login_form_uri);
 

	
 
  return array(
 
    /* 'preload' => $school['webadvisor_url'] . '?TYPE=P&PID=UT-LGRQ&PROCESS=-XUTAUTH01&URL=', */
 
    'uri' => $school['webadvisor_url'] . '?SS=LGRQ&URL=' . rawurlencode($login_form_uri),
 
  );
 
}
 

	
 
function redir($dest)
 
{
 
  header('HTTP/1.1 302 Found');
 
  header('Location: ' . $dest);
 
  header('Content-Type: text/plain; charset=utf-8');
 
  echo 'Location: ' . $dest;
 
  exit;
 
}
 

	
 
/*
 
 * If the page load was not a redirection from webadvisor, we must
 
 * clear our local cache of TOKENIDX's value. We need to get a new
 
 * token because we can't guess what SS= value the ST-WERG form will
 
 * take unless if we start with a new TOKENIDX which doesn't have any
 
 * SSes yet. Also, the old token may have (very likely) expired
 
 * because of the short login timeout.
 
 */
 
if (!isset($_GET['from_webadvisor']))
 
  unset($_SESSION['webadvisor_TOKENIDX']);
 

	
 
if (empty($_SESSION['webadvisor_TOKENIDX']))
 
  {
 
    /*
 
     * Get a token for the ST-WERG form and have the user perform the
 
     * WebAdmin-specific login. This can only be done after the login form
 
     * has an SS allocated for it.
 
     */
 
    webadvisor_login($page, $school, page::uri_resolve('webadvisor.php') . '?r=' . rand()
 
		     . '&sections=' . rawurlencode(empty($_GET['sections']) ? '' : $_GET['sections'])
 
		     . '&school=' . rawurlencode($school['id']),
 
		     page::uri_resolve('webadvisor.php?'));
 
  }
 

	
 
/*
 
 * Use the hopefully-still-valid TOKENIDX to initialize an ST-WERG
 
 * (STudent Web[A]dvisor Express ReGistration) form. When that form is
 
 * iniailized, assume that it has SS=1 and submit the form. &APP=ST
 
 */
 
$TOKENIDX = $_SESSION['webadvisor_TOKENIDX'];
 
$page->head();
 
echo '<form id="sp-webadvisor-form" action="' . htmlentities($school['webadvisor_url'] . '?TOKENIDX=' . $TOKENIDX . '&SS=1', ENT_QUOTES) . '" method="post">' . PHP_EOL;
 
echo '<p>';
 

	
 
$uri = $school['webadvisor_url'] . '?TOKENIDX=' . $TOKENIDX . '&TYPE=P&PID=ST-WERG';
 
$onload_html = '="' . htmlentities('javascript:document.getElementById(\'sp-webadvisor-form\').submit()', ENT_QUOTES) . '"';
 
echo '  <img src="' . htmlentities($uri, ENT_QUOTES) . '" alt="Loading WebAdvisor Express Registration form (ST-WERG)…"'
 
. ' onload' . $onload_html . ' onerror' . $onload_html . ' />' . PHP_EOL;
 
echo '  If you are not redirected after 16 seconds, you may try: ' . PHP_EOL;
 

	
 
$sections = explode(',', empty($_GET['sections']) ? '' : $_GET['sections']);
 
echo '  <input type="hidden" name="LIST.VAR1_CONTROLLER" value="LIST.VAR1" />' . PHP_EOL;
 
echo '  <input type="hidden" name="LIST.VAR1_MEMBERS" value="LIST.VAR1*LIST.VAR2*LIST.VAR3*LIST.VAR4*LIST.VAR5" />' . PHP_EOL;
 
for ($i = 1; $i <= 5; $i ++)
 
  echo //'  <input type="hidden" name="LIST.VAR' . $i . '_MAX" value="' . count($sections) . '" />' . PHP_EOL;
 
    '  <input type="hidden" name="LIST.VAR' . $i . '_MAX" value="10" />' . PHP_EOL;
 
$course_num = 1;
 
foreach ($sections as $course)
 
  {
 
    echo '  <input type="hidden" name="LIST.VAR1_' . $course_num . '" value="' . htmlentities($course, ENT_QUOTES) . '" />' . PHP_EOL;
 
    for ($i = 2; $i <= 5; $i ++)
 
      echo '  <input type="hidden" name="LIST.VAR' . $i . '_' . $course_num . '" value="" />' . PHP_EOL;
 
    $course_num ++;
 
  }
 
while ($course_num < 10)
 
  {
 
    for ($i = 1; $i <= 5; $i ++)
 
      echo '  <input type="hidden" name="LIST.VAR' . $i . '_' . $course_num . '" value="" />' . PHP_EOL;
 
    $course_num ++;
 
  }      
 
echo '  <input type="hidden" name="SUBMIT_OPTIONS" value="" title="Please do not click here unless if you are not redirected within 16 or more seconds." />' . PHP_EOL;
 
echo '  <input type="submit" name="SUBMIT2" value="SUBMIT" />' . PHP_EOL;
 
echo '</p>';
 
echo '</form>';
 

	
 
$page->foot();
0 comments (0 inline, 0 general)