Lack of interest Allow uploading attachments for different content types in a single form

This suggestion has been closed automatically because it did not receive enough votes over an extended period of time. If you wish to see this, please search for an open suggestion and, if you don't find any, post a new one.

TickTackk

Well-known member
Currently it is not possible upload attachment for more than one content type via editor; for example if one wants to add another attach files button in the quick reply both the buttons open the same file browser window. I've already managed to write something which allows this happen but there will be compatibility issues with it.

Diff for js\xf\attachment_manager.js
Diff:
--- attachment_manager.js    2019-11-07 04:14:00.000000000 +0000
+++ attachment_manager.js    2019-11-07 04:34:22.000000000 +0000
@@ -28,8 +28,8 @@
 
         supportsVideoUploads: null,
 
-        manageUrl: null,
-        flow: null,
+        manageUrls: {},
+        flows: {},
 
         fileMap: {},
         isUploading: false,
@@ -59,22 +59,46 @@
                 }
             }
 
-            var $uploaders = $target.find(options.uploadButton);
+            var $uploaders = $target.find(options.uploadButton),
+                baseManageUrl = null;
 
             if (this.options.manageUrl)
             {
-                this.manageUrl = this.options.manageUrl;
+                baseManageUrl = this.options.manageUrl;
             }
-            else
+
+            if ($uploaders.length)
             {
-                if (!$uploaders.length)
+                $uploaders.each(function()
                 {
-                    console.error('No manage URL specified and no uploaders available.');
-                    return;
-                }
+                    var $uploader = $(this),
+                        manageUrl = $uploader.data('upload-href') || $uploader.attr('href'),
+                        uploaderId = $uploader.xfUniqueId();
 
-                var $uploader = $uploaders.first();
-                this.manageUrl = $uploader.data('upload-href') || $uploader.attr('href');
+                    if (uploaderId === undefined)
+                    {
+                        uploaderId = $uploader.xfUniqueId();
+                    }
+
+                    if (!manageUrl)
+                    {
+                        if (!baseManageUrl)
+                        {
+                            console.error('No manage URL specificed for #' + uploaderId);
+                            return;
+                        }
+
+                        manageUrl = baseManageUrl;
+                    }
+
+                    self.manageUrls[uploaderId] = manageUrl;
+                });
+            }
+
+            if ($.isEmptyObject(this.manageUrls))
+            {
+                console.error('No manage URL specified in options or no uploaders are available.');
+                return;
             }
 
             this.$filesContainer = $target.find(options.filesContainer);
@@ -88,15 +112,21 @@
                 console.error('No attached file template found.');
             }
 
-            var flow = this.setupFlow();
-            if (!flow)
+            $uploaders.each(function ()
+            {
+                var $uploader = $(this),
+                    uploaderId = $uploader.xfUniqueId();
+
+                self.flows[uploaderId] = self.setupFlow($uploader);
+            });
+
+            if ($.isEmptyObject(this.flows))
             {
-                console.error('No flow uploader support');
+                console.error('No flow has been setup');
                 return;
             }
 
-            this.flow = flow;
-            this.setupUploadButtons($uploaders, flow);
+            this.setupUploadButtons($uploaders);
 
             if (this.options.allowDrop)
             {
@@ -127,9 +157,9 @@
             }
         },
 
-        setupFlow: function()
+        setupFlow: function($uploader)
         {
-            var options = this.getFlowOptions(),
+            var options = this.getFlowOptions($uploader),
                 flow = new Flow(options),
                 self = this;
 
@@ -145,22 +175,29 @@
                 flow = new FustyFlow(options);
             }
 
-            flow.on('fileAdded', XF.proxy(this, 'fileAdded'));
+            flow.on('fileAdded', XF.proxy(this, 'fileAdded', $uploader));
             flow.on('filesSubmitted', function() { self.setUploading(true); flow.upload(); });
-            flow.on('fileProgress', XF.proxy(this, 'uploadProgress'));
-            flow.on('fileSuccess', XF.proxy(this, 'uploadSuccess'));
-            flow.on('fileError', XF.proxy(this, 'uploadError'));
+            flow.on('fileProgress', XF.proxy(this, 'uploadProgress', $uploader));
+            flow.on('fileSuccess', XF.proxy(this, 'uploadSuccess', $uploader));
+            flow.on('fileError', XF.proxy(this, 'uploadError', $uploader));
 
             return flow;
         },
 
-        getFlowOptions: function()
+        getFlowOptions: function($uploader)
         {
+            var uploaderId = $uploader.xfUniqueId();
+            if (!uploaderId in this.manageUrls)
+            {
+                console.error('No manage URL set for #' + uploaderId);
+                return;
+            }
+
             return {
-                target: this.manageUrl,
+                target: this.manageUrls[uploaderId],
                 allowDuplicateUploads: true,
                 fileParameterName: 'upload',
-                query: XF.proxy(this, 'uploadQueryParams'),
+                query: XF.proxy(this, 'uploadQueryParams', $uploader),
                 simultaneousUploads: 1,
                 testChunks: false,
                 progressCallbacksInterval: 100,
@@ -183,14 +220,22 @@
             };
         },
 
-        setupUploadButtons: function($uploaders, flow)
+        setupUploadButtons: function($uploaders)
         {
             var t = this;
             $uploaders.each(function()
             {
                 var $button = $(this),
                     accept = $button.data('accept') || '',
-                    $target = $('<span />').insertAfter($button).append($button);
+                    $target = $('<span />').insertAfter($button).append($button),
+                    buttonId = $button.xfUniqueId(),
+                    flow = t.flows[buttonId];
+
+                if (!flow)
+                {
+                    console.error('No flow instance available for #' + buttonId);
+                    return
+                }
 
                 if (accept == '.')
                 {
@@ -225,7 +270,7 @@
             });
         },
 
-        fileAdded: function(file)
+        fileAdded: function($uploader, file)
         {
             var $html = this.applyUploadTemplate({
                 filename: file.name,
@@ -242,8 +287,7 @@
 
             this.$target.find(this.options.uploadButton).blur();
 
-            var $button = this.$target.find(this.options.uploadButton).first(),
-                maxVideoSize = $button.data('video-size');
+            var maxVideoSize = $uploader.data('video-size');
 
             // avoid having to upload a huge file fully before being told it is too large
             if (this.options.checkVideoSize
@@ -254,12 +298,12 @@
             )
             {
                 // note: only applying this to videos as images at least can be made smaller after upload through resizing
-                this.uploadError(file, this.addErrorToJson({}, XF.phrase('file_too_large_to_upload')));
+                this.uploadError($uploader, file, this.addErrorToJson({}, XF.phrase('file_too_large_to_upload')));
                 return false;
             }
             if (XF.config.uploadMaxFilesize > 0 && file.size > XF.config.uploadMaxFilesize)
             {
-                this.uploadError(file, this.addErrorToJson({}, XF.phrase('uploaded_file_is_too_large_for_server_to_process')));
+                this.uploadError($uploader, file, this.addErrorToJson({}, XF.phrase('uploaded_file_is_too_large_for_server_to_process')));
                 return false;
             }
         },
@@ -278,7 +322,7 @@
             return (videoExtensions.indexOf(fileParts.pop()) !== -1);
         },
 
-        uploadProgress: function(file)
+        uploadProgress: function($uploader, file)
         {
             var $html = this.fileMap[file.uniqueIdentifier];
             if (!$html)
@@ -307,18 +351,24 @@
                 .css('width', percent + '%');
         },
 
-        uploadSuccess: function(file, message, chunk)
+        uploadSuccess: function($uploader, file, message, chunk)
         {
-            var json = this.getObjectFromMessage(message);
+            var json = this.getObjectFromMessage(message),
+                uploaderId = $uploader.xfUniqueId();
 
             this.setUploading(false);
 
             if (json.status && json.status == 'error')
             {
-                this.uploadError(file, json, chunk);
+                this.uploadError($uploader, file, json, chunk);
                 return;
             }
 
+            if (uploaderId in this.manageUrls)
+            {
+                json.attachment.manage_url = this.manageUrls[uploaderId];
+            }
+
             if (json.attachment)
             {
                 this.insertUploadedRow(json.attachment, this.fileMap[file.uniqueIdentifier]);
@@ -326,7 +376,7 @@
             else
             {
                 json = this.addErrorToJson(json);
-                this.uploadError(file, json, chunk);
+                this.uploadError($uploader, file, json, chunk);
             }
         },
 
@@ -402,7 +452,7 @@
             this.toggleInsertAllRow();
         },
 
-        uploadError: function(file, message, chunk)
+        uploadError: function($uploader, file, message, chunk)
         {
             var json = this.getObjectFromMessage(message);
 
@@ -432,6 +482,7 @@
             var $target = $(e.currentTarget),
                 action = $target.attr('data-action'),
                 type = $target.attr('data-type'),
+                manageUrl = $target.attr('data-manage-url'),
                 $row = $target.closest(this.options.fileRow);
 
             switch (action)
@@ -442,7 +493,7 @@
                     break;
 
                 case 'delete':
-                    this.deleteAttachment($row, type);
+                    this.deleteAttachment(manageUrl, $row, type);
                     break;
 
                 case 'cancel':
@@ -518,7 +569,7 @@
             XF.insertIntoEditor(this.$target, html, bbCode, '[data-attachment-target=false]');
         },
 
-        deleteAttachment: function($row, type)
+        deleteAttachment: function(manageUrl, $row, type)
         {
             type = type || 'image';
 
@@ -532,7 +583,7 @@
 
             XF.ajax(
                 'post',
-                this.manageUrl,
+                manageUrl,
                 { delete: attachmentId },
                 function (data)
                 {
@@ -609,7 +660,7 @@
             this.removeFileRow($row);
         },
 
-        uploadQueryParams: function()
+        uploadQueryParams: function(uploader)
         {
             return {
                 _xfToken: XF.config.csrf,

Diff for attachment_upload:
Diff:
--- attachment_upload.html    2019-11-07 05:31:06.000000000 +0000
+++ attachment_upload.html    2019-11-07 05:30:55.000000000 +0000
@@ -10,12 +10,16 @@
                         <xf:foreach loop="$existing" value="$attachment">
                             <xf:macro template="helper_attach_upload" name="uploaded_file"
                                 arg-attachment="{$attachment}"
-                                arg-noJsFallback="{{ true }}" />
+                                arg-noJsFallback="{{ true }}"
+                                arg-context="{$context}"
+                                arg-hash="{$hash}" />
                         </xf:foreach>
                         <xf:foreach loop="$new" value="$attachment">
                             <xf:macro template="helper_attach_upload" name="uploaded_file"
                                 arg-attachment="{$attachment}"
-                                arg-noJsFallback="{{ true }}" />
+                                arg-noJsFallback="{{ true }}"
+                                arg-context="{$context}"
+                                arg-hash="{$hash}" />
                         </xf:foreach>
                     </ul>
                 </xf:formrow>


Diff for helper_attach_upload:
Diff:
--- helper_attach_upload.html    2019-11-07 05:33:02.000000000 +0000
+++ helper_attach_upload.html    2019-11-07 05:32:50.000000000 +0000
@@ -24,7 +24,7 @@
         arg-hiddenName="{$hiddenName}" />
 </xf:macro>
 
-<xf:macro name="uploaded_files_list" arg-attachments="{{ [] }}" arg-listClass="">
+<xf:macro name="uploaded_files_list" arg-attachments="{{ [] }}" arg-listClass="" arg-context="{{ null }}" arg-hash="{{ null }}">
     <xf:css src="attachments.less" />
     <ul class="attachUploadList {$listClass} js-attachmentFiles u-hidden {{ $attachments is not empty ? 'is-active' : '' }}">
         <li class="attachUploadList-insertAll js-attachmentInsertAllRow u-hidden">
@@ -39,13 +39,13 @@
             </span>
         </li>
     <xf:foreach loop="$attachments" value="$attachment">
-        <xf:macro name="uploaded_file" arg-attachment="{$attachment}" />
+        <xf:macro name="uploaded_file" arg-attachment="{$attachment}" arg-context="{$context}" arg-hash="{$hash}" />
     </xf:foreach>
     </ul>
     <xf:macro name="uploaded_file_template" />
 </xf:macro>
 
-<xf:macro name="uploaded_file" arg-attachment="!" arg-noJsFallback="{{ false }}">
+<xf:macro name="uploaded_file" arg-attachment="!" arg-noJsFallback="{{ false }}" arg-context="{{ null }}" arg-hash="{{ null }}">
     <li class="js-attachmentFile" data-attachment-id="{$attachment.attachment_id}">
         <div class="contentRow">
             <span class="contentRow-figure attachUploadList-figure">
@@ -68,15 +68,21 @@
             </span>
 
             <div class="contentRow-main">
+                <xf:if is="$context AND $hash">
+                    <xf:set var="$manageUrl" value="{{ link('attachments/upload', null, {'type': $attachment.content_type, 'context': $context, 'hash': $hash}) }}" />
+                <xf:else />
+                    <xf:set var="$manageUrl" value="{{ null }}" />
+                </xf:if>
+
                 <xf:if is="$noJsFallback">
                     <span class="contentRow-extra">
-                        <xf:button type="submit" class="button--small" name="delete" value="{$attachment.attachment_id}">
+                        <xf:button type="submit" class="button--small" name="delete" value="{$attachment.attachment_id}" data-manage-url="{{ $manageUrl }}">
                             {{ phrase('button.delete') }}
                         </xf:button>
                     </span>
                 <xf:else />
                     <span class="contentRow-extra u-jsOnly">
-                        <xf:button class="button--small js-attachmentAction" data-action="delete" data-type="{{ $attachment.is_video ? 'video' : 'image' }}">
+                        <xf:button class="button--small js-attachmentAction" data-action="delete" data-type="{{ $attachment.is_video ? 'video' : 'image' }}" data-manage-url="{{ $manageUrl }}">
                             {{ phrase('button.delete') }}
                         </xf:button>
                     </span>
@@ -134,14 +140,14 @@
                     <span class="contentRow-extra u-jsOnly">
                         <xf:mustache name="^uploading">
                             <xf:mustache name="#is_video">
-                                <xf:button class="button--small js-attachmentAction" data-action="delete" data-type="video">
+                                <button type="button" class="button--small js-attachmentAction button" data-action="delete" data-type="video" {{ mustache('#manage_url', 'data-manage-url="{{manage_url}}"') }}>
                                     {{ phrase('button.delete') }}
-                                </xf:button>
+                                </button>
                             </xf:mustache>
                             <xf:mustache name="^is_video">
-                                <xf:button class="button--small js-attachmentAction" data-action="delete" data-type="image">
+                                <button type="button" class="button--small js-attachmentAction button" data-action="delete" data-type="image" {{ mustache('#manage_url', 'data-manage-url="{{manage_url}}"') }}>
                                     {{ phrase('button.delete') }}
-                                </xf:button>
+                                </button>
                             </xf:mustache>
                         </xf:mustache>
                         <xf:mustache name="#uploading">
@@ -196,7 +202,7 @@
 </xf:macro>
 
 <xf:macro name="upload_block" arg-attachmentData="!" arg-forceHash="" arg-hiddenName="attachment_hash">
-    <xf:macro name="uploaded_files_list" arg-attachments="{$attachmentData.attachments}" />
+    <xf:macro name="uploaded_files_list" arg-attachments="{$attachmentData.attachments}" arg-context="{$attachmentData.context}" arg-hash="{{ $forceHash ? $forceHash : $attachmentData.hash }}" />
 
     <xf:macro name="upload_link_from_data"
         arg-attachmentData="{$attachmentData}"


Diff for quick_reply_macros:
Diff:
--- quick_reply_macros.html    2019-11-07 05:35:30.000000000 +0000
+++ quick_reply_macros.html    2019-11-07 05:36:45.000000000 +0000
@@ -107,6 +107,8 @@
     <xf:if is="$attachmentData">
         <xf:macro template="helper_attach_upload" name="uploaded_files_list"
             arg-attachments="{$attachmentData.attachments}"
+            arg-context="{$attachmentData.context}"
+            arg-hash="{$attachmentData.hash}"
             arg-listClass="attachUploadList--spaced" />
     </xf:if>
 
@@ -143,4 +145,4 @@
         <xf:hiddenval name="last_date" autocomplete="off">{$lastDate}</xf:hiddenval>
         <xf:hiddenval name="last_known_date" autocomplete="off">{$lastKnownDate}</xf:hiddenval>
     </div>
-</xf:macro>
\ No newline at end of file
+</xf:macro>


Diff for forum_post_quick_thread:
Diff:
--- forum_post_quick_thread.html    2019-11-07 05:38:37.000000000 +0000
+++ forum_post_quick_thread.html    2019-11-07 05:39:20.000000000 +0000
@@ -59,6 +59,8 @@
         <xf:if is="$attachmentData">
             <xf:macro template="helper_attach_upload" name="uploaded_files_list"
                 arg-attachments="{$attachmentData.attachments}"
+                arg-context="{$attachmentData.context}"
+                arg-hash="{$attachmentData.hash}"
                 arg-listClass="attachUploadList--spaced" />
         </xf:if>
 
Upvote 3
This suggestion has been closed. Votes are no longer accepted.
Top