diff --git a/config.sample.php b/config.sample.php
index 4228dff6d570adf398c1fa2b51e69a9ee989e75b..d27660b88ef06b5df6ed9f1c5fe51ecdc8e558f2 100644
--- a/config.sample.php
+++ b/config.sample.php
@@ -6,58 +6,18 @@
  * @author bbieber
  */
 
-set_include_path(dirname(__FILE__).'/src'.PATH_SEPARATOR.dirname(__FILE__).'/lib/php');
-
-/**
- * Require DB_DataObject before initializing the connection details.
- */
-require_once 'DB/DataObject.php';
-
 function autoload($class)
 {
-    $file = str_replace('_', '/', $class).'.php';
-    $fp = @fopen($file, 'r', true);
-    if ($fp) {
-        fclose($fp);
-        require $file;
-        if (!class_exists($class, false) && !interface_exists($class, false)) {
-            die(new \Exception('Class ' . $class . ' was not present in ' .
-                $file . ' (include_path="' . get_include_path() . '")'));
-        }
-        return true;
-    }
-    return false;
+    $class = str_replace(array('_', '\\'), '/', $class);
+    include $class . '.php';
 }
 
-spl_autoload_register("autoload");
-
-$db_pass = 'catalog';
-$db_user = 'catalog';
-$db      = 'catalog';
-$db_host = 'localhost';
+spl_autoload_register('autoload');
 
-// Load database settings
-$options = &PEAR::getStaticProperty('DB_DataObject','options');
-$options = array(
-    'database'         => "mysqli://$db_user:$db_pass@$db_host/$db",
-    'schema_location'  => dirname(__FILE__).'/src/UNL/Catalog',
-    'class_location'   => dirname(__FILE__).'/src/UNL/Catalog',
-    'require_prefix'   => dirname(__FILE__).'/src/UNL/Catalog',
-    'class_prefix'     => 'UNL_Catalog_',
-    'db_driver'        => 'MDB2'
-);
-
-set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__));
-//DB_DataObject::debugLevel(1);
-
-define('UNL_CATALOG_URI',              'http://localhost/workspace/UNL_GraduateBulletin/www/');
-define('UNL_TEMPLATES_DEPENDENTSPATH', $_SERVER['DOCUMENT_ROOT']);
-define('UNL_UNDERGRADUATEBULLETIN_DIR', dirname(__DIR__) . '/UNL_UndergraduateBulletin');
-
-set_include_path(get_include_path()
-    . PATH_SEPARATOR . UNL_UNDERGRADUATEBULLETIN_DIR . '/includes/php'
-    . PATH_SEPARATOR . UNL_UNDERGRADUATEBULLETIN_DIR . '/src'
-        );
+set_include_path(__DIR__ . '/src' . PATH_SEPARATOR . __DIR__ . '/lib/php');
 
 ini_set('display_errors', true);
 error_reporting(E_ALL);
+
+
+UNL\Catalog\Controller::$url = '/workspace/UNL_GraduateBulletin/www/';
diff --git a/src/UNL/Catalog/Controller.php b/src/UNL/Catalog/Controller.php
new file mode 100644
index 0000000000000000000000000000000000000000..878ca4872ddb5adfd5cda60f472de2b715ff577f
--- /dev/null
+++ b/src/UNL/Catalog/Controller.php
@@ -0,0 +1,145 @@
+<?php
+namespace UNL\Catalog;
+
+class Controller
+{
+
+    public $output = null;
+
+    public static $url = '/workspace/UNL_Catalog/www/';
+
+    /**
+     * Options array
+     * Will include $_GET vars
+     */
+    public $options = array(
+        'model'  => false,
+        'format' => 'html',
+    );
+
+    public function __construct($options = array())
+    {
+        $this->options = $options + $this->options;
+
+        try {
+            $this->run();
+        } catch (\Exception $e) {
+            $this->output = $e;
+        }
+    }
+
+    /**
+     * Populate the actionable items according to the view map.
+     *
+     * @throws Exception if view is unregistered
+     */
+    public function run()
+    {
+         if (!isset($this->options['model'])
+             || false === $this->options['model']) {
+             throw new Exception('Un-registered view', 404);
+         }
+
+         if (is_callable($this->options['model'])) {
+             $this->output = call_user_func($this->options['model'], $this->options);
+         } else {
+             $this->output = new $this->options['model']($this->options);
+         }
+    }
+
+    public function getURL()
+    {
+        return self::$url;
+    }
+
+    /**
+     * Add a file extension to the existing URL
+     *
+     * @param string $url       The URL
+     * @param string $extension The file extension to add, e.g. csv
+     */
+    public static function addURLExtension($url, $extension)
+    {
+        $extension = trim($extension, '.');
+
+        return preg_replace('/^([^?]+)(\.[\w]+)?(\?.*)?$/', '$1.'.$extension.'$3', $url);
+    }
+
+    /**
+     * Add unique querystring parameters to a URL
+     *
+     * @param string $url               The URL
+     * @param array  $additional_params Additional querystring parameters to add
+     *
+     * @return string
+     */
+    public static function addURLParams($url, $additional_params = array())
+    {
+        $params = self::getURLParams($url);
+
+        $params = array_merge($params, $additional_params);
+
+        if (strpos($url, '?') !== false) {
+            $url = substr($url, 0, strpos($url, '?'));
+        }
+
+        $url .= '?';
+
+        foreach ($params as $option=>$value) {
+            if ($option == 'driver') {
+                continue;
+            }
+            if ($option == 'format'
+                && $value == 'html') {
+                continue;
+            }
+            if (isset($value)) {
+                if (is_array($value)) {
+                    foreach ($value as $arr_value) {
+                        $url .= "&{$option}[]=$arr_value";
+                    }
+                } else {
+                    $url .= "&$option=$value";
+                }
+            }
+        }
+        $url = str_replace('?&', '?', $url);
+
+        return trim($url, '?;=');
+    }
+
+    public static function getURLParams($url)
+    {
+        $params = array();
+        if (strpos($url, '?') !== false) {
+            list($url, $existing_params) = explode('?', $url);
+            $existing_params = explode('&', html_entity_decode($existing_params, ENT_QUOTES, 'UTF-8'));
+            foreach ($existing_params as $val) {
+                $split = explode('=', $val);
+                $params[$split[0]] = '';
+                if (isset($split[1])) {
+                    $params[$split[0]] = $split[1];
+                }
+            }
+        }
+
+        return $params;
+    }
+
+    public function handlePost()
+    {
+        $handler = new PostHandler($this->options, $_POST, $_FILES);
+
+        return $handler->handle();
+    }
+
+    public static function redirect($url, $exit = true)
+    {
+        header('Location: '.$url);
+        if (!defined('CLI')
+            && false !== $exit) {
+            exit($exit);
+        }
+    }
+
+}
diff --git a/src/UNL/Catalog/Homepage.php b/src/UNL/Catalog/Homepage.php
new file mode 100644
index 0000000000000000000000000000000000000000..d87bfff5983e8a77ed8b6c8d8212ff23d4f53784
--- /dev/null
+++ b/src/UNL/Catalog/Homepage.php
@@ -0,0 +1,6 @@
+<?php
+namespace UNL\Catalog;
+
+class Homepage
+{
+}
\ No newline at end of file
diff --git a/src/UNL/Catalog/OutputController.php b/src/UNL/Catalog/OutputController.php
new file mode 100644
index 0000000000000000000000000000000000000000..67b1f894616071c0ac92717fb7c4e496396101e3
--- /dev/null
+++ b/src/UNL/Catalog/OutputController.php
@@ -0,0 +1,95 @@
+<?php
+namespace UNL\Catalog;
+
+class OutputController extends \Savvy
+{
+    public function __construct($options = array())
+    {
+        parent::__construct();
+        $this->initialize($options);
+    }
+
+    public function initialize($options = array())
+    {
+
+        switch ($options['format']) {
+            case 'json':
+                header('Content-type:application/json');
+                $this->setTemplateFormatPaths($options['format']);
+                break;
+
+            case 'epub':
+                header('Content-type:application/epub+zip');
+                // Escape output
+                $this->setEscape(function($data) {
+                    return htmlspecialchars($data, ENT_QUOTES, 'UTF-8', false);
+                });
+                $this->setTemplateFormatPaths($options['format']);
+                break;
+
+            case 'pdf':
+                header('Content-type:application/pdf');
+                $this->setTemplateFormatPaths($options['format']);
+                break;
+
+            case 'csv':
+                if (!isset($this->options['delimiter'])) {
+                    $this->options['delimiter'] = ',';
+                }
+
+                $this->addGlobal('delimiter', $this->options['delimiter']);
+                $this->addGlobal('delimitArray', function($delimiter, $array){
+                    $out = fopen('php://output', 'w');
+                    fputcsv($out, $array, $delimiter);
+                });
+
+                $filename = str_replace("\\", "_", $options['model']) . "." . $options['format'];
+                header('Content-disposition: attachment; filename=' . $filename);
+
+            case 'txt':
+                header('Content-type:text/plain;charset=UTF-8');
+                $this->setTemplateFormatPaths($options['format']);
+                break;
+
+            case 'partial':
+                \Savvy_ClassToTemplateMapper::$output_template['Buros\Controller'] = 'Buros/Controller-partial';
+                // intentional no-break
+
+            case 'html':
+                // Always escape output, use $context->getRaw('var'); to get the raw data.
+                $this->setEscape(function($data) {
+                    return htmlspecialchars($data, ENT_QUOTES, 'UTF-8', false);
+                });
+                header('Content-type:text/html;charset=UTF-8');
+                $this->setTemplateFormatPaths('html');
+                $this->addFilters(array(new OutputController\PostRunFilter\HTML($options), 'postRun'));
+                break;
+            default:
+                throw new Exception('Invalid/unsupported output format', 500);
+        }
+    }
+
+    /**
+     * Set the array of template paths necessary for this format
+     *
+     * @param string $format Format to use
+     */
+    public function setTemplateFormatPaths($format)
+    {
+        $web_dir = dirname(dirname(dirname(__DIR__))) . '/www';
+
+        $this->setTemplatePath(
+            array(
+                $web_dir . '/templates/' . $format,
+            )
+        );
+    }
+
+    public function setReplacementData($field, $data)
+    {
+        foreach ($this->getConfig('filters') as $filter) {
+            $filter[0]->setReplacementData($field, $data);
+        }
+    }
+
+}
diff --git a/src/UNL/Catalog/OutputController/PostRunFilter/HTML.php b/src/UNL/Catalog/OutputController/PostRunFilter/HTML.php
new file mode 100644
index 0000000000000000000000000000000000000000..ec15b2388bb9809d96e255a6d4cfff48a64464ab
--- /dev/null
+++ b/src/UNL/Catalog/OutputController/PostRunFilter/HTML.php
@@ -0,0 +1,23 @@
+<?php
+namespace UNL\Catalog\OutputController\PostRunFilter;
+
+class HTML
+{
+    public static $data;
+
+    public static function setReplacementData($field, $data)
+    {
+        self::$data[$field] = $data;
+    }
+
+    public function postRun($data)
+    {
+        if (isset(self::$data['pagetitle'])) {
+            $data = str_replace('<title>UNL Catalog</title>',
+                                '<title>'.self::$data['pagetitle'].' | UNL Catalog</title>',
+            $data);
+        }
+
+        return $data;
+    }
+}
diff --git a/www/index.php b/www/index.php
index 9f3a6ace2dfd38dd39694c5c0a83d6788df394a2..398fe240ff21b442f23eab7fef444a2916594c6a 100644
--- a/www/index.php
+++ b/www/index.php
@@ -2,9 +2,32 @@
 /**
  * This is the main file for the graduate catalog.
  */
+namespace UNL\Catalog;
 
-require_once dirname(__DIR__).'/config.inc.php';
-$catalog = new UNL_Catalog();
-$catalog->run();
+$config_file = __DIR__ . '/../config.sample.php';
 
-echo $catalog->toHtml();
\ No newline at end of file
+if (file_exists(__DIR__ . '/../config.inc.php')) {
+    $config_file = __DIR__ . '/../config.inc.php';
+}
+require_once $config_file;
+
+
+use RegExpRouter as RegExpRouter;
+
+$routes = include __DIR__ . '/../data/routes.php';
+$router = new RegExpRouter\Router(array('baseURL' => Controller::$url));
+$router->setRoutes($routes);
+if (isset($_GET['model'])) {
+    // Prevent injecting a specific model through the web interface
+    unset($_GET['model']);
+}
+
+
+// Initialize Controller, and construct everything the user requested
+$catalog = new Controller($router->route($_SERVER['REQUEST_URI'], $_GET));
+
+// Now render what the user has requested
+$savvy = new OutputController($catalog->options);
+$savvy->addGlobal('catalog', $catalog);
+
+echo $savvy->render($catalog);
\ No newline at end of file
diff --git a/www/templates/Catalog.tpl.php b/www/templates/Catalog.tpl.php
deleted file mode 100644
index 15c5adc7fae9b7dab7ba2410a6cabc4361f76852..0000000000000000000000000000000000000000
--- a/www/templates/Catalog.tpl.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-
-?>
\ No newline at end of file
diff --git a/www/templates/footer.html b/www/templates/html/Footer.tpl.php
similarity index 100%
rename from www/templates/footer.html
rename to www/templates/html/Footer.tpl.php
diff --git a/www/templates/footerContactInfo.html b/www/templates/html/FooterContactInfo.tpl.php
similarity index 100%
rename from www/templates/footerContactInfo.html
rename to www/templates/html/FooterContactInfo.tpl.php
diff --git a/www/templates/html/Navigation.tpl.php b/www/templates/html/Navigation.tpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..12481e7778441bc5a6b9e839a4029c06d2ebee1c
--- /dev/null
+++ b/www/templates/html/Navigation.tpl.php
@@ -0,0 +1,3 @@
+<ul>
+	<li><a href="<?php echo $catalog->url; ?>">Graduate Bulletin Home</a></li>
+</ul>
diff --git a/www/templates/relatedLinks.html b/www/templates/html/RelatedLinks.tpl.php
similarity index 100%
rename from www/templates/relatedLinks.html
rename to www/templates/html/RelatedLinks.tpl.php
diff --git a/www/templates/html/UNL/Catalog/Controller.tpl.php b/www/templates/html/UNL/Catalog/Controller.tpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..b04ab254a25859afd480f1fbab396e45d70398f7
--- /dev/null
+++ b/www/templates/html/UNL/Catalog/Controller.tpl.php
@@ -0,0 +1,41 @@
+<?php
+UNL_Templates::setCachingService(new UNL_Templates_CachingService_Null());
+UNL_Templates::$options['version']        = 3;
+UNL_Templates::$options['sharedcodepath'] = dirname(__FILE__).'/sharedcode';
+
+$url     = $catalog->getURL();
+$page    = UNL_Templates::factory('Fixed');
+
+$page->head           .= '<link rel="stylesheet" type="text/css" href="http://bulletin.unl.edu/undergraduate/templates/html/css/all.css" />';
+$page->head           .= '<link rel="search" href="'.$url.'?cx=015236299699564929946%3Aipiaeommikw&amp;cof=FORID%3A11&amp;view=search" />';
+$page->head           .= '<link rel="home" href="'.$url.'" />';
+$page->doctitle        = '<title>2012-2013 Graduate Studies Bulletin | University of Nebraska-Lincoln</title>';
+$page->titlegraphic    = '<h1>Graduate Studies Bulletin 2012-2013</h1>';
+$page->maincontentarea = '';
+$page->pagetitle       = '';
+$page->breadcrumbs     = '<ul>
+                                <li class="first"><a href="http://www.unl.edu/">UNL</a></li>
+                                <li><a href="http://www.unl.edu/gradstudies/">Graduate Studies</a></li>
+                                <li>Bulletin</li>
+                            </ul>';
+$page->footercontent   = $savvy->render(null, 'Footer.tpl.php');
+$page->leftcollinks    = $savvy->render(null, 'RelatedLinks.tpl.php');
+$page->contactinfo     = $savvy->render(null, 'FooterContactInfo.tpl.php');
+$page->navlinks        = $savvy->render(null, 'Navigation.tpl.php');
+
+
+
+$page->addStylesheet('/wdn/templates_3.0/css/content/notice.css');
+$page->addStylesheet($url . 'templates/html/css/all.css');
+$page->addStyleSheet($url . 'templates/html/css/print.css', 'print');
+
+$page->head .= '
+<script type="text/javascript">var UNL_UGB_URL = "'.$url.'";</script>
+<link rel="home" href="'.$url.'" />
+<link rel="search" href="'.$url.'search/" />
+';
+
+$page->maincontentarea = '';
+$page->maincontentarea .= $savvy->render($context->output);
+
+echo $page;
diff --git a/www/templates/Index.tpl.php b/www/templates/html/UNL/Catalog/Homepage.tpl.php
similarity index 100%
rename from www/templates/Index.tpl.php
rename to www/templates/html/UNL/Catalog/Homepage.tpl.php
diff --git a/www/templates/navigation.html b/www/templates/navigation.html
deleted file mode 100644
index 6c7671cd33c6905e14092a5c1f01acaf77d949ee..0000000000000000000000000000000000000000
--- a/www/templates/navigation.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<ul>
-	<li><a href="@@UNL_CATALOG_URI@@">Graduate Bulletin Home</a></li>
-</ul>