From 3bbe2933581f592865bb7ac5d3546e5da9283dcd Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Sun, 10 May 2015 20:18:25 -0500
Subject: [PATCH] file: Allow different names for duplicated content

This patch (which needs further development), introduces an extra field to
the %attachment table to store a file's name, in the event that two files
with the same content are stored in the database with differing names. In
such a case, the name is stored in the %attachment table. This allows the
attachment to specify a different name each time the same file is attached
to something in the system.
---
 include/class.attachment.php                  | 18 +++++++++++++-
 include/class.forms.php                       | 12 ++++++++--
 include/class.mailer.php                      | 17 ++++++++++---
 include/class.thread.php                      | 24 +++++++++++++++----
 .../client/templates/thread-entry.tmpl.php    |  4 ++--
 include/staff/templates/thread-entry.tmpl.php |  4 ++--
 .../streams/core/9143a511-00000000.patch.sql  |  6 ++++-
 js/filedrop.field.js                          |  2 +-
 setup/inc/streams/core/install-mysql.sql      |  1 +
 9 files changed, 72 insertions(+), 16 deletions(-)

diff --git a/include/class.attachment.php b/include/class.attachment.php
index 3247d0691..cd2ef95ac 100644
--- a/include/class.attachment.php
+++ b/include/class.attachment.php
@@ -56,6 +56,10 @@ class Attachment extends VerySimpleModel {
         return $this->file;
     }
 
+    function getFilename() {
+        return $this->name ?: $this->file->name;
+    }
+
     function getHashtable() {
         return $this->ht;
     }
@@ -112,7 +116,8 @@ class GenericAttachments {
 
     function upload($files, $inline=false, $lang=false) {
         $i=array();
-        if (!is_array($files)) $files=array($files);
+        if (!is_array($files))
+            $files = array($files);
         foreach ($files as $file) {
             if (is_numeric($file))
                 $fileId = $file;
@@ -131,6 +136,17 @@ class GenericAttachments {
                 'file_id' => $fileId,
                 'inline' => $_inline ? 1 : 0,
             ));
+
+            // Record varying file names in the attachment record
+            if (is_array($file) && isset($file['name'])) {
+                $filename = $file['name'];
+            }
+            if ($filename) {
+                // This should be a noop since the ORM caches on PK
+                $file = AttachmentFile::lookup($fileId);
+                if ($file->name != $filename)
+                    $att->name = $filename;
+            }
             if ($lang)
                 $att->lang = $lang;
 
diff --git a/include/class.forms.php b/include/class.forms.php
index 9f4ece823..5a3302259 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -3123,6 +3123,14 @@ class FileUploadWidget extends Widget {
     function getValue() {
         $data = $this->field->getSource();
         $ids = array();
+        $base = parent::getValue();
+        if (is_array($base)) {
+            foreach ($base as $info) {
+                list($id, $name) = explode(',', $info, 2);
+                // Keep the values as the IDs
+                $ids[$name] = $id;
+            }
+        }
         // Handle manual uploads (IE<10)
         if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES[$this->name])) {
             foreach (AttachmentFile::format($_FILES[$this->name]) as $file) {
@@ -3132,13 +3140,13 @@ class FileUploadWidget extends Widget {
                 }
                 catch (FileUploadError $ex) {}
             }
-            return array_merge($ids, parent::getValue() ?: array());
+            return $ids;
         }
         // If no value was sent, assume an empty list
         elseif ($data && is_array($data) && !isset($data[$this->name]))
             return array();
 
-        return parent::getValue();
+        return $ids;
     }
 }
 
diff --git a/include/class.mailer.php b/include/class.mailer.php
index 83f097050..266594408 100644
--- a/include/class.mailer.php
+++ b/include/class.mailer.php
@@ -82,7 +82,7 @@ class Mailer {
     function addAttachment(Attachment $attachment) {
         // XXX: This looks too assuming; however, the attachment processor
         // in the ::send() method seems hard coded to expect this format
-        $this->attachments[$attachment->file_id] = $attachment->file;
+        $this->attachments[$attachment->file_id] = $attachment;
     }
 
     function addFile(AttachmentFile $file) {
@@ -433,7 +433,10 @@ class Mailer {
                     $file = false;
                     foreach ($self->attachments as $id=>$F) {
                         if (strcasecmp($F->getKey(), $match[1]) === 0) {
-                            $file = $F;
+                            if ($F instanceof Attachment)
+                                $file = $F->getFile();
+                            else
+                                $file = $F;
                             break;
                         }
                     }
@@ -452,8 +455,16 @@ class Mailer {
         //XXX: Attachments
         if(($attachments=$this->getAttachments())) {
             foreach($attachments as $id=>$file) {
+                // Read the filename from the Attachment if possible
+                if ($file instanceof Attachment) {
+                    $filename = $file->getFilename();
+                    $file = $file->getFile();
+                }
+                else {
+                    $filename = $file->getName();
+                }
                 $mime->addAttachment($file->getData(),
-                    $file->getType(), $file->getName(),false);
+                    $file->getType(), $filename, false);
             }
         }
 
diff --git a/include/class.thread.php b/include/class.thread.php
index b228fd63e..d1439b243 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -877,7 +877,7 @@ implements TemplateVariable {
     Save attachment to the DB.
     @file is a mixed var - can be ID or file hashtable.
     */
-    function saveAttachment(&$file) {
+    function saveAttachment(&$file, $name=false) {
 
         $inline = is_array($file) && @$file['inline'];
 
@@ -898,6 +898,21 @@ implements TemplateVariable {
             'file_id' => $fileId,
             'inline' => $inline ? 1 : 0,
         ));
+
+        // Record varying file names in the attachment record
+        if (is_array($file) && isset($file['name'])) {
+            $filename = $file['name'];
+        }
+        elseif (is_string($name)) {
+            $filename = $name;
+        }
+        if ($filename) {
+            // This should be a noop since the ORM caches on PK
+            $file = AttachmentFile::lookup($fileId);
+            if ($file->name != $filename)
+                $att->name = $filename;
+        }
+
         if (!$att->save())
             return false;
         return $att;
@@ -905,9 +920,10 @@ implements TemplateVariable {
 
     function saveAttachments($files) {
         $attachments = array();
-        foreach ($files as $file)
-           if (($A = $this->saveAttachment($file)))
+        foreach ($files as $name=>$file) {
+           if (($A = $this->saveAttachment($file, $name)))
                $attachments[] = $A;
+        }
 
         return $attachments;
     }
@@ -921,7 +937,7 @@ implements TemplateVariable {
         foreach ($this->attachments as $att) {
             $json[$att->file->getKey()] = array(
                 'download_url' => $att->file->getDownloadUrl(),
-                'filename' => $att->file->name,
+                'filename' => $att->getFilename(),
             );
         }
 
diff --git a/include/client/templates/thread-entry.tmpl.php b/include/client/templates/thread-entry.tmpl.php
index fe9f686e8..70e4bbb5c 100644
--- a/include/client/templates/thread-entry.tmpl.php
+++ b/include/client/templates/thread-entry.tmpl.php
@@ -70,8 +70,8 @@ if ($user && ($url = $user->get_gravatar(48)))
         <span class="attachment-info">
         <i class="icon-paperclip icon-flip-horizontal"></i>
         <a class="no-pjax truncate filename" href="<?php echo $A->file->getDownloadUrl();
-            ?>" download="<?php echo Format::htmlchars($A->file->name); ?>"
-            target="_blank"><?php echo Format::htmlchars($A->file->name);
+            ?>" download="<?php echo Format::htmlchars($A->getFilename()); ?>"
+            target="_blank"><?php echo Format::htmlchars($A->getFilename());
         ?></a><?php echo $size;?>
         </span>
 <?php   }  ?>
diff --git a/include/staff/templates/thread-entry.tmpl.php b/include/staff/templates/thread-entry.tmpl.php
index f06b376ef..01839aac5 100644
--- a/include/staff/templates/thread-entry.tmpl.php
+++ b/include/staff/templates/thread-entry.tmpl.php
@@ -70,8 +70,8 @@ if ($user && ($url = $user->get_gravatar(48)))
         <span class="attachment-info">
         <i class="icon-paperclip icon-flip-horizontal"></i>
         <a class="no-pjax truncate filename" href="<?php echo $A->file->getDownloadUrl();
-            ?>" download="<?php echo Format::htmlchars($A->file->name); ?>"
-            target="_blank"><?php echo Format::htmlchars($A->file->name);
+            ?>" download="<?php echo Format::htmlchars($A->getFilename()); ?>"
+            target="_blank"><?php echo Format::htmlchars($A->getFilename());
         ?></a><?php echo $size;?>
         </span>
 <?php   }  ?>
diff --git a/include/upgrader/streams/core/9143a511-00000000.patch.sql b/include/upgrader/streams/core/9143a511-00000000.patch.sql
index 83bfa49cc..dd28bbed9 100644
--- a/include/upgrader/streams/core/9143a511-00000000.patch.sql
+++ b/include/upgrader/streams/core/9143a511-00000000.patch.sql
@@ -1,6 +1,6 @@
 /**
  * @signature 959aca6ed189cd918d227a3ea8a135a3
- * @version v1.9.6
+ * @version v1.10.0
  * @title Retire `private`, `required`, and `edit_mask` for fields
  *
  */
@@ -109,6 +109,10 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%_search`;
 UPDATE `%TABLE_PREFIX%config` SET `value` = '1'
   WHERE `key` = 'reindex' and `namespace` = 'mysqlsearch';
 
+-- Support varying names for duplicated content
+ALTER TABLE `%TABLE_PREFIX%attachment`
+  ADD `name` varchar(255) NULL default NULL AFTER `file_id`;
+
 -- Finished with patch
 UPDATE `%TABLE_PREFIX%config`
     SET `value` = '00000000000000000000000000000000'
diff --git a/js/filedrop.field.js b/js/filedrop.field.js
index 4e82280ae..8b863fc9f 100644
--- a/js/filedrop.field.js
+++ b/js/filedrop.field.js
@@ -109,7 +109,7 @@
             // Upload failed. TODO: Add a button to the UI to retry on
             // HTTP 500
             return e.remove();
-          e.find('[name="'+that.options.name+'"]').val(json.id);
+          e.find('[name="'+that.options.name+'"]').val(''+json.id+','+file.name);
           e.data('fileId', json.id);
           e.find('.progress-bar')
             .width('100%')
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index 873da4920..963adce7f 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -21,6 +21,7 @@ CREATE TABLE `%TABLE_PREFIX%attachment` (
   `object_id` int(11) unsigned NOT NULL,
   `type` char(1) NOT NULL,
   `file_id` int(11) unsigned NOT NULL,
+  `name` varchar(255) NULL default NULL,
   `inline` tinyint(1) unsigned NOT NULL DEFAULT '0',
   `lang` varchar(16),
   PRIMARY KEY  (`id`),
-- 
GitLab