# HG changeset patch # User Nathan Phillip Brink # Date 2010-10-02 01:49:41 # Node ID 6b24e9820611856add9f70939ddde64f1b6baf01 # Parent 264ef5810d61c33332cdb83c594adfae0e39f0f4 Support pastebin-style referencing of one's saved schedules: each schedule is now assigned a global identification number and is accessible using that number. This commit requires y'all to delete your old session cookie because the storage format of the session cookie has changed. diff --git a/.hgignore b/.hgignore new file mode 100644 --- /dev/null +++ b/.hgignore @@ -0,0 +1,7 @@ +style: regex + +# ignore all saved schedules, but keep track of the .keep file. +^saved_schedules/[^.] + +# ignore common unwanted suffixes +(~|\.orig|\.rej)$ diff --git a/class.schedule.php b/class.schedule.php --- a/class.schedule.php +++ b/class.schedule.php @@ -22,6 +22,12 @@ class Schedule private $storage; // Integer array of valid schedules private $title; /** + * \brief + * My global identification number. Not defined until the schedule + * is processed and first saved. + */ + private $id; + /** * The input format of the sections. Only used for the UI. Valid * values are 'numerous' for custom, 'numbered' for numeric, and 'lettered' for * alphabetical. @@ -300,11 +306,11 @@ class Schedule echo '});'; /* Close document.ready */ echo 'window.print(); '; - echo '

Select Schedules to Print :: Return to normal view :: Home

'; - echo '


'; + echo '

Select Schedules to Print :: Return to normal view :: Home

'; + echo '


'; } else { - echo '

Print :: Home

'; + echo '

Print :: Home

'; } if($this->nPermutations > 0) @@ -575,18 +581,7 @@ class Schedule } /* edit button */ - if (!isset($savedkey)) - { - if (isset($_REQUEST['savedkey'])) - $savedkey = (int)$_REQUEST['savedkey']; - else - /* - * if this is a new saved schedule, it'll be the - * next item added to $_SESSION['saved'] - */ - $savedkey = max(array_keys($_SESSION['saved'])) + 1; - } - echo '

'; + echo '

'; echo "

There were a total of " . $this->possiblePermutations . " possible permutations. Only " . $this->nPermutations . " permutations had no class conflicts.

"; @@ -649,4 +644,24 @@ class Schedule { return $this->classStorage[$class_key]; } + + /** + * \brief + * Set my global ID. + * + * Only to be called by schedule_store_store(). + */ + function id_set($id) + { + $this->id = $id; + } + + /* + * \brief + * Get my global ID. + */ + function id_get() + { + return $this->id; + } } diff --git a/inc/class.page.php b/inc/class.page.php --- a/inc/class.page.php +++ b/inc/class.page.php @@ -1,8 +1,8 @@ '; - if(isset($session['saved']) && count($session['saved']) > 0){ - echo '

Saved Schedules:

'; - foreach($session['saved'] as $key => $schedule){ - $sch = unserialize($schedule); - echo "#" . ($key + 1) . " - " . $sch->getName() - . " - view" .' edit ' - . "delete" - . "

\n"; - } - echo '
'; - } - echo '

'; + public function showSavedScheds($session) + { + echo '

'; + if (isset($session['saved']) && count($session['saved']) > 0) + { + echo '

Saved Schedules:

'; + foreach($session['saved'] as $key => $name) + { + echo '#' . $key . ":\n " + . htmlentities($name) + . ' edit' + . ' delete' + . "

\n"; + } + echo '
'; + } + echo '

'; + } + + /** + * \brief + * Display a 404 page and halt the PHP interpreter. + * + * This function does not return. It handles the creation of a Page + * class with 404-ish stuff and then calls exit() after flushing the + * page out to the user. + * + * \param $message + * A message consisting of valid XHTML to display to the user in + * the 404 page. + */ + public static function show_404($message = 'I couldn\'t find what you were looking for :-/.') + { + $page_404 = new Page('404: Content Not Found'); + + echo "

404: Content Not Found

\n" + . "

\n" + . ' ' . $message . "\n" + . "

\n"; + + $page_404->foot(); + + exit(); + } } - -} diff --git a/inc/schedule_store.inc b/inc/schedule_store.inc new file mode 100644 --- /dev/null +++ b/inc/schedule_store.inc @@ -0,0 +1,157 @@ + + * + * This file is a part of slate_permutate. + * + * slate_permutate 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. + * + * slate_permutate 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 slate_permutate. If not, see . + */ + +require_once('class.schedule.php'); + +/** + * \brief + * Initialize a schedule_store. + * + * \param $dir + * Directory to use as the schedule storage. + * \return + * A schedule_store handle or NULL on failure. + */ +function schedule_store_init($dir = 'saved_schedules') +{ + $schedule_store = array(); + + if (!is_dir($dir) || !is_writable($dir)) + { + error_log('I can\'t write to ' . $dir . ' or it is not a directory!'); + return NULL; + } + + $schedule_store['dir'] = realpath($dir); + + return $schedule_store; +} + +/** + * \brief + * Store a saved schedule into the schedule storage. + * + * \param $schedule_store + * The schedule_store handle from schedule_store_init(). + * \param $schedule + * The schedule object of type Schedule. + * \return + * The newly-saved schedules global ID (numeric) or NULL on error. + */ +function schedule_store_store($schedule_store, $schedule) +{ + $tempfile_name = tempnam($schedule_store['dir'], 'sch'); + + $new_schedule_id_name = tempnam($schedule_store['dir'], 'id'); + $new_schedule_id_file = fopen($new_schedule_id_name, 'wb'); + + _schedule_store_flock_grab($schedule_store, LOCK_EX); + /* if the file doesn't exist, we'll end up with a value of 1 for our first entry. */ + $schedule_id = (int)file_get_contents($schedule_store['dir'] . DIRECTORY_SEPARATOR . 'lastid'); + $new_schedule_id = $schedule_id + 1; + fwrite($new_schedule_id_file, $new_schedule_id); + fclose($new_schedule_id_file); + rename($new_schedule_id_name, $schedule_store['dir'] . DIRECTORY_SEPARATOR . 'lastid'); + _schedule_store_flock_release($schedule_store); + + /* we need to serialize the schedule _after_ giving it an ID */ + $schedule->id_set($new_schedule_id); + file_put_contents($tempfile_name, serialize($schedule)); + + rename($tempfile_name, $schedule_store['dir'] . DIRECTORY_SEPARATOR . $new_schedule_id); + + return $new_schedule_id; +} + +/** + * \brief + * Retrieve a stored saved schedule from the schedule storage. + * + * \param $schedule_store + * The schedule_store handle from which to retrieve the saved + * schedule. + * \param $schedule_id + * The saved schedule's globally-accessible ID. This value must have + * been returned from schedule_store_store() at one point. + * \return + * A Schedule object whose ID was $schedule_id or NULL if + * $schedule_id is an invalid or not-yet-allocated schedule + * identifier. + */ +function schedule_store_retrieve($schedule_store, $schedule_id) +{ + if (strcmp($schedule_id, (int)$schedule_id)) + return NULL; + $schedule_id = (int)$schedule_id; + + $schedule_serialized = file_get_contents($schedule_store['dir'] . DIRECTORY_SEPARATOR . $schedule_id); + if ($schedule_serialized === FALSE) + return NULL; + + $schedule = unserialize($schedule_serialized); + if ($schedule === FALSE) + return NULL; + return $schedule; +} + +/** + * \brief + * Delete a saved schedule. + * + * \param $schedule_store + * The store from which to delete the schedule. + * \param $schedule_id + * The identifier of the schedule to delete. + */ +function schedule_store_delete($schedule_store, $schedule_id) +{ + $remove_filename = $schedule_store['dir'] . DIRECTORY_SEPARATOR . $schedule_id; + /* avoid an E_WARNING if the file doesn't exist */ + if (file_exists($remove_filename)) + remove($remove_filename); +} + +/** + * \brief + * Obtains a lock on the /lastid file in the schedule_store. + * + * \see _schedule_store_flock_release(). + * + * \param $schedule_store + * The schedule_store instance we're working with. + * \param $operation + * Which flock() operation to perform: valid are LOCK_SH and LOCK_EX. + */ +function _schedule_store_flock_grab(&$schedule_store, $operation) +{ + $schedule_store['lastid_flock_file'] = fopen($schedule_store['dir'] . DIRECTORY_SEPARATOR . 'lastid.flock', 'c'); + return flock($schedule_store['lastid_flock_file'], $operation); +} + +/** + * \brief + * Release a lock grabbed with _schedule_store_flock_grab(). + */ +function _schedule_store_flock_release(&$schedule_store) +{ + flock($schedule_store['lastid_flock_file'], LOCK_UN); + fclose($schedule_store['lastid_flock_file']); + unset($schedule_store['lastid_flock_file']); +} diff --git a/input.php b/input.php --- a/input.php +++ b/input.php @@ -4,18 +4,18 @@ include_once 'class.schedule.php'; include_once 'class.class.php'; include_once 'class.section.php'; include_once 'inc/class.page.php'; +require_once('inc/schedule_store.inc'); $scripts = array('jQuery', 'jQueryUI', 'jValidate','schedInput'); $inputPage = new page('Scheduler', $scripts, FALSE); +$schedule_store = FALSE; $sch = FALSE; -if (isset($_REQUEST['savedkey']) && isset($_SESSION['saved'])) +if (isset($_REQUEST['s'])) { - $savedkey = (int)$_REQUEST['savedkey']; - if (isset($_SESSION['saved'][$savedkey])) - { - $sch = unserialize($_SESSION['saved'][$savedkey]); - } + $schedule_store = schedule_store_init(); + $schedule_id = (int)$_REQUEST['s']; + $sch = schedule_store_retrieve($schedule_store, $schedule_id); } if ($sch) diff --git a/process.php b/process.php --- a/process.php +++ b/process.php @@ -2,6 +2,8 @@ session_start(); +require_once('inc/schedule_store.inc'); +require_once('inc/class.page.php'); include_once 'class.schedule.php'; include_once 'class.class.php'; include_once 'class.section.php'; @@ -55,23 +57,36 @@ function prettyTime($time){ return substr($time,0,strlen($time)-2) . ":" . substr($time,strlen($time)-2, strlen($time)); } -$DEBUG = false; -if(isset($_GET['debug'])) - $DEBUG = $_GET['debug']; +$DEBUG = FALSE; +if (isset($_GET['debug'])) + $DEBUG = $_GET['debug']; -if(!$DEBUG){ +$schedule_store = schedule_store_init(); - if(isset($_GET['savedkey'])){ - $savedSched = unserialize($_SESSION['saved'][$_GET['savedkey']]); - $savedSched->writeoutTables(); - } - else if(isset($_GET['delsaved'])){ - $_SESSION['saved'][$_GET['delsaved']] = ''; - $_SESSION['saved'] = array_filter($_SESSION['saved']); // Remove null entries - header( 'Location: input.php' ) ; +if(!$DEBUG) + { + if(isset($_GET['s'])) + { + $savedSched = schedule_store_retrieve($schedule_store, $_GET['s']); + if ($savedSched) + $savedSched->writeoutTables(); + else + Page::show_404('Unable to find a saved schedule with an ID of ' . $_GET['s'] . '.'); + } + elseif(isset($_GET['del'])) + { + /* Allow the user to delete schedules that he has stored in his session */ + if ($_SESSION['saved'][(int)$_GET['del']]) + { + /* user owns this schedule ID */ + schedule_store_delete($schedule_store, (int)$_GET['del']); + unset($_SESSION['saved'][(int)$_GET['del']]); + } - } - else{ + header('Location: input.php'); + } + else + { $allClasses = new Schedule($_POST['postData']['name']); foreach(sortInputs($_POST) as $class) @@ -95,14 +110,22 @@ if(!$DEBUG){ } } $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(); + + /* + * writeoutTables() needs to know $schedule_id, so it + * has to be called after we save the schedule. See + * schedule_store_store(). + */ $allClasses->writeoutTables(); - if(!isset($_SESSION['saved'])) - $_SESSION['saved'] = array(); - array_push ( $_SESSION['saved'], serialize($allClasses)); - } -} else { - - + } + } +else + { echo '
DEBUG OUTPUT: 

'; foreach(sortInputs($_POST) as $class) { echo 'Class: ' . $class['name'] . '
'; diff --git a/saved_schedules/.keep b/saved_schedules/.keep new file mode 100644