diff --git a/include/class.attachment.php b/include/class.attachment.php
index a46028d83c085241461771a0b3faaf091777273d..10159f5e82842892398a9de147534e2807cc3b09 100644
--- a/include/class.attachment.php
+++ b/include/class.attachment.php
@@ -86,7 +86,7 @@ class Attachment {
     function getIdByFileHash($hash, $tid=0) {
         $sql='SELECT attach_id FROM '.TICKET_ATTACHMENT_TABLE.' a '
             .' INNER JOIN '.FILE_TABLE.' f ON(f.id=a.file_id) '
-            .' WHERE f.hash='.db_input($hash);
+            .' WHERE f.`key`='.db_input($hash);
         if($tid)
             $sql.=' AND a.ticket_id='.db_input($tid);
 
@@ -157,7 +157,7 @@ class GenericAttachments {
     function _getList($separate=false, $inlines=false) {
         if(!isset($this->attachments)) {
             $this->attachments = array();
-            $sql='SELECT f.id, f.size, f.hash, f.name, a.inline '
+            $sql='SELECT f.id, f.size, f.`key`, f.name, a.inline '
                 .' FROM '.FILE_TABLE.' f '
                 .' INNER JOIN '.ATTACHMENT_TABLE.' a ON(f.id=a.file_id) '
                 .' WHERE a.`type`='.db_input($this->getType())
@@ -171,7 +171,7 @@ class GenericAttachments {
         $attachments = array();
         foreach ($this->attachments as $a) {
             if ($a['inline'] != $separate || $a['inline'] == $inlines) {
-                $a['key'] = md5($a['id'].session_id().$a['hash']);
+                $a['key'] = md5($a['id'].session_id().$a['key']);
                 $a['file_id'] = $a['id'];
                 $attachments[] = $a;
             }
diff --git a/include/class.file.php b/include/class.file.php
index 52451de550c6400e91c23870de0b1ec3243a505c..260c846af68059f1415bc0defe1c11e128717c4a 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -119,9 +119,9 @@ class AttachmentFile {
         return FileStorageBackend::getInstance($this);
     }
 
-    function sendData($redirect=true) {
+    function sendData($redirect=true, $disposition='inline') {
         $bk = $this->open();
-        if ($redirect && $bk->sendRedirectUrl())
+        if ($redirect && $bk->sendRedirectUrl($disposition))
             return;
 
         @ini_set('zlib.output_compression', 'Off');
@@ -213,7 +213,7 @@ class AttachmentFile {
 
         header('Content-Transfer-Encoding: binary');
         header('Content-Length: '.$this->getSize());
-        $this->sendData();
+        $this->sendData(true, 'attachment');
         exit();
     }
 
@@ -336,12 +336,18 @@ class AttachmentFile {
             .',`key`='.db_input($file['key'])
             .',signature='.db_input($file['signature']);
 
-        if (!(db_query($sql) && ($file['id']=db_insert_id())))
+        if (!(db_query($sql) && ($id = db_insert_id())))
+            return false;
+
+        if (!($f = AttachmentFile::lookup($id)))
             return false;
 
-        $bk = self::getBackendForFile($file);
-        if (isset($file['tmp_file'])) {
-            if (!$bk->upload($file['tmp_file']))
+        // Note that this is preferred over $f->open() because the file does
+        // not have a valid backend configured yet. ::getBackendForFile()
+        // will consider the system configuration for storing the file
+        $bk = self::getBackendForFile($f);
+        if (isset($file['tmp_name'])) {
+            if (!$bk->upload($file['tmp_name']))
                 return false;
         }
         elseif (!$bk->write($file['data'])) {
@@ -352,14 +358,14 @@ class AttachmentFile {
         # XXX: ft does not exists during the upgrade when attachments are
         #      migrated! Neither does `bk`
         if ($ft) {
-            $sql .= 'UPDATE '.FILE_TABLE.' SET ft='
-                .db_input(FileStorageBackend::getBkChar($bk))
+            $sql = 'UPDATE '.FILE_TABLE.' SET bk='
+                .db_input($bk->getBkChar())
                 .', ft='.db_input($ft)
-                .' WHERE id='.db_input($file->getId());
+                .' WHERE id='.db_input($f->getId());
             db_query($sql);
         }
 
-        return $file->getId();
+        return $f->getId();
     }
 
     /**
@@ -379,22 +385,17 @@ class AttachmentFile {
 
         // Copy the file to the new backend and hash the contents
         $target = AttachmentStorageBackend::lookup($bk, $this->ht);
-        print("Opening source\n");
         $source = $this->open();
-        print("Copying data ");
         // TODO: Make this resumable so that if the file cannot be migrated
         //      in the max_execution_time, the migration can be continued
         //      the next time the cron runs
         while ($block = $source->read()) {
-            print(".");
             hash_update($before, $block);
             $target->write($block);
         }
-        print(" Done\n");
 
         // Verify that the hash of the target file matches the hash of the
         // source file
-        print("Verifying transferred data\n");
         $target = AttachmentStorageBackend::lookup($bk, $this->ht);
         while ($block = $target->read())
             hash_update($after, $block);
@@ -404,17 +405,27 @@ class AttachmentFile {
             return false;
         }
 
-        print("Updating file meta table\n");
         $sql = 'UPDATE '.FILE_TABLE.' SET bk='
             .db_input($target->getBkChar())
             .' WHERE id='.db_input($this->getId());
         if (!db_query($sql) || db_affected_rows()!=1)
             return false;
 
-        print("Unlinking source data\n");
         return $source->unlink();
     }
 
+    /**
+     * Considers the system's configuration for file storage selection based
+     * on the file information and purpose (FAQ attachment, image, etc).
+     *
+     * Parameters:
+     * $file - (hasharray) file information which would be passed to
+     * ::save() for instance.
+     *
+     * Returns:
+     * Instance<FileStorageBackend> backend selected based on the file
+     * received.
+     */
     static function getBackendForFile($file) {
         global $cfg;
 
@@ -633,7 +644,7 @@ class FileStorageBackend {
      * false to indicate that the read() method should be used to retrieve
      * the data and broker it to the user agent.
      */
-    function sendRedirectUrl() {
+    function sendRedirectUrl($disposition='inline') {
         return false;
     }
 
diff --git a/include/staff/plugins.inc.php b/include/staff/plugins.inc.php
index 4b9c5bccc3d83a0474fc4e953b8dfb5c161d0ef9..a25ccc095a1c11a1544d2c24d3666d185ee0ca70 100644
--- a/include/staff/plugins.inc.php
+++ b/include/staff/plugins.inc.php
@@ -51,7 +51,7 @@ foreach ($ost->plugins->allInstalled() as $p) {
             <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
             <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No extra forms defined yet &mdash; add one!';
+                echo 'No plugins installed yet &mdash; <a href="?a=add">add one</a>!';
             } ?>
         </td>
      </tr>