diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css
index 077b79fbb6a5e65a4cf9136ccf686f3077b552b8..c4bd447e8ce924d267453b60b4b0faa764ce5a5f 100644
--- a/assets/default/css/theme.css
+++ b/assets/default/css/theme.css
@@ -568,6 +568,15 @@ label.required {
   position: relative;
   top: 6px;
 }
+#ticketForm > table {
+    table-layout: fixed;
+}
+#ticketForm > table td {
+    width: 160px;
+}
+#ticketForm > table td + td {
+    width: auto;
+}
 #ticketForm td textarea,
 #clientLogin td textarea,
 #ticketForm div textarea,
diff --git a/bootstrap.php b/bootstrap.php
index 7c2f1135ac00c12ee4b696ac1819049260c45509..3e01197710452ab346db3d580dbd40a5c931b3ba 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -2,7 +2,7 @@
 
 class Bootstrap {
 
-    function init() {
+    static function init() {
         #Disable Globals if enabled....before loading config info
         if(ini_get('register_globals')) {
            ini_set('register_globals',0);
@@ -218,9 +218,11 @@ class Bootstrap {
             define('LATIN1_UC_CHARS', 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝ');
             define('LATIN1_LC_CHARS', 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüý');
             function mb_strtoupper($str) {
+                if (is_array($str)) $str = $str[0];
                 return strtoupper(strtr($str, LATIN1_LC_CHARS, LATIN1_UC_CHARS));
             }
             function mb_strtolower($str) {
+                if (is_array($str)) $str = $str[0];
                 return strtolower(strtr($str, LATIN1_UC_CHARS, LATIN1_LC_CHARS));
             }
             define('MB_CASE_LOWER', 1);
@@ -231,16 +233,20 @@ class Bootstrap {
                 //      char is not a single-byte char
                 switch ($mode) {
                 case MB_CASE_LOWER:
-                    return preg_replace_callback('/\p{Lu}+/u',
-                        function($a) { return mb_strtolower($a); }, $str);
+                    return preg_replace_callback('/\p{Lu}+/u', 'mb_strtolower', $str);
                 case MB_CASE_UPPER:
-                    return preg_replace_callback('/\p{Ll}+/u',
-                        function($a) { return mb_strtoupper($a); }, $str);
+                    return preg_replace_callback('/\p{Ll}+/u', 'mb_strtoupper', $str);
                 case MB_CASE_TITLE:
-                    return preg_replace_callback('/\b\p{Ll}/u',
-                        function($a) { return mb_strtoupper($a); }, $str);
+                    return preg_replace_callback('/\b\p{Ll}/u', 'mb_strtoupper', $str);
                 }
             }
+            function mb_internal_encoding($encoding) { return 'UTF-8'; }
+            function mb_regex_encoding($encoding) { return 'UTF-8'; }
+            function mb_substr_count($haystack, $needle) {
+                $matches = array();
+                return preg_match_all('`'.preg_quote($needle).'`u', $haystack,
+                    $matches);
+            }
         }
         else {
             // Use UTF-8 for all multi-byte string encoding
@@ -296,7 +302,6 @@ Bootstrap::init();
 
 #CURRENT EXECUTING SCRIPT.
 define('THISPAGE', Misc::currentURL());
-define('THISURI', $_SERVER['REQUEST_URI']);
 
 define('DEFAULT_MAX_FILE_UPLOADS',ini_get('max_file_uploads')?ini_get('max_file_uploads'):5);
 define('DEFAULT_PRIORITY_ID',1);
diff --git a/css/thread.css b/css/thread.css
index 1312af2f38a45e73bce112edf6475ad2c4f1f642..14c0c694d7ce5e76346027181ef3404ee79506e6 100644
--- a/css/thread.css
+++ b/css/thread.css
@@ -396,7 +396,21 @@
 	line-height: 1.25rem;
 }
 
-.thread-body div:not(.caption),
+/* Adjust plain/text messages posted as <pre> in the thread body to show in
+ * a more normal font. Other <pre> elements in the ticket thread body should
+ * be shown with the ususal monospace font
+ */
+.thread-body > div > pre:first-child {
+    font-family: sans-serif;
+}
+
+/* Avoid extra padding at the bottom of the thread body element */
+.thread-body :last-child,
+.thread-body > div {
+    margin-bottom: 0 !important;
+}
+
+.thread-body > div div:not(.caption),
 .thread-body p,
 .thread-body ul,
 .thread-body ol,
@@ -451,10 +465,24 @@ table.thread-entry {
     table-layout: fixed;
 }
 
+table.thread-entry th div span {
+    vertical-align: middle;
+}
+table.thread-entry th div :not(.title) {
+    font-weight: 600;
+}
+table.thread-entry th div .title {
+    font-weight: 400;
+}
+table.thread-entry th .textra {
+    margin-right: 1em;
+    display: inline-block;
+}
 /* Inline image hovering with download link */
 .image-hover {
     display: inline-block;
     position: relative;
+    max-width: 100%; /* Ensure image hovered is resized */
 }
 .image-hover .caption {
     background-color: rgba(0,0,0,0.5);
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 058bb12a37f9c5f7c8723b695fa40d56ed1c51c2..bee2c7d6cfcd7be03684f1baedb273979b7205a5 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -211,13 +211,13 @@ class TicketsAjaxAPI extends AjaxController {
              'WHERE entry.object_type="T" GROUP BY entry.object_id)';
         $vals = array();
         foreach (TicketForm::getInstance()->getFields() as $f) {
-            if ($f->get('name') && isset($req[$f->getFormName()])
+            if (isset($req[$f->getFormName()])
                     && ($val = $req[$f->getFormName()])) {
-                $name = $f->get('name');
-                $vals[] = "MAX(IF(field.name = '$name', ans.value_id, NULL)) as `{$name}_id`";
-                $vals[] = "MAX(IF(field.name = '$name', ans.value, NULL)) as `$name`";
-                $where .= " AND (dyn.`{$name}_id` = ".db_input($val)
-                    . " OR dyn.`$name` LIKE '%".db_real_escape($val)."%')";
+                $id = $f->get('id');
+                $vals[] = "MAX(IF(field.id = '$id', ans.value_id, NULL)) as `f_{$id}_id`";
+                $vals[] = "MAX(IF(field.id = '$id', ans.value, NULL)) as `f_$id`";
+                $where .= " AND (dyn.`f_{$id}_id` = ".db_input($val)
+                    . " OR dyn.`f_$id` LIKE '%".db_real_escape($val)."%')";
             }
         }
         if ($vals)
@@ -604,8 +604,9 @@ class TicketsAjaxAPI extends AjaxController {
 
 
         $info = array(
-                'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(), $user->getName())
-                );
+            'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(),
+                Format::htmlchars($user->getName()))
+            );
 
         ob_start();
         include(STAFFINC_DIR . 'templates/user.tmpl.php');
@@ -632,8 +633,9 @@ class TicketsAjaxAPI extends AjaxController {
         $forms = $user->getForms();
 
         $info = array(
-                'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(), $user->getName())
-                );
+            'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(),
+                Format::htmlchars($user->getName()))
+            );
 
         ob_start();
         include(STAFFINC_DIR . 'templates/user.tmpl.php');
diff --git a/include/ajax.tips.php b/include/ajax.tips.php
index 3b2a1d19a565fe7d812df9920857c9589c89f6b5..ed560403e945933600ad3390dea1aa508530ea8b 100644
--- a/include/ajax.tips.php
+++ b/include/ajax.tips.php
@@ -31,8 +31,9 @@ class HelpTipAjaxAPI extends AjaxController {
 
         // Translate links to the root path of this installation
         foreach ($data as $tip=>&$info) {
-            $info = $ost->replaceTemplateVariables($info, array(
-                'config'=>$ost->getConfig()));
+            if ($ost)
+                $info = $ost->replaceTemplateVariables($info, array(
+                    'config'=>$ost->getConfig()));
             if (isset($info['links']))
                 foreach ($info['links'] as &$l)
                     if ($l['href'][0] == '/')
diff --git a/include/ajax.users.php b/include/ajax.users.php
index e73c0b054894af1bdecf2a0f2af2d2a514ec0ce8..28b98e0190de1a2085fa5dee0c7f075634734a56 100644
--- a/include/ajax.users.php
+++ b/include/ajax.users.php
@@ -45,6 +45,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if(($res=db_query($sql)) && db_num_rows($res)){
             while(list($id,$email,$name)=db_fetch_row($res)) {
+                $name = Format::htmlchars($name);
                 $users[] = array('email'=>$email, 'name'=>$name, 'info'=>"$email - $name",
                     "id" => $id, "/bin/true" => $_REQUEST['q']);
             }
@@ -54,9 +55,41 @@ class UsersAjaxAPI extends AjaxController {
 
     }
 
-    function getUser() {
+    function editUser($id) {
+        global $thisstaff;
+
+        if(!$thisstaff)
+            Http::response(403, 'Login Required');
+        elseif(!($user = User::lookup($id)))
+            Http::response(404, 'Unknown user');
+
+        $info = array(
+            'title' => sprintf('Update %s', $user->getName())
+        );
+        $forms = $user->getForms();
+
+        include(STAFFINC_DIR . 'templates/user.tmpl.php');
+    }
+
+    function updateUser($id) {
+        global $thisstaff;
+
+        if(!$thisstaff)
+            Http::response(403, 'Login Required');
+        elseif(!($user = User::lookup($id)))
+            Http::response(404, 'Unknown user');
+
+        $errors = array();
+        if($user->updateInfo($_POST, $errors))
+             Http::response(201, $user->to_json());
+
+        $forms = $user->getForms();
+        include(STAFFINC_DIR . 'templates/user.tmpl.php');
+    }
+
+    function getUser($id=false) {
 
-        if(($user=User::lookup($_REQUEST['id'])))
+        if(($user=User::lookup(($id) ? $id : $_REQUEST['id'])))
            Http::response(201, $user->to_json());
 
         $info = array('error' =>'Unknown or invalid user');
diff --git a/include/class.config.php b/include/class.config.php
index e6c9351f9bc23040db0704e5d84bc7661a1befc2..06607fdc52393e084f8fdc9dde376b4c8108a1e1 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -858,7 +858,7 @@ class OsticketConfig extends Config {
             'autolock_minutes'=>$vars['autolock_minutes'],
             'use_email_priority'=>isset($vars['use_email_priority'])?1:0,
             'enable_captcha'=>isset($vars['enable_captcha'])?1:0,
-            'auto_assign_reopened_tickets'=>isset($vars['auto_assign_reopened_tickets'])?1:0,
+            'auto_claim_tickets'=>isset($vars['auto_claim_tickets'])?1:0,
             'show_assigned_tickets'=>isset($vars['show_assigned_tickets'])?1:0,
             'show_answered_tickets'=>isset($vars['show_answered_tickets'])?1:0,
             'show_related_tickets'=>isset($vars['show_related_tickets'])?1:0,
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 081ceabc7d342a110791065e8f48c13296c3b045..52e3109356db760e6a9d412c7782b5697b9891ec 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -39,10 +39,11 @@ class DynamicForm extends VerySimpleModel {
 
     var $_form;
     var $_fields;
+    var $_has_data = false;
     var $_dfields;
 
-    function getFields() {
-        if (!isset($this->_fields)) {
+    function getFields($cache=true) {
+        if (!isset($this->_fields) || !$cache) {
             $this->_fields = array();
             foreach ($this->getDynamicFields() as $f)
                 // TODO: Index by field name or id
@@ -83,7 +84,7 @@ class DynamicForm extends VerySimpleModel {
 
     function getForm($source=false) {
         if (!$this->_form || $source) {
-            $fields = $this->getFields();
+            $fields = $this->getFields($this->_has_data);
             $this->_form = new Form($fields, $source, array(
                 'title'=>$this->title, 'instructions'=>$this->instructions));
         }
@@ -102,6 +103,7 @@ class DynamicForm extends VerySimpleModel {
     function data($data) {
         if ($data instanceof DynamicFormEntry) {
             $this->_fields = $data->getFields();
+            $this->_has_data = true;
         }
     }
 
@@ -144,8 +146,8 @@ class UserForm extends DynamicForm {
         return $os->filter(array('type'=>'U'));
     }
 
-    function getFields() {
-        $fields = parent::getFields();
+    function getFields($cache=true) {
+        $fields = parent::getFields($cache);
         foreach ($fields as $f) {
             if ($f->get('name') == 'email') {
                 $f->getConfiguration();
@@ -535,7 +537,7 @@ class DynamicFormEntry extends VerySimpleModel {
             if ($field->hasData() && !$field->isPresentationOnly())
                 $a->save();
         }
-        $this->_values = array();
+        $this->_values = null;
     }
 
     function delete() {
@@ -619,6 +621,10 @@ class DynamicFormEntryAnswer extends VerySimpleModel {
         return $this->getField()->toString($this->getValue());
     }
 
+    function display() {
+        return $this->getField()->display($this->getValue());
+    }
+
     function asVar() {
         return $this->toString();
     }
diff --git a/include/class.forms.php b/include/class.forms.php
index 67b05e16ddf8aaae0f6e37056b2cade65b5595db..cfa0a1cef0deb5528dbd3a03f5a271c91a7db335 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -290,6 +290,13 @@ class FormField {
         return $value;
     }
 
+    /**
+     * Returns an HTML friendly value for the data in the field.
+     */
+    function display($value) {
+        return Format::htmlchars($this->toString($value));
+    }
+
     function getLabel() { return $this->get('label'); }
 
     /**
@@ -532,6 +539,14 @@ class TextareaField extends FormField {
                 'configuration'=>array('desc'=>'Allow HTML input in this box'))),
         );
     }
+
+    function display($value) {
+        $config = $this->getConfiguration();
+        if ($config['html'])
+            return Format::safe_html($value);
+        else
+            return Format::htmlchars($value);
+    }
 }
 
 class PhoneField extends FormField {
diff --git a/include/class.osticket.php b/include/class.osticket.php
index 0e76fe3e6f4f02c6c79b72081182e90d2c002c91..e1c7a9075b656bef894a2cdf774f3947ca9754b3 100644
--- a/include/class.osticket.php
+++ b/include/class.osticket.php
@@ -366,8 +366,7 @@ class osTicket {
         return null;
     }
 
-    /* static */
-    function get_root_path($dir) {
+    static function get_root_path($dir) {
 
         /* If run from the commandline, DOCUMENT_ROOT will not be set. It is
          * also likely that the ROOT_PATH will not be necessary, so don't
@@ -398,7 +397,8 @@ class osTicket {
          * the osTicket installation. That is removed from SCRIPT_NAME.
          * What's left is the ROOT_PATH.
          */
-        $frame = array_pop(debug_backtrace(false));
+        $bt = debug_backtrace(false);
+        $frame = array_pop($bt);
         $file = str_replace('\\','/', $frame['file']);
         $path = substr($file, strlen(ROOT_DIR));
         if($path && ($pos=strpos($_SERVER['SCRIPT_NAME'], $path))!==false)
diff --git a/include/class.setup.php b/include/class.setup.php
index f281da61b59a910063af511bcb2a0d0ff9da7cf7..cc13fe2f5e60b273f4aff26dbcb754aebfd917c0 100644
--- a/include/class.setup.php
+++ b/include/class.setup.php
@@ -93,6 +93,10 @@ Class SetupWizard {
         return (extension_loaded('mysqli'));
     }
 
+    function check_mysql_version() {
+        return (version_compare(db_version(), $this->getMySQLVersion())>=0);
+    }
+
     function check_prereq() {
         return ($this->check_php() && $this->check_mysql());
     }
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 318d205358e4775aeeb5e42b377f689dde6af4cb..273d8e292773aeaf0b9720eb239001db010be9a8 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -23,7 +23,6 @@ include_once(INCLUDE_DIR.'class.topic.php');
 include_once(INCLUDE_DIR.'class.lock.php');
 include_once(INCLUDE_DIR.'class.file.php');
 include_once(INCLUDE_DIR.'class.attachment.php');
-include_once(INCLUDE_DIR.'class.pdf.php');
 include_once(INCLUDE_DIR.'class.banlist.php');
 include_once(INCLUDE_DIR.'class.template.php');
 include_once(INCLUDE_DIR.'class.variable.php');
@@ -304,16 +303,6 @@ class Ticket {
         return '';
     }
 
-    function getPhone() {
-        list($phone, $ext) = $this->getPhoneNumber();
-        return $phone;
-    }
-
-    function getPhoneExt() {
-        list($phone, $ext) = $this->getPhoneNumber();
-        return $ext;
-    }
-
     function getPhoneNumber() {
         return (string)$this->getOwner()->getPhoneNumber();
     }
@@ -1150,6 +1139,7 @@ class Ticket {
             return call_user_func(array($this, 'get'.ucfirst($tag)));
 
         switch(strtolower($tag)) {
+            case 'phone':
             case 'phone_number':
                 return $this->getPhoneNumber();
                 break;
@@ -1761,6 +1751,7 @@ class Ticket {
 
     //Print ticket... export the ticket thread as PDF.
     function pdfExport($psize='Letter', $notes=false) {
+        require_once(INCLUDE_DIR.'class.pdf.php');
         $pdf = new Ticket2PDF($this, $psize, $notes);
         $name='Ticket-'.$this->getExtId().'.pdf';
         $pdf->Output($name, 'I');
@@ -1931,47 +1922,57 @@ class Ticket {
         if(!$staff || (!is_object($staff) && !($staff=Staff::lookup($staff))) || !$staff->isStaff())
             return null;
 
-        $sql='SELECT count(open.ticket_id) as open, count(answered.ticket_id) as answered '
-            .' ,count(overdue.ticket_id) as overdue, count(assigned.ticket_id) as assigned, count(closed.ticket_id) as closed '
-            .' FROM '.TICKET_TABLE.' ticket '
-            .' LEFT JOIN '.TICKET_TABLE.' open
-                ON (open.ticket_id=ticket.ticket_id
-                        AND open.status=\'open\'
-                        AND open.isanswered=0
-                        '.((!($cfg->showAssignedTickets() || $staff->showAssignedTickets()))?
-                        ' AND open.staff_id=0 ':'').') '
-            .' LEFT JOIN '.TICKET_TABLE.' answered
-                ON (answered.ticket_id=ticket.ticket_id
-                        AND answered.status=\'open\'
-                        AND answered.isanswered=1) '
-            .' LEFT JOIN '.TICKET_TABLE.' overdue
-                ON (overdue.ticket_id=ticket.ticket_id
-                        AND overdue.status=\'open\'
-                        AND overdue.isoverdue=1) '
-            .' LEFT JOIN '.TICKET_TABLE.' assigned
-                ON (assigned.ticket_id=ticket.ticket_id
-                        AND assigned.status=\'open\'
-                        AND assigned.staff_id='.db_input($staff->getId()).')'
-            .' LEFT JOIN '.TICKET_TABLE.' closed
-                ON (closed.ticket_id=ticket.ticket_id
-                        AND closed.status=\'closed\' )'
-            .' WHERE (ticket.staff_id='.db_input($staff->getId());
+        $where = array();
+        $where2 = '';
 
         if(($teams=$staff->getTeams()))
-            $sql.=' OR ticket.team_id IN('.implode(',', db_input(array_filter($teams))).')';
+            $where[] = 'ticket.team_id IN('.implode(',', db_input(array_filter($teams))).')';
 
         if(!$staff->showAssignedOnly() && ($depts=$staff->getDepts())) //Staff with limited access just see Assigned tickets.
-            $sql.=' OR ticket.dept_id IN('.implode(',', db_input($depts)).') ';
-
-        $sql.=')';
+            $where[] = 'ticket.dept_id IN('.implode(',', db_input($depts)).') ';
 
         if(!$cfg || !($cfg->showAssignedTickets() || $staff->showAssignedTickets()))
-            $sql.=' AND (ticket.staff_id=0 OR ticket.staff_id='.db_input($staff->getId()).') ';
-
-        return db_fetch_array(db_query($sql));
+            $where2 =' AND (ticket.staff_id=0 OR ticket.staff_id='.db_input($staff->getId()).') ';
+        $where = implode(' OR ', $where);
+        if ($where) $where = 'AND ( '.$where.' ) ';
+
+        $sql =  'SELECT \'open\', count( ticket.ticket_id ) AS tickets '
+                .'FROM ' . TICKET_TABLE . ' ticket '
+                .'WHERE ticket.status = \'open\' '
+                .'AND ticket.isanswered =0 '
+                . $where . $where2
+
+                .'UNION SELECT \'answered\', count( ticket.ticket_id ) AS tickets '
+                .'FROM ' . TICKET_TABLE . ' ticket '
+                .'WHERE ticket.status = \'open\' '
+                .'AND ticket.isanswered =1 '
+                . $where
+
+                .'UNION SELECT \'overdue\', count( ticket.ticket_id ) AS tickets '
+                .'FROM ' . TICKET_TABLE . ' ticket '
+                .'WHERE ticket.status = \'open\' '
+                .'AND ticket.isoverdue =1 '
+                . $where
+
+                .'UNION SELECT \'assigned\', count( ticket.ticket_id ) AS tickets '
+                .'FROM ' . TICKET_TABLE . ' ticket '
+                .'WHERE ticket.status = \'open\' '
+                .'AND ticket.staff_id = ' . db_input($staff->getId()) . ' '
+                . $where
+
+                .'UNION SELECT \'closed\', count( ticket.ticket_id ) AS tickets '
+                .'FROM ' . TICKET_TABLE . ' ticket '
+                .'WHERE ticket.status = \'closed\' '
+                . $where;
+
+        $res = db_query($sql);
+        $stats = array();
+        while($row = db_fetch_row($res)) {
+            $stats[$row[0]] = $row[1];
+        }
+        return $stats;
     }
 
-
     /* Quick client's tickets stats
        @email - valid email.
      */
diff --git a/include/class.upgrader.php b/include/class.upgrader.php
index 69af4dfde6ee3f1b96b583ac3d00abcf07ff8562..c08ff6c43cbbd4e9e347efc27108873d1695e0c5 100644
--- a/include/class.upgrader.php
+++ b/include/class.upgrader.php
@@ -103,20 +103,16 @@ class Upgrader {
         return $this->getCurrentStream()->upgrade();
     }
 
-    function check_prereq() {
-        if ($this->getCurrentStream())
-            return $this->getCurrentStream()->check_prereq();
-    }
-    function check_php() {
-        if ($this->getCurrentStream())
-            return $this->getCurrentStream()->check_php();
-    }
-    function check_mysql() {
-        if ($this->getCurrentStream())
-            return $this->getCurrentStream()->check_mysql();
+    function __call($what, $args) {
+        if ($this->getCurrentStream()) {
+            $callable = array($this->getCurrentStream(), $what);
+            if (!is_callable($callable))
+                throw new Exception('InternalError: Upgrader method not callable: '
+                    . $what);
+            return call_user_func_array($callable, $args);
+        }
     }
 
-
     function getTask() {
         if($this->getCurrentStream())
             return $this->getCurrentStream()->getTask();
@@ -213,6 +209,9 @@ class StreamUpgrader extends SetupWizard {
         $this->migrater = null;
     }
 
+    function check_prereq() {
+        return (parent::check_prereq() && $this->check_mysql_version());
+    }
     function onError($error) {
         global $ost, $thisstaff;
 
diff --git a/include/class.user.php b/include/class.user.php
index 85bffe026d3769715c466219d487749de92f6fca..3281d38c2865ee013e62f563c11c116fd6b9f490 100644
--- a/include/class.user.php
+++ b/include/class.user.php
@@ -143,7 +143,8 @@ class User extends UserModel {
         $info = array(
                 'id'  => $this->getId(),
                 'name' => (string) $this->getName(),
-                'email' => (string) $this->getEmail());
+                'email' => (string) $this->getEmail(),
+                'phone' => (string) $this->getPhoneNumber());
 
         return JsonDataEncoder::encode($info);
     }
diff --git a/include/client/header.inc.php b/include/client/header.inc.php
index e03f36cbb1aa29b762fc8f4e5eaadba7a51aa489..7266060fbf2f50160e54b1a275f3b4e90fafc87e 100644
--- a/include/client/header.inc.php
+++ b/include/client/header.inc.php
@@ -43,7 +43,7 @@ header("Content-Type: text/html; charset=UTF-8\r\n");
             <p>
              <?php
              if($thisclient && is_object($thisclient) && $thisclient->isValid()) {
-                 echo $thisclient->getName().'&nbsp;-&nbsp;';
+                 echo Format::htmlchars($thisclient->getName()).'&nbsp;-&nbsp;';
                  ?>
                 <?php
                 if($cfg->showRelatedTickets()) {?>
diff --git a/include/client/view.inc.php b/include/client/view.inc.php
index f7884302e3db5256af9a92272cf1696369106297..910d021de0fd98940996d90b311fb54a848a134e 100644
--- a/include/client/view.inc.php
+++ b/include/client/view.inc.php
@@ -39,7 +39,7 @@ if(!$dept || !$dept->isPublic())
            <table class="infoTable" cellspacing="1" cellpadding="3" width="100%" border="0">
                <tr>
                    <th width="100">Name:</th>
-                   <td><?php echo ucfirst($ticket->getName()); ?></td>
+                   <td><?php echo ucfirst(Format::htmlchars($ticket->getName())); ?></td>
                </tr>
                <tr>
                    <th width="100">Email:</th>
@@ -70,7 +70,7 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $idx=>$form) {
         <tr>
         <th width="100"><?php echo $answer->getField()->get('label');
             ?>:</th>
-        <td><?php echo $answer->toString(); ?></td>
+        <td><?php echo $answer->display(); ?></td>
         </tr>
     <?php } ?>
     </table></td>
diff --git a/include/staff/cannedresponse.inc.php b/include/staff/cannedresponse.inc.php
index 4e38ff9649cb3447e3d4a6d98b6277ea144c32d4..d82baad74ffb9a98cd6dc494f3ab4ce9ae55c8fb 100644
--- a/include/staff/cannedresponse.inc.php
+++ b/include/staff/cannedresponse.inc.php
@@ -28,8 +28,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
  <h2>Canned Response</h2>
- <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
+ <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
+        <tr><td></td><td></td></tr> <!-- For fixed table layout -->
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php
index de9ae77e8eda8790e0f018d58442b6f25b38fb35..a5019a722707d7941a731033eccd48be1abde9fd 100644
--- a/include/staff/dynamic-form.inc.php
+++ b/include/staff/dynamic-form.inc.php
@@ -37,6 +37,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             <td><input type="text" name="title" size="40" value="<?php
                 echo $info['title']; ?>"/>
                 <i class="help-tip icon-question-sign" href="#form_title"></i>
+                <font class="error"><?php
+                    if ($errors['title']) echo '<br/>'; echo $errors['title']; ?></font>
             </td>
         </tr>
         <tr>
diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php
index 476cf5b0af0e9a4a9cb63d4ec8edc6051c455a27..8b390c3de8d43762ce79f5560d07486e2c8bc636 100644
--- a/include/staff/faq.inc.php
+++ b/include/staff/faq.inc.php
@@ -30,8 +30,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
  <h2>FAQ</h2>
- <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
+ <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
+        <tr><td></td><td></td></tr> <!-- For fixed table layout -->
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
@@ -125,7 +126,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 <em><strong>Help Topics</strong>: Check all help topics related to this FAQ.</em>
             </th>
         </tr>
-        <tr><td>
+        <tr><td colspan="2">
             <?php
             while(list($topicId,$topic)=db_fetch_row($res)) {
                 echo sprintf('<input type="checkbox" name="topics[]" value="%d" %s>%s<br>',
diff --git a/include/staff/page.inc.php b/include/staff/page.inc.php
index b76a40607b58614c031829b62431e66c9496183e..884d7377eacb64d6152eda71373e504716a290fd 100644
--- a/include/staff/page.inc.php
+++ b/include/staff/page.inc.php
@@ -32,8 +32,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
  <h2>Site Pages</h2>
- <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
+ <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
+        <tr><td></td><td></td></tr> <!-- For fixed table layout -->
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
diff --git a/include/staff/settings-pages.inc.php b/include/staff/settings-pages.inc.php
index e0f834107810ade075abaf1c9c3f9afeb609a52b..7b75501d55fe8d1d9706eda5bcfe91bab72ded64 100644
--- a/include/staff/settings-pages.inc.php
+++ b/include/staff/settings-pages.inc.php
@@ -15,7 +15,9 @@ $pages = Page::getPages();
     </tr></thead>
     <tbody>
     <?php
-        $ost->company->getForm()->render();
+        $form = $ost->company->getForm();
+        $form->addMissingFields();
+        $form->render();
     ?>
     </tbody>
     <thead>
diff --git a/include/staff/templates/user-lookup.tmpl.php b/include/staff/templates/user-lookup.tmpl.php
index 8a6edd8f8771ca1cd583f3d1801eb1f72925969d..fcc8ec2ca3624364bc1b668aebdf843f00036fbd 100644
--- a/include/staff/templates/user-lookup.tmpl.php
+++ b/include/staff/templates/user-lookup.tmpl.php
@@ -1,3 +1,4 @@
+<div id="the-lookup-form">
 <h3><?php echo $info['title']; ?></h3>
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
 <hr/>
@@ -15,8 +16,23 @@ if ($info['error']) {
     <i class="icon-user icon-4x pull-left icon-border"></i>
     <a class="action-button pull-right" style="overflow:inherit"
         id="unselect-user"  href="#"><i class="icon-remove"></i> Add New User</a>
-    <div><strong id="user-name"><?php echo $user ? $user->getName() : ''; ?></strong></div>
+    <div><strong id="user-name"><?php echo $user ? Format::htmlchars($user->getName()) : ''; ?></strong></div>
     <div>&lt;<span id="user-email"><?php echo $user ? $user->getEmail() : ''; ?></span>&gt;</div>
+<?php if ($user) { ?>
+    <table style="margin-top: 1em;">
+<?php foreach ($user->getDynamicData() as $entry) { ?>
+    <tr><td colspan="2" style="border-bottom: 1px dotted black"><strong><?php
+         echo $entry->getForm()->get('title'); ?></strong></td></tr>
+<?php foreach ($entry->getAnswers() as $a) { ?>
+    <tr style="vertical-align:top"><td style="width:30%;border-bottom: 1px dotted #ccc"><?php echo Format::htmlchars($a->getField()->get('label'));
+         ?>:</td>
+    <td style="border-bottom: 1px dotted #ccc"><?php echo $a->display(); ?></td>
+    </tr>
+<?php }
+}
+?>
+</table>
+<?php } ?>
     <div class="clear"></div>
     <hr>
     <p class="full-width">
@@ -49,6 +65,7 @@ if ($info['error']) {
 </form>
 </div>
 <div class="clear"></div>
+</div>
 <script type="text/javascript">
 $(function() {
     $('#user-search').typeahead({
@@ -62,12 +79,9 @@ $(function() {
             });
         },
         onselect: function (obj) {
-            $('#user-name').text(obj.name);
-            $('#user-email').text(obj.email);
-            $('#user-id').val(obj.id);
-            $('div#selected-user-info').show();
-            $('div#new-user-form').hide();
-            $('#user-search').val('');
+            $('#the-lookup-form').load(
+                "ajax.php/users/select/"+obj.id
+            );
         },
         property: "/bin/true"
     });
diff --git a/include/staff/templates/user.tmpl.php b/include/staff/templates/user.tmpl.php
index 37528cc52d13deff96f20c7062231503c43ec45a..456176c8139c37f4a3e040de20753eafa5945a36 100644
--- a/include/staff/templates/user.tmpl.php
+++ b/include/staff/templates/user.tmpl.php
@@ -1,6 +1,6 @@
 <?php
 if (!$info['title'])
-    $info['title'] = $user->getName();
+    $info['title'] = Format::htmlchars($user->getName());
 ?>
 <h3><?php echo $info['title']; ?></h3>
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
@@ -20,9 +20,22 @@ if ($info['error']) {
     <?php
     } ?>
     <div><b><a href="#" id="edituser"><i class="icon-edit"></i>&nbsp;<?php
-    echo $user->getName(); ?></a></b></div>
+    echo Format::htmlchars($user->getName()); ?></a></b></div>
     <div>&lt;<?php echo $user->getEmail(); ?>&gt;</div>
-    <div><?php echo $user->getPhoneNumber(); ?></div>
+    <table style="margin-top: 1em;">
+<?php foreach ($user->getDynamicData() as $entry) {
+?>
+    <tr><td colspan="2" style="border-bottom: 1px dotted black"><strong><?php
+         echo $entry->getForm()->get('title'); ?></strong></td></tr>
+<?php foreach ($entry->getAnswers() as $a) { ?>
+    <tr style="vertical-align:top"><td style="width:30%;border-bottom: 1px dotted #ccc"><?php echo Format::htmlchars($a->getField()->get('label'));
+         ?>:</td>
+    <td style="border-bottom: 1px dotted #ccc"><?php echo $a->display(); ?></td>
+    </tr>
+<?php }
+}
+?>
+    </table>
     <div class="clear"></div>
     <hr>
     <div class="faded">Last updated <b><?php echo Format::db_datetime($user->getUpdateDate()); ?> </b></div>
@@ -47,7 +60,8 @@ if ($ticket && $ticket->getOwnerId() == $user->getId())
     <p class="full-width">
         <span class="buttons" style="float:left">
             <input type="reset" value="Reset">
-            <input type="button" name="cancel" class="<?php echo $user ? 'cancel' : 'close' ?>"  value="Cancel">
+            <input type="button" name="cancel" class="<?php
+    echo ($ticket && $user) ? 'cancel' : 'close' ?>"  value="Cancel">
         </span>
         <span class="buttons" style="float:right">
             <input type="submit" value="Update User">
diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php
index 6294cda9205fda70173468c4fad871c8e8c3ce38..cddb2ee3c6b36f248110fb85833cdb87760b9b9a 100644
--- a/include/staff/ticket-edit.inc.php
+++ b/include/staff/ticket-edit.inc.php
@@ -25,8 +25,17 @@ if ($_POST)
     ?>
     <tr><td>Client:</td><td>
         <div id="client-info">
-            <span id="client-name"><?php echo $user->getName(); ?></span>
-            <span id="client-email">&lt;<?php echo $user->getEmail(); ?>&gt;</span>
+            <a href="#" onclick="javascript:
+                $.userLookup('ajax.php/users/<?php echo $ticket->getOwnerId(); ?>/edit',
+                        function (user) {
+                            $('#client-name').text(user.name);
+                            $('#client-email').text(user.email);
+                        });
+                return false;
+                "><i class="icon-user"></i>
+            <span id="client-name"><?php echo Format::htmlchars($user->getName()); ?></span>
+            &lt;<span id="client-email"><?php echo $user->getEmail(); ?></span>&gt;
+            </a>
             <a class="action-button" style="float:none;overflow:inherit" href="#"
                 onclick="javascript:
                     $.userLookup('ajax.php/tickets/<?php echo $ticket->getId(); ?>/change-user',
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index a143ebb80fe42cf40fb984473e7fc42bfccc2cec..1c49b407308ba09a9487c4a465e9986a13db9c2f 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -8,8 +8,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="create">
  <input type="hidden" name="a" value="open">
  <h2>Open New Ticket</h2>
- <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
+ <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
+    <!-- This looks empty - but beware, with fixed table layout, the user
+         agent will usually only consult the cells in the first row to
+         construct the column widths of the entire toable. Therefore, the
+         first row needs to have two cells -->
+        <tr><td></td><td></td></tr>
         <tr>
             <th colspan="2">
                 <h4>New Ticket</h4>
@@ -27,8 +32,17 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr><td>Client:</td><td>
             <div id="client-info">
                 <input type="hidden" name="uid" id="uid" value="<?php echo $user->getId(); ?>" />
+            <a href="#" onclick="javascript:
+                $.userLookup('ajax.php/users/<?php echo $user->getId(); ?>/edit',
+                        function (user) {
+                            $('#client-name').text(user.name);
+                            $('#client-email').text(user.email);
+                        });
+                return false;
+                "><i class="icon-user"></i>
                 <span id="client-name"><?php echo $user->getName(); ?></span>
-                <span id="client-email">&lt;<?php echo $user->getEmail(); ?>&gt;</span>
+                &lt;<span id="client-email"><?php echo $user->getEmail(); ?></span>&gt;
+                </a>
                 <a class="action-button" style="float:none;overflow:inherit" href="#"
                     onclick="javascript:
                         $.userLookup('ajax.php/users/select/'+$('input#uid').val(),
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 275bd2c2be091714938351194cdf95b7adc332b6..bc6a25e3fd57d8d859f8dd6de2af2113e6235801 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -153,6 +153,7 @@ if($ticket->isOverdue())
                                     function (user) {
                                         $('#user-'+user.id+'-name').text(user.name);
                                         $('#user-'+user.id+'-email').text(user.email);
+                                        $('#user-'+user.id+'-phone').text(user.phone);
                                         $('#user-to-name').text(user.name);
                                         $('#user-to-email').text(user.email);
                                     });
@@ -191,7 +192,9 @@ if($ticket->isOverdue())
                 </tr>
                 <tr>
                     <th>Phone:</th>
-                    <td><?php echo $ticket->getPhoneNumber(); ?></td>
+                    <td>
+                        <span id="user-<?php echo $ticket->getOwnerId(); ?>-phone"><?php echo $ticket->getPhoneNumber(); ?></span>
+                    </td>
                 </tr>
                 <tr>
                     <th>Source:</th>
@@ -299,13 +302,13 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) {
         <td colspan="2">
             <table cellspacing="0" cellpadding="4" width="100%" border="0">
             <?php foreach($answers as $a) {
-                if (!$a->toString()) continue; ?>
+                if (!($v = $a->display())) continue; ?>
                 <tr>
                     <th width="100"><?php
     echo $a->getField()->get('label');
                     ?>:</th>
                     <td><?php
-    echo $a->toString();
+    echo $v;
                     ?></td>
                 </tr>
                 <?php } ?>
@@ -337,10 +340,20 @@ $tcount+= $ticket->getNumNotes();
            ?>
         <table class="thread-entry <?php echo $threadTypes[$entry['thread_type']]; ?>" cellspacing="0" cellpadding="1" width="940" border="0">
             <tr>
-                <th width="auto"><?php echo Format::db_datetime($entry['created']);?></th>
-                <th width="440"><span><?php echo $entry['title']; ?></span></th>
-                <th width="auto" class="textra" style="text-align:right"></th>
-                <th width="auto" class="tmeta"><?php echo Format::htmlchars($entry['poster']); ?></th>
+                <th colspan="4" width="100%">
+                <div>
+                    <span style="display:inline-block"><?php
+                        echo Format::db_datetime($entry['created']);?></span>
+                    <span style="display:inline-block;padding-left:1em" class="faded title"><?php
+                        echo Format::truncate($entry['title'], 100); ?></span>
+                    <span style="float:right;white-space:no-wrap;display:inline-block">
+                        <span style="vertical-align:middle;" class="textra"></span>
+                        <span style="vertical-align:middle;"
+                            class="tmeta faded title"><?php
+                            echo Format::htmlchars($entry['poster']); ?></span>
+                    </span>
+                </div>
+                </th>
             </tr>
             <tr><td colspan="4" class="thread-body" id="thread-id-<?php
                 echo $entry['id']; ?>"><div><?php
@@ -412,6 +425,7 @@ $tcount+= $ticket->getNumNotes();
                 </td>
                 <td>
                     <?php
+                    # XXX: Add user-to-name and user-to-email HTML ID#s
                     $to =sprintf('%s &lt;%s&gt;', $ticket->getName(), $ticket->getReplyToEmail());
                     $emailReply = (!isset($info['emailreply']) || $info['emailreply']);
                     ?>
@@ -918,8 +932,10 @@ $tcount+= $ticket->getNumNotes();
         Are you sure want to <b>unassign</b> ticket from <b><?php echo $ticket->getAssigned(); ?></b>?
     </p>
     <p class="confirm-action" style="display:none;" id="changeuser-confirm">
+        <p id="msg_warning">
+        <b><?php echo Format::htmlchars($ticket->getName()); ?></b> &lt;<?php echo $ticket->getEmail(); ?>&gt; will no longer have access to the ticket.
+        </p>
         Are you sure want to <b>change</b> ticket owner to <b><span id="newuser">this guy</span></b>?
-        <br><br><b><?php echo $ticket->getName(); ?></b> &lt;<?php echo $ticket->getEmail(); ?>&gt; will no longer have access to the ticket.
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
         <font color="red"><strong>Are you sure you want to DELETE this ticket?</strong></font>
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 16fc4e1e2ff2323f2fb7021707fb95f3b7b40a6d..e463e515d88a0afa7c7e013f57adf9f411d0dc9a 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -367,7 +367,7 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting..
                     $lc=Format::truncate($row['dept_name'],40);
                 }
                 $tid=$row['ticketID'];
-                $subject = Format::truncate($row['subject'],40);
+                $subject = Format::htmlchars(Format::truncate($row['subject'],40));
                 $threadcount=$row['thread_count'];
                 if(!strcasecmp($row['status'],'open') && !$row['isanswered'] && !$row['lock_id']) {
                     $tid=sprintf('<b>%s</b>',$tid);
diff --git a/include/upgrader/prereq.inc.php b/include/upgrader/prereq.inc.php
index ac2b0b2febf2d454604d5f03476ea9b83c6bbb11..98665cbc9dece47058ff2526164cb2cf4634fa3b 100644
--- a/include/upgrader/prereq.inc.php
+++ b/include/upgrader/prereq.inc.php
@@ -17,7 +17,9 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access D
                 <li class="<?php echo $upgrader->check_php()?'yes':'no'; ?>">
                 PHP v5.3 or greater - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li>
                 <li class="<?php echo $upgrader->check_mysql()?'yes':'no'; ?>">
-                MySQL v5.0 or greater - (<small><b><?php echo extension_loaded('mysql')?'module loaded':'missing!'; ?></b></small>)</li>
+                MySQLi extension for PHP - (<small><b><?php echo extension_loaded('mysqli')?'module loaded':'missing!'; ?></b></small>)</li>
+                <li class="<?php echo $upgrader->check_mysql_version()?'yes':'no'; ?>">
+                MySQL v5.0 or greater - (<small><b><?php echo db_version(); ?></b></small>)</li>
             </ul>
             <h3>Highly Recommended:</h3>
             We highly recommend that you follow the steps below.
diff --git a/main.inc.php b/main.inc.php
index 924f728886779c0cd668d07376cee0f745596a6b..7feb271e7743274b02b2227ac29e68f4e67f3f7e 100644
--- a/main.inc.php
+++ b/main.inc.php
@@ -23,8 +23,8 @@ if(isset($_SERVER['SCRIPT_NAME'])
 require('bootstrap.php');
 Bootstrap::loadConfig();
 Bootstrap::defineTables(TABLE_PREFIX);
-Bootstrap::loadCode();
 Bootstrap::i18n_prep();
+Bootstrap::loadCode();
 Bootstrap::connect();
 
 if(!($ost=osTicket::start()) || !($cfg = $ost->getConfig()))
diff --git a/scp/ajax.php b/scp/ajax.php
index a8a407acceb30705a62bc31bc74fd48f16ac3a42..397d1f44b563cb2d4e8221d7d8dd5f71229330b6 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -60,6 +60,8 @@ $dispatcher = patterns('',
     url('^/users', patterns('ajax.users.php:UsersAjaxAPI',
         url_get('^$', 'search'),
         url_get('^/(?P<id>\d+)$', 'getUser'),
+        url_post('^/(?P<id>\d+)$', 'updateUser'),
+        url_get('^/(?P<id>\d+)/edit$', 'editUser'),
         url_get('^/lookup$', 'getUser'),
         url_get('^/lookup/form$', 'getLookupForm'),
         url_post('^/lookup/form$', 'addUser'),
diff --git a/scp/css/scp.css b/scp/css/scp.css
index 1ed00d4dbb4fe6bf044bc506268ef787e4cac89f..d3eaceb6637dec2e8e7071a371c6c74635cee883 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -546,6 +546,17 @@ a.print {
     border-bottom:1px solid #ddd;
 }
 
+.form_table.fixed {
+    table-layout: fixed;
+    border-collapse: collapse;
+    width: 100%;
+}
+.form_table.fixed td {
+    width: 180px;
+}
+.form_table.fixed td + td {
+    width: auto;
+}
 
 td.multi-line {
     vertical-align:top;
@@ -792,21 +803,6 @@ h2 .reload {
     text-align:right;
 }
 
-#ticket_thread table th.tmeta {
-    font-weight:bold;
-    font-size:10pt;
-    color:#888;
-    text-align:right;
-    padding-right:15px;
-}
-
-#ticket_thread table th span {
-    font-weight:normal;
-    font-size:10pt;
-    color:#888;
-    padding-left:5px;
-}
-
 #ticket_thread > .message th {
     background:#C3D9FF;
 }
@@ -902,8 +898,8 @@ h2 .reload {
     border-top:none;
 }
 
-#response_options > table {
-    width:928px;
+#response_options > form > table {
+    table-layout: fixed;
 }
 
 #response_options > table td {
@@ -1023,6 +1019,7 @@ h2 .reload {
     position:absolute;
     top:0.3em;
     right:0.5em;
+    text-decoration:none;
 }
 
 .tip_shadow {
@@ -1057,7 +1054,6 @@ h2 .reload {
 .tip_menu li a {
     display:block;
     width:auto;
-    _width:0;
     float:left;
     padding:0 10px;
     border-right:1px solid #ddd;
diff --git a/scp/forms.php b/scp/forms.php
index ef697162bc848b23383310185dbc80eb1066e59b..5af2dd327781d18adca740b46ad654eb272929db 100644
--- a/scp/forms.php
+++ b/scp/forms.php
@@ -8,12 +8,15 @@ if($_REQUEST['id'] && !($form=DynamicForm::lookup($_REQUEST['id'])))
 
 if($_POST) {
     $fields = array('title', 'notes', 'instructions');
-    $required = array('subject');
+    $required = array('title');
     $max_sort = 0;
     switch(strtolower($_POST['do'])) {
         case 'update':
             foreach ($fields as $f)
-                if (isset($_POST[$f]))
+                if (in_array($f, $required) && !$_POST[$f])
+                    $errors[$f] = sprintf('%s is required',
+                        mb_convert_case($f, MB_CASE_TITLE));
+                elseif (isset($_POST[$f]))
                     $form->set($f, $_POST[$f]);
             $form->save(true);
             $names = array();
@@ -107,6 +110,8 @@ if($_POST) {
     }
     if ($errors)
         $errors['err'] = 'Unable to commit form. Check validation errors';
+    else
+        $msg = 'Custom form successfully updated';
 }
 
 $page='dynamic-forms.inc.php';
diff --git a/scp/staff.inc.php b/scp/staff.inc.php
index 0c835d46a780a5acf439af2268da039a73335905..359663a4c6fccd04dc466126c759d71a89acb5a4 100644
--- a/scp/staff.inc.php
+++ b/scp/staff.inc.php
@@ -49,7 +49,8 @@ require_once(INCLUDE_DIR.'class.csrf.php');
 if(!function_exists('staffLoginPage')) { //Ajax interface can pre-declare the function to  trap expired sessions.
     function staffLoginPage($msg) {
         global $ost, $cfg;
-        $_SESSION['_staff']['auth']['dest']=THISURI;
+        $_SESSION['_staff']['auth']['dest'] =
+            '/' . ltrim($_SERVER['REQUEST_URI'], '/');
         $_SESSION['_staff']['auth']['msg']=$msg;
         require(SCP_DIR.'login.php');
         exit;
diff --git a/scp/upgrade.php b/scp/upgrade.php
index 072ca79781e9fb1f9737a7350851fe37ad944941..5664c63f5167c231da937f962a14f759ec374deb 100644
--- a/scp/upgrade.php
+++ b/scp/upgrade.php
@@ -35,7 +35,7 @@ if($_POST && $_POST['s'] && !$upgrader->isAborted()) {
             }
             break;
         case 'upgrade': //Manual upgrade.... when JS (ajax) is not supported.
-            if($upgrader->getPendingTask()) {
+            if($upgrader->getTask()) {
                 $upgrader->doTask();
             } elseif($ost->isUpgradePending() && $upgrader->isUpgradable()) {
                 $upgrader->upgrade();
diff --git a/setup/css/wizard.css b/setup/css/wizard.css
index c92b05447a6acb9992ea21be3ce13581840784a9..2dd02eced36596ac0fb570c21a6205e58de00023 100644
--- a/setup/css/wizard.css
+++ b/setup/css/wizard.css
@@ -1,3 +1,4 @@
+@import url("../../css/font-awesome.min.css");
 body { background: url('../images/background.jpg?1312906017') top left repeat-x white; font-family: arial, helvetica, sans-serif; font-size: 10pt; color: #000; margin: 0; padding: 0; }
 
 #wizard { background: #fff; width: 800px; margin: 30px auto; padding: 10px; border: 1px solid #2a67ac; border-right: 2px solid #2a67ac; border-bottom: 3px solid #2a67ac; overflow: hidden; margin-bottom:5px;}
@@ -60,13 +61,117 @@ form .row input { background: #fff; height: 22px; padding: 0; line-height: 22px;
 form .row label, form .row span { display: block; float: left; width: 150px; line-height: 24px; }
 form .row span { width: 600px; color: #666666; }
 
-.tip_box { display: block; height: 30px; position: absolute; z-index: 1000; }
-
-.tip_arrow { display: block; position: absolute; top: 5px; left: -11px; width: 12px; z-index: 700; }
-
-.tip_content { height: auto !important; height: 20px; min-height: 20px; padding: 10px 5px 5px 5px; border: 1px solid #666; background: #fff; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; z-index: 500; position: absolute; top: 0; left: -1px; width: 300px; }
-
-.tip_close { position: absolute; text-decoration: none; left: 100%; top: 0; margin-left: -12px; }
+.tip_box {
+    display:block;
+    height:30px;
+    position:absolute;
+    z-index:1000;
+}
+
+.tip_arrow {
+    display:block;
+    position:absolute;
+    top:5px;
+    left:-12px;
+    width:12px;
+    z-index:700;
+}
+
+.tip_box.right .tip_arrow {
+    top: 5px;
+    right: -12px;
+    left: auto;
+}
+
+.flip-x {
+    -moz-transform: scaleX(-1);
+    -o-transform: scaleX(-1);
+    -webkit-transform: scaleX(-1);
+    transform: scaleX(-1);
+    filter: FlipH;
+    -ms-filter: "FlipH";
+}
+
+.tip_content {
+    height:auto !important;
+    height:20px;
+    min-height:20px;
+    padding:10px;
+    border:1px solid #666;
+    background:#fff;
+    -moz-border-radius:5px;
+    -webkit-border-radius:5px;
+    border-radius:5px;
+    -moz-box-shadow: 3px 3px 3px #666;
+    -webkit-box-shadow: 3px 3px 3px #666;
+    box-shadow: 3px 3px 3px #666;
+    z-index:500;
+    position:absolute;
+    top:0;
+    left:-1px;
+    min-width:300px;
+    line-height: 1.15rem;
+}
+
+.tip_content .links {
+    margin-top: 0.7em;
+    padding-top: 0.4em;
+    border-top: 1px solid #ddd;
+}
+
+.tip_content .links a {
+    color: #548dd4;
+}
+
+.tip_content hr {
+
+    color: #ddd;
+    background-color: #ddd;
+    height: 1px;
+    border: 0;
+    padding: 0;
+    margin: 0.2em 0;
+    width: 100%;
+}
+
+.tip_close {
+    position:absolute;
+    top:0.3em;
+    right:0.5em;
+    text-decoration:none;
+}
+
+.tip_shadow {
+    display:none;
+    background:#000;
+    filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius=3,MakeShadow=true,ShadowOpacity=0.60);
+    -ms-filter: "progid:DXImageTransform.Microsoft.Blur(PixelRadius=3,MakeShadow=true,ShadowOpacity=0.60)";
+    zoom: 1;
+    position:absolute;
+    z-index:200;
+    top:0;
+    left:0;
+    width:auto !important;
+    width:310px;
+}
+
+.tip_content h1 {
+    font-size: 13pt;
+    margin-top: 0;
+    margin-bottom: 0.5em;
+    padding-bottom: 0.2em;
+    border-bottom: 1px solid #ddd;
+    padding-right: 1.5em;
+}
+
+i.help-tip {
+    vertical-align: inherit;
+    color: #aaa;
+}
+i.help-tip:hover {
+    color: black;
+    cursor: pointer;
+}
 
 #overlay { display: none; position: fixed; background: #000; z-index: 2000; }
 
diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php
index b735e031afbf9f978524ac71651d0d770d814743..d57fa0b449095e9bb88a1cb3b9004dd0a1325c5a 100644
--- a/setup/inc/class.installer.php
+++ b/setup/inc/class.installer.php
@@ -205,7 +205,7 @@ class Installer extends SetupWizard {
                 'default_dept_id'=>$dept_id_1, 'default_sla_id'=>$sla_id_1,
                 'default_template_id'=>$template_id_1,
                 'admin_email'=>$vars['admin_email'],
-                'schema_signature'=>$signature,
+                'schema_signature'=>$streams['core'],
                 'helpdesk_url'=>URL,
                 'helpdesk_title'=>$vars['name']);
             $config = new Config('core');
@@ -217,6 +217,14 @@ class Installer extends SetupWizard {
             $company = new Company();
             $company->getForm()->setAnswer('name', $vars['name']);
             $company->getForm()->save();
+
+			foreach ($streams as $stream=>$signature) {
+				if ($stream != 'core') {
+                    $config = new Config($stream);
+                    if (!$config->update('schema_signature', $signature))
+                        $this->errors['err']='Unable to create config settings (#8)';
+				}
+			}
         }
 
         if($this->errors) return false; //Abort on internal errors.
diff --git a/setup/inc/install-prereq.inc.php b/setup/inc/install-prereq.inc.php
index 37579a32a1ac976e0800511612f29029e1f23be3..90e0f176ac0f0cb138b136a2e76ff16427f73898 100644
--- a/setup/inc/install-prereq.inc.php
+++ b/setup/inc/install-prereq.inc.php
@@ -17,7 +17,7 @@ if(!defined('SETUPINC')) die('Kwaheri!');
                 <li class="<?php echo $installer->check_php()?'yes':'no'; ?>">
                 PHP v5.3 or greater - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li>
                 <li class="<?php echo $installer->check_mysql()?'yes':'no'; ?>">
-                MySQL v5.0 or greater - (<small><b><?php echo extension_loaded('mysql')?'module loaded':'missing!'; ?></b></small>)</li>
+                MySQLi extension for PHP - (<small><b><?php echo extension_loaded('mysqli')?'module loaded':'missing!'; ?></b></small>)</li>
             </ul>
             <h3>Recommended:</h3>
             You can use osTicket without these, but you may not be able to use all features.
diff --git a/setup/inc/install.inc.php b/setup/inc/install.inc.php
index c9f16384398a75e5d6c919b2fdf08be948ff7189..e2d6a5a0f3b87b28f7466a91780b2d5d932956fb 100644
--- a/setup/inc/install.inc.php
+++ b/setup/inc/install.inc.php
@@ -17,13 +17,13 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbho
                 <div class="row">
                     <label>Helpdesk Name:</label>
                     <input type="text" name="name" size="45" tabindex="1" value="<?php echo $info['name']; ?>">
-                    <a class="tip" href="#helpdesk_name">?</a>
+                    <a class="tip" href="#helpdesk_name"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['name']; ?></font>
                 </div>
                 <div class="row">
                     <label>Default Email:</label>
                     <input type="text" name="email" size="45" tabindex="2" value="<?php echo $info['email']; ?>">
-                    <a class="tip" href="#system_email">?</a>
+                    <a class="tip" href="#system_email"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['email']; ?></font>
                 </div>
 
@@ -32,37 +32,37 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbho
                 <div class="row">
                     <label>First Name:</label>
                     <input type="text" name="fname" size="45" tabindex="3" value="<?php echo $info['fname']; ?>">
-                    <a class="tip" href="#first_name">?</a>
+                    <a class="tip" href="#first_name"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['fname']; ?></font>
                 </div>
                 <div class="row">
                     <label>Last Name:</label>
                     <input type="text" name="lname" size="45" tabindex="4" value="<?php echo $info['lname']; ?>">
-                    <a class="tip" href="#last_name">?</a>
+                    <a class="tip" href="#last_name"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['lname']; ?></font>
                 </div>
                 <div class="row">
                     <label>Email Address:</label>
                     <input type="text" name="admin_email" size="45" tabindex="5" value="<?php echo $info['admin_email']; ?>">
-                    <a class="tip" href="#email">?</a>
+                    <a class="tip" href="#email"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['admin_email']; ?></font>
                 </div>
                 <div class="row">
                     <label>Username:</label>
                     <input type="text" name="username" size="45" tabindex="6" value="<?php echo $info['username']; ?>" autocomplete="off">
-                    <a class="tip" href="#username">?</a>
+                    <a class="tip" href="#username"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['username']; ?></font>
                 </div>
                 <div class="row">
                     <label> Password:</label>
                     <input type="password" name="passwd" size="45" tabindex="7" value="<?php echo $info['passwd']; ?>" autocomplete="off">
-                    <a class="tip" href="#password">?</a>
+                    <a class="tip" href="#password"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['passwd']; ?></font>
                 </div>
                 <div class="row">
                     <label>Retype Password:</label>
                     <input type="password" name="passwd2" size="45" tabindex="8" value="<?php echo $info['passwd2']; ?>">
-                    <a class="tip" href="#password2">?</a>
+                    <a class="tip" href="#password2"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['passwd2']; ?></font>
                 </div>
 
@@ -71,31 +71,31 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbho
                 <div class="row">
                     <label>MySQL Table Prefix:</label>
                     <input type="text" name="prefix" size="45" tabindex="9" value="<?php echo $info['prefix']; ?>">
-                    <a class="tip" href="#db_prefix">?</a>
+                    <a class="tip" href="#db_prefix"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['prefix']; ?></font>
                 </div>
                 <div class="row">
                     <label>MySQL Hostname:</label>
                     <input type="text" name="dbhost" size="45" tabindex="10" value="<?php echo $info['dbhost']; ?>">
-                    <a class="tip" href="#db_host">?</a>
+                    <a class="tip" href="#db_host"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['dbhost']; ?></font>
                 </div>
                 <div class="row">
                     <label>MySQL Database:</label>
                     <input type="text" name="dbname" size="45" tabindex="11" value="<?php echo $info['dbname']; ?>">
-                    <a class="tip" href="#db_schema">?</a>
+                    <a class="tip" href="#db_schema"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['dbname']; ?></font>
                 </div>
                 <div class="row">
                     <label>MySQL Username:</label>
                     <input type="text" name="dbuser" size="45" tabindex="12" value="<?php echo $info['dbuser']; ?>">
-                    <a class="tip" href="#db_user">?</a>
+                    <a class="tip" href="#db_user"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['dbuser']; ?></font>
                 </div>
                 <div class="row">
                     <label>MySQL Password:</label>
                     <input type="password" name="dbpass" size="45" tabindex="13" value="<?php echo $info['dbpass']; ?>">
-                    <a class="tip" href="#db_password">?</a>
+                    <a class="tip" href="#db_password"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['dbpass']; ?></font>
                 </div>
                 <br>
diff --git a/setup/js/tips.js b/setup/js/tips.js
index 9fd2007e5a67cd2e98b85b3ef1df81deb092dfb4..af111e21ed7171f4e33b4f45256fc09be29d70d0 100644
--- a/setup/js/tips.js
+++ b/setup/js/tips.js
@@ -20,54 +20,69 @@ jQuery(function($) {
     })
     .live('mouseover click', function(e) {
         e.preventDefault();
-        var tip_num = this.rel;
 
-        if($('.' + tip_num).length == 0) {
+        var elem = $(this),
+            pos = elem.offset(),
+            y_pos = pos.top - 8,
+            x_pos = pos.left + elem.width() + 16,
+            tip_arrow = $('<img>')
+                .attr('src', './images/tip_arrow.png')
+                .addClass('tip_arrow'),
+            tip_box = $('<div>')
+                .addClass('tip_box'),
+            tip_content = $('<div>')
+                .append('<a href="#" class="tip_close"><i class="icon-remove-circle"></i></a>')
+                .addClass('tip_content'),
+            the_tip = tip_box
+                .append(tip_content.append(tip_arrow))
+                .css({
+                    "top":y_pos + "px",
+                    "left":x_pos + "px"
+                }),
+            tip_timer = setTimeout(function() {
+                $('.tip_box').remove();
+                $('body').append(the_tip.hide().fadeIn());
+                if ($(window).width() < tip_content.outerWidth() + the_tip.position().left) {
+                    the_tip.css({'left':x_pos-tip_content.outerWidth()-40+'px'});
+                    tip_box.addClass('right');
+                    tip_arrow.addClass('flip-x');
+                }
+            }, 500);
 
-            var elem = $(this),
-                pos = elem.offset(),
-                y_pos = pos.top - 12,
-                x_pos = pos.left + elem.width() + 20,
-                tip_arrow = $('<img>')
-                    .attr('src', './images/tip_arrow.png')
-                    .addClass('tip_arrow'),
-                tip_box = $('<div>')
-                    .addClass('tip_box'),
-                tip_content = $('<div>')
-                    .append('<a href="#" class="tip_close">x</a>')
-                    .addClass('tip_content'),
-                the_tip = tip_box
-                    .append(tip_arrow)
-                    .append(tip_content)
-                    .css({
-                        "top":y_pos + "px",
-                        "left":x_pos + "px"
-                    })
-                    .addClass(tip_num),
-                tip_timer = setTimeout(function() {
-                    $('.tip_box').remove();
-                    $('body').append(the_tip.hide().fadeIn());
-                }, 500);
+        elem.live('mouseout', function() {
+            clearTimeout(tip_timer);
+        });
 
-            elem.live('mouseout', function() {
+        getHelpTips().then(function(tips) {
+            var section = tips[elem.attr('href').substr(1)];
+            if (!section) {
+                elem.remove();
                 clearTimeout(tip_timer);
-            });
-
-            getHelpTips().then(function(tips) {
-                var section = tips[elem.attr('href').substr(1)];
-                if (!section) {
-                    elem.remove();
-                    clearTimeout(tip_timer);
-                    return;
-                }
-                tip_content.append(
-                    $('<b>').append(section.title))
-                    .append(section.content);
-            });
-            $('.' + tip_num + ' .tip_shadow').css({
-                "height":$('.' + tip_num).height() + 5
-            });
-        }
+                return;
+            }
+            tip_content.append(
+                $('<h1>')
+                    .append('<i class="icon-info-sign faded"> ')
+                    .append(section.title)
+                ).append(section.content);
+            if (section.links) {
+                var links = $('<div class="links">');
+                $.each(section.links, function(i,l) {
+                    var icon = l.href.match(/^http/)
+                        ? 'icon-external-link' : 'icon-share-alt';
+                    links.append($('<div>')
+                        .append($('<a>')
+                            .html(l.title)
+                            .prepend('<i class="'+icon+'"></i> ')
+                            .attr('href', l.href).attr('target','_blank'))
+                    );
+                });
+                tip_content.append(links);
+            }
+        });
+        $('.tip_shadow', the_tip).css({
+            "height":the_tip.height() + 5
+        });
     });
 
     $('body')