diff --git a/file.php b/file.php
index 994b77a0c2256a0218a27ffebb01b1c7d77a344b..00193f7a0b4b810964d9388a2a9a893f00406907 100644
--- a/file.php
+++ b/file.php
@@ -28,10 +28,11 @@ if (!$_GET['key']
 
 // Get the object type the file is attached to
 $type = '';
+$attachment = null;
 if ($_GET['id']
-        && ($a=$file->attachments->findFirst(array(
+        && ($attachment=$file->attachments->findFirst(array(
                     'id' => $_GET['id']))))
-    $type = $a->type;
+    $type = $attachment->type;
 
 // Enforce security settings if enabled.
 if ($cfg->isAuthRequiredForFiles()
@@ -62,7 +63,9 @@ if ($file->verifySignature($_GET['signature'], $_GET['expires'])) {
             return $file->display($s);
 
         // Download the file..
-        $file->download(@$_GET['disposition'] ?: false, $_GET['expires']);
+        $filename = $attachment ? $attachment->name : $file->getName();
+        $disposition = @$_GET['disposition'] ?: false;
+        $file->download($filename, $disposition, @$_GET['expires']);
     }
     catch (Exception $ex) {
         Http::response(500, 'Unable to find that file: '.$ex->getMessage());
diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php
index 88b11bee010075c07b8293d260ec09f284569891..cd85cf613cbf696280ae79182946d6c9e6b46fea 100644
--- a/include/ajax.tasks.php
+++ b/include/ajax.tasks.php
@@ -84,8 +84,10 @@ class TasksAjaxAPI extends AjaxController {
     function add($tid=0, $vars=array()) {
         global $thisstaff;
 
-        if ($tid)
-          $originalTask = Task::lookup($tid);
+        if ($tid) {
+            $vars = array_merge($_SESSION[':form-data'], $vars);
+            $originalTask = Task::lookup($tid);
+        }
         else
           unset($_SESSION[':form-data']);
 
@@ -111,7 +113,7 @@ class TasksAjaxAPI extends AjaxController {
                 if ($desc
                         && $desc->isAttachmentsEnabled()
                         && ($attachments=$desc->getWidget()->getAttachments()))
-                    $vars['cannedattachments'] = $attachments->getClean();
+                    $vars['files'] = $attachments->getFiles();
                 $vars['staffId'] = $thisstaff->getId();
                 $vars['poster'] = $thisstaff;
                 $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
@@ -809,9 +811,9 @@ class TasksAjaxAPI extends AjaxController {
             switch ($_POST['a']) {
             case 'postnote':
                 $vars = $_POST;
-                $attachments = $note_form->getField('attachments')->getClean();
-                $vars['cannedattachments'] = array_merge(
-                    $vars['cannedattachments'] ?: array(), $attachments);
+                $attachments = $note_form->getField('attachments')->getFiles();
+                $vars['files'] = array_merge(
+                    $vars['files'] ?: array(), $attachments);
                 if(($note=$task->postNote($vars, $errors, $thisstaff))) {
                     $msg=__('Note posted successfully');
                     // Clear attachment list
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 4f5e7a62bcc55939dd2c788b86ee880a4fe37489..2864efda74a09e4c5ceed50e699f15edbff6f5ef 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -1382,7 +1382,7 @@ function refer($tid, $target=null) {
                 && ($f=$iform->getField('duedate'))) {
             $f->configure('max', Misc::db2gmtime($ticket->getEstDueDate()));
         }
-
+        $vars = array_merge($_SESSION[':form-data'], $vars);
 
         if ($_POST) {
             Draft::deleteForNamespace(
@@ -1408,7 +1408,7 @@ function refer($tid, $target=null) {
                 if ($desc
                         && $desc->isAttachmentsEnabled()
                         && ($attachments=$desc->getWidget()->getAttachments()))
-                    $vars['cannedattachments'] = $attachments->getClean();
+                    $vars['files'] = $attachments->getFiles();
                 $vars['staffId'] = $thisstaff->getId();
                 $vars['poster'] = $thisstaff;
                 $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
@@ -1490,9 +1490,9 @@ function refer($tid, $target=null) {
             $vars = $_POST;
             switch ($_POST['a']) {
             case 'postnote':
-                $attachments = $note_attachments_form->getField('attachments')->getClean();
-                $vars['cannedattachments'] = array_merge(
-                    $vars['cannedattachments'] ?: array(), $attachments);
+                $attachments = $note_attachments_form->getField('attachments')->getFiles();
+                $vars['files'] = array_merge(
+                    $vars['files'] ?: array(), $attachments);
                 if (($note=$task->postNote($vars, $errors, $thisstaff))) {
                     $msg=__('Note posted successfully');
                     // Clear attachment list
@@ -1506,9 +1506,9 @@ function refer($tid, $target=null) {
                 }
                 break;
             case 'postreply':
-                $attachments = $reply_attachments_form->getField('attachments')->getClean();
-                $vars['cannedattachments'] = array_merge(
-                    $vars['cannedattachments'] ?: array(), $attachments);
+                $attachments = $reply_attachments_form->getField('attachments')->getFiles();
+                $vars['files'] = array_merge(
+                    $vars['files'] ?: array(), $attachments);
                 if (($response=$task->postReply($vars, $errors))) {
                     $msg=__('Update posted successfully');
                     // Clear attachment list
diff --git a/include/class.attachment.php b/include/class.attachment.php
index bcda6d08af5d757c01b691b78a29b28f03def623..bb5e8e292f11aa0342f82299f7d2b5c1bec04dd8 100644
--- a/include/class.attachment.php
+++ b/include/class.attachment.php
@@ -60,6 +60,10 @@ class Attachment extends VerySimpleModel {
         return $this->name ?: $this->file->name;
     }
 
+    function getName() {
+        return $this->getFilename();
+    }
+
     function getHashtable() {
         return $this->ht;
     }
@@ -108,16 +112,15 @@ extends InstrumentedList {
      */
     function keepOnlyFileIds($ids, $inline=false, $lang=false) {
         if (!$ids) $ids = array();
-        $new = array_flip($ids);
         foreach ($this as $A) {
-            if (!isset($new[$A->file_id]) && $A->lang == $lang && $A->inline == $inline)
+            if (!isset($ids[$A->file_id]) && $A->lang == $lang && $A->inline == $inline)
                 // Not in the $ids list, delete
                 $this->remove($A);
-            unset($new[$A->file_id]);
+            unset($ids[$A->file_id]);
         }
         $attachments = array();
         // Format $new for upload() with new name
-        foreach ($new as $id=>$name) {
+        foreach ($ids as $id=>$name) {
             $attachments[] = array(
                     'id' => $id,
                     'name' => $name
@@ -134,7 +137,7 @@ extends InstrumentedList {
         foreach ($files as $file) {
             if (is_numeric($file))
                 $fileId = $file;
-            elseif (is_array($file) && isset($file['id']))
+            elseif (is_array($file) && isset($file['id']) && $file['id'])
                 $fileId = $file['id'];
             elseif (isset($file['tmp_name']) && ($F = AttachmentFile::upload($file)))
                 $fileId = $F->getId();
diff --git a/include/class.canned.php b/include/class.canned.php
index 21cd9aa6367f2b4ec81959d1790177fbb751ad65..388420c5f541a7667f7d3c33a020df5000a68b36 100644
--- a/include/class.canned.php
+++ b/include/class.canned.php
@@ -138,7 +138,7 @@ extends VerySimpleModel {
 
                 $resp['files'] = array();
                 foreach ($this->getAttachedFiles(!$html) as $file) {
-                    $_SESSION[':cannedFiles'][$file->id] = 1;
+                    $_SESSION[':cannedFiles'][$file->id] = $file->name;
                     $resp['files'][] = array(
                         'id' => $file->id,
                         'name' => $file->name,
diff --git a/include/class.file.php b/include/class.file.php
index d880840861b09065a34cd275ebb726773d82e105..e61b5afe76516fcecfd880359bb6193c13164b4e 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -248,7 +248,7 @@ class AttachmentFile extends VerySimpleModel {
         return hash_hmac('sha1', implode("\n", $pieces), SECRET_SALT);
     }
 
-    function download($disposition=false, $expires=false) {
+    function download($name=false, $disposition=false, $expires=false) {
         $disposition = $disposition ?: 'inline';
         $bk = $this->open();
         if ($bk->sendRedirectUrl($disposition))
@@ -258,7 +258,7 @@ class AttachmentFile extends VerySimpleModel {
         $type = $this->getType() ?: 'application/octet-stream';
         if (isset($_REQUEST['overridetype']))
             $type = $_REQUEST['overridetype'];
-        Http::download($this->getName(), $type, null, 'inline');
+        Http::download($name ?: $this->getName(), $type, null, 'inline');
         header('Content-Length: '.$this->getSize());
         $this->sendData(false);
         exit();
diff --git a/include/class.filter.php b/include/class.filter.php
index 311bfb91dddc179ed9725cb4a1dccdc18a016ef0..5066eb002df1e940167bc5abdcea2cbae8b7bc1d 100755
--- a/include/class.filter.php
+++ b/include/class.filter.php
@@ -137,8 +137,10 @@ extends VerySimpleModel {
         if ($val)
             $this->flags |= $flag;
         else
-            $this->flags &= ~$flag;
-        $this->save();
+            $this->ht['flags'] &= ~$flag;
+        $vars['rules']= $this->getRules();
+        $this->ht['pass'] = true;
+        $this->update($this->ht, $errors);
     }
 
     function stopOnMatch() {
@@ -490,6 +492,10 @@ extends VerySimpleModel {
     }
 
     function validate_actions($vars, &$errors) {
+        //allow the save if it is to set a filter flag
+        if ($vars['pass'])
+            return true;
+
         if (!is_array(@$vars['actions']))
             return;
       foreach ($vars['actions'] as $sort=>$v) {
@@ -517,26 +523,30 @@ extends VerySimpleModel {
                   }
               }
           }
-          switch ($action->ht['type']) {
-            case 'dept':
-              $dept = Dept::lookup($config['dept_id']);
-              if (!$dept || !$dept->isActive()) {
-                $errors['err'] = sprintf(__('Unable to save: Please choose an active %s'), 'Department');
-              }
-              break;
-            case 'topic':
-              $topic = Topic::lookup($config['topic_id']);
-              if (!$topic || !$topic->isActive()) {
-                $errors['err'] = sprintf(__('Unable to save: Please choose an active %s'), 'Help Topic');
-              }
-              break;
-            default:
-              foreach ($config as $key => $value) {
-                if (!$value) {
-                  $errors['err'] = sprintf(__('Unable to save: Please insert a value for %s'), ucfirst($action->ht['type']));
-                }
+
+          // do not throw an error if we are deleting an action
+          if (substr($v, 0, 1) != 'D') {
+              switch ($action->ht['type']) {
+                case 'dept':
+                  $dept = Dept::lookup($config['dept_id']);
+                  if (!$dept || !$dept->isActive()) {
+                    $errors['err'] = sprintf(__('Unable to save: Please choose an active %s'), 'Department');
+                  }
+                  break;
+                case 'topic':
+                  $topic = Topic::lookup($config['topic_id']);
+                  if (!$topic || !$topic->isActive()) {
+                    $errors['err'] = sprintf(__('Unable to save: Please choose an active %s'), 'Help Topic');
+                  }
+                  break;
+                default:
+                  foreach ($config as $key => $value) {
+                    if (!$value) {
+                      $errors['err'] = sprintf(__('Unable to save: Please insert a value for %s'), ucfirst($action->ht['type']));
+                    }
+                  }
+                  break;
               }
-              break;
           }
       }
       return count($errors) == 0;
diff --git a/include/class.forms.php b/include/class.forms.php
index b2c04d6c514e610d9692728dd5c7a66e172d8dc1..4fb4de4609802681c71b57d2c9b8009d66d0aa77 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -1974,10 +1974,25 @@ class DatetimeField extends FormField {
             'w' => _N('week', 'weeks', $count),
             'm' => _N('month', 'months', $count),
         );
-
         return $i ? $intervals[$i] : $intervals;
     }
 
+    static function periods($period='') {
+        $periods = array(
+                'td' => __('Today'),
+                'yd' => __('Yesterday'),
+                'tw' => __('This Week'),
+                'tm' => __('This Month'),
+                'tq' => __('This Quater'),
+                'ty' => __('This Year'),
+                'lw' => __('Last Week'),
+                'lm' => __('Last Month'),
+                'lq' => __('Last Quater'),
+                'ly' => __('Last Year'),
+        );
+        return $period ? $periods[$period] : $periods;
+    }
+
     // Get php DatateTime object of the field  - null if value is empty
     function getDateTime($value=null) {
         return Format::parseDateTime($value ?: $this->value);
@@ -2190,6 +2205,7 @@ class DatetimeField extends FormField {
             'before' =>     __('before'),
             'after' =>      __('after'),
             'between' =>    __('between'),
+            'period' =>     __('period'),
             'ndaysago' =>   __('in the last n days'),
             'ndays' =>      __('in the next n days'),
             'future' =>     __('in the future'),
@@ -2239,6 +2255,9 @@ class DatetimeField extends FormField {
                     'right' => new DatetimeField(),
                 ),
             )),
+            'period' => array('ChoiceField', array(
+                'choices' => self::periods(),
+            )),
             'ndaysago' => array('InlineformField', array('form'=>$nday_form())),
             'ndays' => array('InlineformField', array('form'=>$nday_form())),
             'distfut' => array('InlineformField', array('form'=>$nday_form())),
@@ -2247,6 +2266,8 @@ class DatetimeField extends FormField {
     }
 
     function getSearchQ($method, $value, $name=false) {
+        global $cfg;
+
         static $intervals = array(
             'm' => 'MONTH',
             'w' => 'WEEK',
@@ -2257,10 +2278,9 @@ class DatetimeField extends FormField {
         $name = $name ?: $this->get('name');
         $now = SqlFunction::NOW();
         $config = $this->getConfiguration();
-
        if (is_int($value))
           $value = DateTime::createFromFormat('U', !$config['gmt'] ? Misc::gmtime($value) : $value) ?: $value;
-       elseif (is_string($value))
+       elseif (is_string($value) && strlen($value) > 2)
            $value = Format::parseDateTime($value) ?: $value;
 
         switch ($method) {
@@ -2322,6 +2342,27 @@ class DatetimeField extends FormField {
             return new Q(array(
                 "{$name}__gte" => $now->plus($interval),
             ));
+        case 'period':
+            // Get the period range boundaries - timezone doesn't matter
+            $period = Misc::date_range($value, Misc::gmtime('now'));
+            $tz = new DateTimeZone($cfg->getTimezone());
+            // Get datetime boundaries in user's effective timezone
+            $tz = new DateTimeZone($cfg->getTimezone());
+            $start = new DateTime($period->start->format('Y-m-d H:i:s'),
+                    $tz);
+            $end = new DateTime($period->end->format('Y-m-d H:i:s'), $tz);
+            // Convert boundaries to db time
+            $dbtz = new DateTimeZone($cfg->getDbTimezone());
+            $start->setTimezone($dbtz);
+            $end->setTimezone($dbtz);
+            // Set the range
+            return new Q(array(
+                "{$name}__range" => array(
+                    $start->format('Y-m-d H:i:s'),
+                    $end->format('Y-m-d H:i:s')
+                    )
+                ));
+            break;
         default:
             return parent::getSearchQ($method, $value, $name);
         }
@@ -2347,6 +2388,8 @@ class DatetimeField extends FormField {
             return __('%1$s is in the future');
         case 'past':
             return __('%1$s is in the past');
+        case 'period':
+            return __('%1$s is %2$s');
         default:
             return parent::describeSearchMethod($method);
         }
@@ -2375,6 +2418,8 @@ class DatetimeField extends FormField {
             case 'before':
             case 'after':
                 return sprintf($desc, $name, $this->toString($value));
+            case 'period':
+                return sprintf($desc, $name, self::periods($value) ?: $value);
             default:
                 return parent::describeSearch($method, $value, $name);
         }
@@ -3268,7 +3313,7 @@ class FileUploadField extends FormField {
         $id = $F->getId();
 
         // This file is allowed for attachment in this session
-        $_SESSION[':uploadedFiles'][$id] = 1;
+        $_SESSION[':uploadedFiles'][$id] = $F->getName();
 
         return $id;
     }
@@ -3357,6 +3402,10 @@ class FileUploadField extends FormField {
                 if ($a && ($f=$a->getFile()))
                     $files[] = $f;
             }
+
+            foreach (@$this->getClean() as $key => $value)
+                $files[] = array('id' => $key, 'name' => $value);
+
             $this->files = $files;
         }
         return $this->files;
@@ -3427,9 +3476,7 @@ class FileUploadField extends FormField {
     }
 
     function parse($value) {
-        // Values in the database should be integer file-ids
-        return array_map(function($e) { return (int) $e; },
-            $value ?: array());
+        return $value;
     }
 
     function to_php($value) {
@@ -4400,13 +4447,7 @@ class FileUploadWidget extends Widget {
         );
         $maxfilesize = ($config['size'] ?: 1048576) / 1048576;
         $files = array();
-        $new = array_flip($this->field->getClean());
-
-        //get file ids stored in session when creating tickets/tasks from thread
-        //XXX: This shouldn't be here!!
-        if (!$new && is_array($_SESSION[':form-data'])
-                  && array_key_exists($this->field->get('name'), $_SESSION[':form-data']))
-          $new = $_SESSION[':form-data'][$this->field->get('name')];
+        $new = $this->field->getClean();
 
         foreach ($this->field->getAttachments() as $att) {
             unset($new[$att->file_id]);
@@ -4425,6 +4466,7 @@ class FileUploadWidget extends Widget {
             $F = AttachmentFile::objects()
                 ->filter(array('id__in' => array_keys($new)))
                 ->all();
+
             foreach ($F as $f) {
                 $f->tmp_name = $new[$f->getId()];
                 $files[] = array(
@@ -4473,7 +4515,7 @@ class FileUploadWidget extends Widget {
             foreach (AttachmentFile::format($_FILES[$this->name]) as $file) {
                 try {
                     $F = $this->field->uploadFile($file);
-                    $ids[] = $F->getId();
+                    $ids[$F->getId()] = $F->getName();
                 }
                 catch (FileUploadError $ex) {}
             }
@@ -4487,38 +4529,37 @@ class FileUploadWidget extends Widget {
         if (!($files = parent::getValue()))
             return array();
 
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $_files = array();
+            foreach ($files as $info) {
+                if (@list($id, $name) = explode(',', $info, 2))
+                    $_files[$id] = $name;
+            }
+            $files = $_files;
+        }
+
         $allowed = array();
         // Files already attached to the field are allowed
         foreach ($this->field->getFiles() as $F) {
             // FIXME: This will need special porting in v1.10
-            $allowed[$F->id] = 1;
+            $allowed[$F->id] = $F->getName();
         }
 
         // New files uploaded in this session are allowed
         if (isset($_SESSION[':uploadedFiles']))
             $allowed += $_SESSION[':uploadedFiles'];
 
-        // Files attached to threads where we are creating tasks/tickets are allowed
-        if (isset($_SESSION[':form-data'][$this->field->get('name')])) {
-          foreach ($_SESSION[':form-data'][$this->field->get('name')] as $key => $value)
-            $allowed[$key] = $value;
-        }
-
         // Canned attachments initiated by this session
         if (isset($_SESSION[':cannedFiles']))
            $allowed += $_SESSION[':cannedFiles'];
 
         // Parse the files and make sure it's allowed.
-        foreach ($files as $info) {
-            @list($id, $name) = explode(',', $info, 2);
+        foreach ($files as $id => $name) {
             if (!isset($allowed[$id]))
                 continue;
 
             // Keep the values as the IDs
-            if ($name)
-                $ids[$name] = $id;
-            else
-                $ids[] = $id;
+            $ids[$id] = $name;
         }
 
         return $ids;
diff --git a/include/class.misc.php b/include/class.misc.php
index 57c18a8df5f0456bd8cce201c7c003ae52fdcee4..65b1c357a677707a480e4819eb21f4796c465713 100644
--- a/include/class.misc.php
+++ b/include/class.misc.php
@@ -143,6 +143,85 @@ class Misc {
         return ((float)$usec + (float)$sec);
     }
 
+    // Date range for the period in a given time
+    function date_range($period, $time=false) {
+        $time = $time ?: self::gmtime();
+        if (!($dt = Format::parseDateTime($time)))
+            return null;
+        // Force UTC
+        $dt->setTimezone(new DateTimeZone('UTC'));
+
+        // Make dt Immutable.
+        $dt = DateTimeImmutable::createFromMutable($dt);
+        switch ($period) {
+            case 'td':
+            case 'today':
+                $start = $end = $dt->modify('today');
+                break;
+            case 'yd':
+            case 'yesterday':
+                $start = $end = $dt->modify('yesterday');
+                break;
+            case 'tw':
+            case 'this-week':
+                $N = $dt->format('N');
+                $start = $dt->modify($N == 1 ? 'today' : 'last monday');
+                $end = $start->modify('next sunday');
+                break;
+            case 'tm':
+            case 'this-month';
+                $start = $dt->modify('first day of this month');
+                $end = $dt->modify('last day of this month');
+                break;
+            case 'tq':
+            case 'this-quater':
+                $offset = ($dt->format('m') - 1) % 3;
+                $start = $dt->modify(" - $offset month")
+                    ->modify('first day of this month');
+                $end = $start->modify('+ 3 month')->modify('- 1 day');
+                break;
+            case 'ty':
+            case 'this-year':
+                $start = $dt->modify('january')->modify('first day of this month');
+                $end = $dt->modify('december')->modify('last day of this month');
+                break;
+            case 'lw':
+            case 'last-week':
+                //TODO: address edge cases
+                $start = $dt->modify('- 1 week')->modify('last monday');
+                $end = $start->modify('next sunday');
+                break;
+            case 'lm':
+            case 'last-month';
+                $start = $dt->modify('- 1 month')->modify('first day of this month');
+                $end = $start->modify('last day of this month');
+                break;
+            case 'lq':
+            case 'last-quater':
+                $offset = (($dt->format('m') - 1) % 3)+3;
+                $start = $dt->modify(" - $offset month")
+                    ->modify('first day of this month');
+                $end = $start->modify('+ 3 month')->modify('- 1 day');
+                break;
+            case 'ly':
+            case 'last-year':
+                $start = $dt->modify('- 1 year')
+                    ->modify('january')
+                    ->modify('first day of this month');
+                $end = $start->modify('december')->modify('last day of this month');
+                break;
+            default:
+                return null;
+        }
+
+        if ($start)
+            $start = $start->setTime(00, 00, 00);
+        if ($end)
+            $end = $end->setTime(23, 59, 59);
+
+        return (object) array('start' => $start, 'end' => $end);
+    }
+
     //Current page
     function currentURL() {
 
diff --git a/include/class.queue.php b/include/class.queue.php
index 9e9ace88f5b8e9a698e3566a4ab971b53e2a29c5..5d23711784dec4364be188ba6f1308c22108596a 100644
--- a/include/class.queue.php
+++ b/include/class.queue.php
@@ -667,7 +667,7 @@ class CustomQueue extends VerySimpleModel {
                 "bits" => QueueColumn::FLAG_SORTABLE,
                 "filter" => "link:ticketP",
                 "annotations" => '[{"c":"TicketSourceDecoration","p":"b"}]',
-                "conditions" => '[{"crit":["isanswered","set",null],"prop":{"font-weight":"bold"}}]',
+                "conditions" => '[{"crit":["isanswered","nset",null],"prop":{"font-weight":"bold"}}]',
             )),
             QueueColumn::placeholder(array(
                 "id" => 2,
@@ -686,7 +686,7 @@ class CustomQueue extends VerySimpleModel {
                 "bits" => QueueColumn::FLAG_SORTABLE,
                 "filter" => "link:ticket",
                 "annotations" => '[{"c":"TicketThreadCount","p":">"},{"c":"ThreadAttachmentCount","p":"a"},{"c":"OverdueFlagDecoration","p":"<"}]',
-                "conditions" => '[{"crit":["isanswered","set",null],"prop":{"font-weight":"bold"}}]',
+                "conditions" => '[{"crit":["isanswered","nset",null],"prop":{"font-weight":"bold"}}]',
                 "truncate" => 'ellipsis',
             )),
             QueueColumn::placeholder(array(
diff --git a/include/class.thread.php b/include/class.thread.php
index b499b95ed59c497d37d70955079f9501e3b58e80..481a34835aed3ce179a8c83bf8e07b3ed3b84596 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -1082,21 +1082,20 @@ implements TemplateVariable {
             $files = array($files);
 
         $ids = array();
-        foreach ($files as $name => $file) {
-            $F = array('inline' => is_array($file) && @$file['inline']);
-
-            if (is_numeric($file))
-                $fileId = $file;
-            elseif ($file instanceof AttachmentFile)
-                $fileId = $file->getId();
-            elseif (is_array($file) && isset($file['id']))
-                $fileId = $file['id'];
-            elseif ($AF = AttachmentFile::create($file))
+        foreach ($files as $id => $info) {
+            $F = array('inline' => is_array($info) && @$info['inline']);
+            $AF = null;
+
+            if ($info instanceof AttachmentFile)
+                $fileId = $info->getId();
+            elseif (is_array($info) && isset($info['id']))
+                $fileId = $info['id'];
+            elseif ($AF = AttachmentFile::create($info))
                 $fileId = $AF->getId();
             elseif ($add_error) {
-                $error = $file['error']
-                    ?: sprintf(_S('Unable to import attachment - %s'),
-                        $name ?: $file['name']);
+                $error = $info['error']
+                    ?: sprintf(_S('Unable to save attachment - %s'),
+                        $info['name'] ?: $info['id']);
                 if (is_numeric($error) && isset($error_descriptions[$error])) {
                     $error = sprintf(_S('Error #%1$d: %2$s'), $error,
                         _S($error_descriptions[$error]));
@@ -1119,15 +1118,15 @@ implements TemplateVariable {
 
             $F['id'] = $fileId;
 
-            if (is_string($name))
-                $F['name'] = $name;
+            if (is_string($info))
+                $F['name'] = $info;
             if (isset($AF))
                 $F['file'] = $AF;
 
             // Add things like the `key` field, but don't change current
             // keys of the file array
-            if (is_array($file))
-                $F += $file;
+            if (is_array($info))
+                $F += $info;
 
             // Key is required for CID rewriting in the body
             if (!isset($F['key']) && ($AF = AttachmentFile::lookup($F['id'])))
@@ -1157,11 +1156,13 @@ implements TemplateVariable {
         elseif (is_string($name)) {
             $filename = $name;
         }
+
         if ($filename) {
             // This should be a noop since the ORM caches on PK
             $F = @$file['file'] ?: AttachmentFile::lookup($file['id']);
             // XXX: This is not Unicode safe
-            if ($F && 0 !== strcasecmp($F->name, $filename))
+            // TODO: fix name lookup
+            if ($F && strcasecmp($F->name, $filename) !== 0)
                 $att->name = $filename;
         }
 
@@ -1578,7 +1579,7 @@ implements TemplateVariable {
         $attached_files = array();
         foreach (array(
             // Web uploads and canned attachments
-            $vars['files'], $vars['cannedattachments'],
+            $vars['files'],
             // Emailed or API attachments
             $vars['attachments'],
             // Inline images (attached to the draft)
diff --git a/include/class.thread_actions.php b/include/class.thread_actions.php
index 2bccfd311586643492e48b0b987f6d33c0a95d2d..54a2821c996b9fa4c3ef64ce8546aea735076825 100644
--- a/include/class.thread_actions.php
+++ b/include/class.thread_actions.php
@@ -525,6 +525,7 @@ JS
               foreach ($this->entry->getAttachments() as $a)
                   if (!$a->inline && $a->file) {
                     $_SESSION[':form-data'][$k][$a->file->getId()] = $a->getFilename();
+                    $_SESSION[':uploadedFiles'][$a->file->getId()] = $a->getFilename();
                   }
         }
 
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 27b7e6710754b86837c6223c1ac728b8fdffad10..43446e89e67ed43a885f61045ec3266f3f1efbc2 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -534,7 +534,8 @@ implements RestrictedAccess, Threadable, Searchable {
     }
 
     function getSource() {
-        return $this->source;
+        $sources = $this->getSources();
+        return $sources[$this->source] ?: $this->source;
     }
 
     function getIP() {
@@ -549,7 +550,7 @@ implements RestrictedAccess, Threadable, Searchable {
         global $cfg;
 
         return array(
-            'source'    => $this->getSource(),
+            'source'    => $this->source,
             'topicId'   => $this->getTopicId(),
             'slaId'     => $this->getSLAId(),
             'user_id'   => $this->getOwnerId(),
@@ -1028,7 +1029,7 @@ implements RestrictedAccess, Threadable, Searchable {
                         'id' => $fid,
                         'name' => 'source',
                         'label' => __('Ticket Source'),
-                        'default' => $this->getSource(),
+                        'default' => $this->source,
                         'choices' => Ticket::getSources()
                         ));
             break;
@@ -2836,9 +2837,9 @@ implements RestrictedAccess, Threadable, Searchable {
             return false;
         }
         $files = array();
-        foreach ($canned->attachments->getAll() as $file) {
-            $files[] = $file->file_id;
-            $_SESSION[':cannedFiles'][$file->file_id] = 1;
+        foreach ($canned->attachments->getAll() as $att) {
+            $files[] = array('id' => $att->file_id, 'name' => $att->getName());
+            $_SESSION[':cannedFiles'][$att->file_id] = $att->getName();
         }
 
         if ($cfg->isRichTextEnabled())
@@ -2851,7 +2852,7 @@ implements RestrictedAccess, Threadable, Searchable {
         $info = array('msgId' => $message instanceof ThreadEntry ? $message->getId() : 0,
                       'poster' => __('SYSTEM (Canned Reply)'),
                       'response' => $response,
-                      'cannedattachments' => $files
+                      'files' => $files
         );
         $errors = array();
         if (!($response=$this->postReply($info, $errors, false, false)))
@@ -4087,8 +4088,8 @@ implements RestrictedAccess, Threadable, Searchable {
         $vars['note'] = ThreadEntryBody::clean($vars['note']);
         $create_vars = $vars;
         $tform = TicketForm::objects()->one()->getForm($create_vars);
-        $create_vars['cannedattachments']
-            = $tform->getField('message')->getWidget()->getAttachments()->getClean();
+        $create_vars['files']
+            = $tform->getField('message')->getWidget()->getAttachments()->getFiles();
 
         if (!($ticket=self::create($create_vars, $errors, 'staff', false)))
             return false;
diff --git a/include/i18n/en_US/templates/ticket/upgraded.yaml b/include/i18n/en_US/templates/ticket/upgraded.yaml
index b46a44d854c3849262da9b024b4af64baaee78c8..1ff02c0527cda0e4c8f548a4b68ff64a46792936 100644
--- a/include/i18n/en_US/templates/ticket/upgraded.yaml
+++ b/include/i18n/en_US/templates/ticket/upgraded.yaml
@@ -13,7 +13,7 @@ subject: osTicket Upgraded!
 message: |
     <p>
     osTicket upgraded successfully! Please refer to the Release Notes
-    (http://osticket.com/wiki/Release_Notes) for more information about
+    (https://docs.osticket.com/en/latest/Developer%20Documentation/Changelog.html?highlight=notes) for more information about
     changes and new features.
     </p><p>
     Be sure to join the <a href="http://osticket.com/forums">osTicket
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index 62a0c60fc0f0c30833e05b6f5619ac3d2717245f..09bcfb21fdf95ee2a05366724b6ee921c63eaf48 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -31,6 +31,7 @@ if (!$user && $_GET['tid'] && ($entry = ThreadEntry::lookup($_GET['tid']))) {
         foreach ($entry->getAttachments() as $a) {
           if (!$a->inline && $a->file) {
             $_SESSION[':form-data'][$k][$a->file->getId()] = $a->getFilename();
+            $_SESSION[':uploadedFiles'][$a->file->getId()] = $a->getFilename();
           }
         }
      }
diff --git a/include/upgrader/done.inc.php b/include/upgrader/done.inc.php
index 2ad200f78b41fd148a91a31696ed83d7c2b66fc7..39c598d7f49a283ca0539f11f024ad57812eefd9 100644
--- a/include/upgrader/done.inc.php
+++ b/include/upgrader/done.inc.php
@@ -9,7 +9,7 @@ $_SESSION['ost_upgrader']=null;
         <div id="intro">
         <p><?php echo __('Congratulations! osTicket upgrade has been completed successfully.');?></p>
         <p><?php echo sprintf(__('Please refer to %s for more information about changes and/or new features.'),
-            sprintf('<a href="http://osticket.com/wiki/Release_Notes" target="_blank">%s</a>',
+            sprintf('<a href="https://docs.osticket.com/en/latest/Developer%20Documentation/Changelog.html?highlight=notes" target="_blank">%s</a>',
             __('Release Notes')
         ));?></p>
         </div>
@@ -26,7 +26,7 @@ $_SESSION['ost_upgrader']=null;
             <p><b><?php echo __('Post-upgrade');?></b>: <?php
             echo sprintf(__('You can now go to %s to enable the system and explore the new features. For complete and up-to-date release notes see the %s'),
                 sprintf('<a href="'. ROOT_PATH . 'scp/settings.php" target="_blank">%s</a>', __('Admin Panel')),
-                sprintf('<a href="http://osticket.com/wiki/Release_Notes" target="_blank">%s</a>', __('osTicket Wiki')));?></p>
+                sprintf('<a href="https://docs.osticket.com/en/latest/Developer%20Documentation/Changelog.html?highlight=notes" target="_blank">%s</a>', __('osTicket Docs')));?></p>
             <p><b><?php echo __('Stay up to date');?></b>: <?php echo __("It's important to keep your osTicket installation up to date. Get announcements, security updates and alerts delivered directly to you!");?>
             <?php echo sprintf(__('%1$s Get in the loop %2$s today and stay informed!'), '<a target="_blank" href="http://osticket.com/newsletter">', '</a>');?></p>
             <p><b><?php echo __('Commercial Support Available');?></b>: <?php echo sprintf(__('Get guidance and hands-on expertise to address unique challenges and make sure your osTicket runs smoothly, efficiently, and securely. %1$s Learn More! %2$s'), '<a target="_blank" href="http://osticket.com/support">','</a>');?></p>
diff --git a/open.php b/open.php
index a507bc979515181f5d46eb035a8ccaf9ade92e59..ef1dde53285c8d406c3ba5406fcb3f52df151854 100644
--- a/open.php
+++ b/open.php
@@ -33,7 +33,7 @@ if ($_POST) {
     $messageField = $tform->getField('message');
     $attachments = $messageField->getWidget()->getAttachments();
     if (!$errors && $messageField->isAttachmentsEnabled())
-        $vars['cannedattachments'] = $attachments->getClean();
+        $vars['files'] = $attachments->getFiles();
 
     // Drop the draft.. If there are validation errors, the content
     // submitted will be displayed back to the user
diff --git a/scp/tasks.php b/scp/tasks.php
index 63e4b87ca1b35831e54f6bfcde6ce0efe07620db..5375261863e375f72e088b9958b80967299383b3 100644
--- a/scp/tasks.php
+++ b/scp/tasks.php
@@ -48,9 +48,7 @@ if($_POST && !$errors):
         switch(strtolower($_POST['a'])):
         case 'postnote': /* Post Internal Note */
             $vars = $_POST;
-            $attachments = $note_attachments_form->getField('attachments')->getClean();
-            $vars['cannedattachments'] = array_merge(
-                $vars['cannedattachments'] ?: array(), $attachments);
+            $vars['files'] = $note_attachments_form->getField('attachments')->getFiles();
 
             $wasOpen = ($task->isOpen());
             if(($note=$task->postNote($vars, $errors, $thisstaff))) {
@@ -78,9 +76,7 @@ if($_POST && !$errors):
             break;
         case 'postreply': /* Post an update */
             $vars = $_POST;
-            $attachments = $reply_attachments_form->getField('attachments')->getClean();
-            $vars['cannedattachments'] = array_merge(
-                $vars['cannedattachments'] ?: array(), $attachments);
+            $vars['files'] = $reply_attachments_form->getField('attachments')->getFiles();
 
             $wasOpen = ($task->isOpen());
             if (($response=$task->postReply($vars, $errors))) {
diff --git a/scp/tickets.php b/scp/tickets.php
index 4851b4169d257b259671e525d12b0917be428b46..d100f38d7b1af8e1505301922754e5925bc264dc 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -53,7 +53,7 @@ if (!$ticket) {
             && $_GET['a'] !== 'open'
     ) {
         $criteria = [
-            ['user__name', 'equal', $user->name],
+            ['user__emails__address', 'equal', $user->getDefaultEmailAddress()],
             ['user_id', 'equal', $user->id],
         ];
         if ($S = $_GET['status'])
@@ -149,7 +149,7 @@ if($_POST && !$errors):
                 $errors['err'] = __('Action denied. Contact admin for access');
             } else {
                 $vars = $_POST;
-                $vars['cannedattachments'] = $response_form->getField('attachments')->getClean();
+                $vars['files'] = $response_form->getField('attachments')->getFiles();
                 $vars['response'] = ThreadEntryBody::clean($vars['response']);
                 if(!$vars['response'])
                     $errors['response']=__('Response required');
@@ -211,9 +211,7 @@ if($_POST && !$errors):
             break;
         case 'postnote': /* Post Internal Note */
             $vars = $_POST;
-            $attachments = $note_form->getField('attachments')->getClean();
-            $vars['cannedattachments'] = array_merge(
-                $vars['cannedattachments'] ?: array(), $attachments);
+            $vars['files'] = $note_form->getField('attachments')->getFiles();
             $vars['note'] = ThreadEntryBody::clean($vars['note']);
 
             if ($cfg->isTicketLockEnabled()) {
@@ -405,7 +403,7 @@ if($_POST && !$errors):
                     if ($vars['uid'] && !($user=User::lookup($vars['uid'])))
                         $vars['uid'] = 0;
 
-                    $vars['cannedattachments'] = $response_form->getField('attachments')->getClean();
+                    $vars['files'] = $response_form->getField('attachments')->getFiles();
 
                     if(($ticket=Ticket::open($vars, $errors))) {
                         $msg=__('Ticket created successfully');
@@ -418,6 +416,10 @@ if($_POST && !$errors):
                         $response_form->getField('attachments')->reset();
                         $_SESSION[':form-data'] = null;
                     } elseif(!$errors['err']) {
+                        // ensure that we retain the tid if ticket is created from thread
+                        if ($_SESSION[':form-data']['ticketId'] || $_SESSION[':form-data']['taskId'])
+                            $_GET['tid'] = $_SESSION[':form-data']['ticketId'] ?: $_SESSION[':form-data']['taskId'];
+
                         $errors['err']=sprintf('%s %s',
                             __('Unable to create the ticket.'),
                             __('Correct any errors below and try again.'));
diff --git a/tickets.php b/tickets.php
index fa88e5e55775b4aeff32dda04841b35eed6fc178..560b16f3dc963008e05aa8c610607b82992fee95 100644
--- a/tickets.php
+++ b/tickets.php
@@ -84,7 +84,7 @@ if ($_POST && is_object($ticket) && $ticket->getId()) {
                     'poster' => (string) $thisclient->getName(),
                     'message' => $_POST['message']
                     );
-            $vars['cannedattachments'] = $attachments->getClean();
+            $vars['files'] = $attachments->getFiles();
             if (isset($_POST['draft_id']))
                 $vars['draft_id'] = $_POST['draft_id'];