diff --git a/auto.php b/auto.php --- a/auto.php +++ b/auto.php @@ -59,11 +59,16 @@ if (!count($term_parts)) { clean_empty_exit(); } -$school = school_load_guess(); +/* + * We let the *_load_guess() functions check $_REQUEST['school'] and + * $_REQUEST['semester'] for us, asking them not to update the + * session. + */ +$school = school_load_guess(FALSE); if (!$school['crawled']) { clean_empty_exit(); } -$semester = school_semester_guess($school); +$semester = school_semester_guess($school, FALSE); $cache_dir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'auto' . DIRECTORY_SEPARATOR . $school['id'] . DIRECTORY_SEPARATOR . $semester['id'] . DIRECTORY_SEPARATOR; diff --git a/inc/class.page.php b/inc/class.page.php --- a/inc/class.page.php +++ b/inc/class.page.php @@ -84,14 +84,25 @@ class page /* the current school. See get_school(). */ private $school; + private $semester; + + /* + * Whether or not the user should be presented with the option to + * change the school profile or semester. + */ + private $school_semester_constant; /** * \param $ntitle * Must be a valid HTML string (i.e., escaped with htmlentities()). * \param $nscripts * An array of strings identifying the scripts to include for this page. + * \param $options + * An array containing any of the following keys: + * - 'school': The school to use instead of the autodetected one. + * - 'semester': The semester to use instead of the autodetected one. */ - public function __construct($ntitle, $nscripts = array(), $immediate = TRUE) + public function __construct($ntitle, $nscripts = array(), $immediate = TRUE, array $options = array()) { /* Begin tracking generation time */ $this->pageGenTime = round(microtime(),4); @@ -149,8 +160,17 @@ class page self::session_start(); /* everything that needs sessions started to work: */ - $this->school = school_load_guess(); - $this->semester = school_semester_guess($this->school); + if (empty($options['school'])) + $options['school'] = school_load_guess(); + $this->school = $options['school']; + + if (empty($options['semester'])) + $options['semester'] = school_semester_guess($this->school); + $this->semester = $options['semester']; + + if (!isset($options['school_semester_constant'])) + $options['school_semester_constant'] = TRUE; + $this->school_semester_constant = (bool)$options['school_semester_constant']; if($immediate && $ntitle != "NOHEAD") @@ -172,10 +192,17 @@ class page * A list of scripts which the page desires to be included in the * of the page. Should this param just be moved to the * page::head() function? + * \param $options + * An array containing any of the following keys: + * - 'school': The school to use instead of the autodetected one. + * - 'semester': The semester to use instead of the autodetected one. + * - 'school_semester_constant': Whether the options to change + * the current school and semester should be hidden. TRUE by + * default. */ - public static function page_create($title, array $scripts = array()) + public static function page_create($title, array $scripts = array(), array $options = array()) { - return new page(htmlentities($title), $scripts, FALSE); + return new page(htmlentities($title), $scripts, FALSE, $options); } /** @@ -222,7 +249,10 @@ class page ' '. PHP_EOL . ' ' . PHP_EOL; + . ' ' . PHP_EOL + . $this->script_wrap('' + . 'var slate_permutate_school = ' . json_encode($this->school['id']) . ';' . PHP_EOL + . 'var slate_permutate_semester = ' . json_encode($this->semester['id']) . ';' . PHP_EOL); // Write out all passed scripts foreach ($this->scripts as $i) echo ' ' . $this->headCode["$i"] . "\n"; @@ -236,9 +266,9 @@ class page '

'. PHP_EOL . ' '.$this->pagetitle.''. PHP_EOL . ' ' . PHP_EOL - . ' Profile: '.$this->school['name'].' (change)' . PHP_EOL; - if ($this->semester !== NULL) - echo ' Semester: ' . $this->semester['name'] . '(change)' . PHP_EOL; + . ' Profile: ' . $this->school['name'] . '' . ($this->school_semester_constant ? '' : ' (change)') . PHP_EOL; + if (!empty($this->semester)) + echo ' Semester: ' . $this->semester['name'] . '' . ($this->school_semester_constant ? '' : ' (change)') . PHP_EOL; echo ' '. PHP_EOL . '

'. PHP_EOL . ' '. PHP_EOL . @@ -526,6 +556,15 @@ class page /** * \brief + * Get the current semester. + */ + public function semester_get() + { + return $this->semester; + } + + /** + * \brief * Format a chunk of javascript suitable for adding to headcode. * * Takes into account whether or not the code should be wrapped in diff --git a/inc/class.schedule.php b/inc/class.schedule.php --- a/inc/class.schedule.php +++ b/inc/class.schedule.php @@ -30,6 +30,7 @@ include_once $incdir . 'class.course.inc'; include_once $incdir . 'class.section.php'; include_once $incdir . 'class.page.php'; +require_once $incdir . 'school.inc'; /* * Load a Classes -> Course converter class for the sake of the @@ -52,6 +53,24 @@ class Schedule private $possiblePermutations; // Integer number of possible permutations private $scheduleName; // String name of schedule private $storage; // Integer array of valid schedules + /** + * \brief + * The school_id of the school this schedule was created for. + */ + private $school_id; + /** + * \brief + * The semester this schedule was created for. + * + * The semester array is stored in full in a schedule because some + * schools do not keep a backlog of all semesters for their course + * data. Such a school is calvin. We want to be able to access, for + * example, the friendly name and so on of a semester even one year + * after the semester is created without worrying about having that + * semester be stored in the autocomplete cache. + */ + private $semester; + /* The of the page used when rendering this schedule */ private $title; @@ -79,8 +98,17 @@ class Schedule * derived. A schedule is considered to be derived of another of * the user created this schedule by clicking ``Edit'' for the * previous schedule. Or NULL if this schedule stands on its own. + * \param $school + * The school used for this schedule. The intention of storing + * this data is that people from different schools may share + * schedules with eachother. Also, people who bookmark their + * schedules and want to edit their schedules should not have to + * go through the school selection dialogue again but should just + * be set to use the correct school. + * \param $semester + * The semester used for this schedule. */ - function __construct($name, $parent = NULL) + function __construct($name, $parent = NULL, array $school = NULL, array $semester = NULL) { $this->courses = array(); $this->scheduleName = $name; @@ -88,6 +116,16 @@ class Schedule $this->title = "SlatePermutate - Scheduler"; $this->parent_id = $parent; + if (empty($school)) + $school = school_load_guess(); + $this->school_id = $school['id']; + + if (empty($semester)) + { + $semester = school_semester_guess($school); + } + $this->semester = $semester; + /* mark this as an upgraded Schedule class. See __wakeup() */ $this->nclasses = -1; } @@ -152,6 +190,45 @@ class Schedule echo 'Could not find class: ' . $course_name . "<br />\n"; } + /** + * \brief + * Get the school associated with this schedule. + * + * \return + * The school associated with this schedule or some fallback. + */ + public function school_get() + { + $school = NULL; + + if (!empty($this->school_id)) + /* + * May return NULL, so we don't just return this value right + * away -- we fall through. + */ + $school = school_load($this->school_id); + if (empty($school)) + { + /* Ensure we have $_SESSION. */ + page::session_start(); + $school = school_load_guess(); + } + + return $school; + } + + /** + * \brief + * Get the semester associated with this schedule. + * + * \return + * The schedule's associated semester. + */ + public function semester_get() + { + return $this->semester; + } + //-------------------------------------------------- // Finds all of the possible permutations and stores // the results in the storage array. @@ -293,7 +370,8 @@ class Schedule else { $headcode = array('outputStyle', 'jQuery', 'jQueryUI', 'jAddress', 'uiTabsKeyboard', 'qTip2','displayTables'); } - $outputPage = page::page_create(htmlentities($this->getName()), $headcode); + $outputPage = page::page_create(htmlentities($this->getName()), $headcode, + array('school' => $this->school_get(), 'semester' => $this->semester_get())); $outputPage->head(); @@ -739,5 +817,24 @@ class Schedule if (empty($this->parent_id)) $this->parent_id = NULL; + + if (empty($this->school_id)) + { + /* Ensure we have $_SESSION. */ + page::session_start(); + $school = school_load_guess(); + $this->school_id = $school['id']; + } + if (empty($this->semester)) + { + if (empty($school)) + { + /* Ensure we have $_SESSION. */ + page::session_start(); + + $school = school_load($this->school_id); + $this->semester = school_semester_guess($school); + } + } } } diff --git a/inc/school.inc b/inc/school.inc --- a/inc/school.inc +++ b/inc/school.inc @@ -112,18 +112,29 @@ function school_load($school_id, $load_a * use. Then it tries to make a best guess as to the school he's from * using the rDNS information provided by the httpd. * + * \param $update_session + * Whether or not the results should be stored into the session for + * later use. A value of disabled makes sense for the auto.php AJAX + * callback script, where the user is not loading a page himself but + * the current school is being specified in the URL + * parameters... thus updating the session value here would be a + * side-effect. We're doing this so that the user can do + * autocomplete for two different school/semester pairs in two + * different browser tabs under the same session. + * * \return * A school profile or NULL if the school isn't in the session and * can't be guessed. */ -function school_load_guess() +function school_load_guess($update_session = TRUE) { if (isset($_REQUEST['school'])) { $school = school_load($_REQUEST['school']); if ($school) { - $_SESSION['school'] = $school['id']; + if ($update_session) + $_SESSION['school'] = $school['id']; return $school; } } @@ -160,7 +171,8 @@ function school_load_guess() $school = school_load($domain_school); if ($school) { - $_SESSION['school'] = $domain_school; + if ($update_sesssion) + $_SESSION['school'] = $domain_school; return school_load($domain_school); } } @@ -172,10 +184,11 @@ function school_load_guess() * doesn't have to be done too often. (the isset() call above should * detect even the empty string). */ - $_SESSION['school'] = 'default'; + if ($update_session) + $_SESSION['school'] = 'default'; /* loading the school_id of 'default' MUST always work */ - return school_load($_SESSION['school']); + return school_load('default'); } /** @@ -331,12 +344,15 @@ function school_semesters(array $school) * * \param $school * The school for which a semester should be guessed. + * \param $update_session + * Whether or not $_SESSION should be updatd with the new value. A + * value of FALSE makes sense for the ajax.php callback script. * \return * An array with the keys 'id', 'name', and 'weight' corresponding * to the same keys in the arrays returned by school_semesters() or * NULL if no semester can be found. */ -function school_semester_guess(array $school) +function school_semester_guess(array $school, $update_session = FALSE) { $semesters = school_semesters($school); @@ -344,7 +360,8 @@ function school_semester_guess(array $sc && isset($semesters[$_REQUEST['semester']])) { $semester = $semesters[$_REQUEST['semester']]; - $_SESSION['semester'] = $semester['id']; + if ($update_session) + $_SESSION['semester'] = $semester['id']; return $semester; } diff --git a/input.php b/input.php --- a/input.php +++ b/input.php @@ -24,13 +24,10 @@ include_once 'inc' . DIRECTORY_SEPARATOR 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; $errors_fix = FALSE; -$school = $inputPage->get_school(); +$inputPage_options = array('school_semester_constant' => FALSE); $parent_schedule_id = NULL; if (isset($_REQUEST['s'])) @@ -38,6 +35,8 @@ if (isset($_REQUEST['s'])) $schedule_store = schedule_store_init(); $parent_schedule_id = (int)$_REQUEST['s']; $sch = schedule_store_retrieve($schedule_store, $parent_schedule_id); + $inputPage_options += array('school' => $sch->school_get(), + 'semester' => $sch->semester_get()); } elseif (!empty($_REQUEST['e'])) { @@ -51,6 +50,15 @@ elseif (!empty($_REQUEST['e'])) $parent_schedule_id = (int)$_POST['postData']['parent_schedule_id']; } +/* + * We cannot initialize the page object nor guess the school before + * figuring loading a saved schedule because we'll default to that + * saved_schedule's school/semester. + */ +$scripts = array('jQuery', 'jQueryUI', 'qTip2', 'schedInput'); +$inputPage = page::page_create('Scheduler', $scripts, $inputPage_options); +$school = $inputPage->get_school(); + $my_hc = 'var slate_permutate_example_course_id = ' . json_encode(school_example_course_id($inputPage->get_school())) . '; jQuery(document).ready( @@ -186,7 +194,7 @@ if (!empty($_REQUEST['selectsemester'])) class="defText required" type="text" size="25" - title="My <?php echo $inputPage->semester['name'] ?> Schedule" + title="My <?php $semester = $inputPage->semester_get(); echo $semester['name'] ?> Schedule" name="postData[name]" <?php if ($sch) diff --git a/scripts/scheduleInput.js b/scripts/scheduleInput.js --- a/scripts/scheduleInput.js +++ b/scripts/scheduleInput.js @@ -275,7 +275,7 @@ function add_class_n(course_id, title) var class_elem = jQuery('.className' + classNum); - class_elem.autocomplete({ source: 'auto.php' }); + class_elem.autocomplete({ source: 'auto.php?school=' + slate_permutate_school + '&semester=' + slate_permutate_semester }); class_elem.bind('autocompleteselect', {class_num: classNum, class_elem: class_elem}, function(event, ui) { @@ -287,7 +287,12 @@ function add_class_n(course_id, title) jQuery.ajax( { url: 'auto.php', - data: {'getsections': 1, 'term': ui.item.value}, + data: { + getsections: 1, + term: ui.item.value, + school: slate_permutate_school, + semester: slate_permutate_semester + }, context: {'class_num': event.data.class_num}, success: function(data, textStatus, reqobj) {