Page MenuHomePhabricator

D15953.id38413.diff
No OneTemporary

D15953.id38413.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -8,10 +8,10 @@
return array(
'names' => array(
'core.pkg.css' => '204cabae',
- 'core.pkg.js' => '6972d365',
+ 'core.pkg.js' => '9f2969e9',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '33da0633',
- 'differential.pkg.js' => 'd0cd0df6',
+ 'differential.pkg.js' => '4b7d8f19',
'diffusion.pkg.css' => '91c5d3a6',
'diffusion.pkg.js' => '3a9a8bfa',
'maniphest.pkg.css' => '4845691a',
@@ -245,7 +245,7 @@
'rsrc/externals/javelin/lib/URI.js' => 'c989ade3',
'rsrc/externals/javelin/lib/Vector.js' => '2caa8fb8',
'rsrc/externals/javelin/lib/WebSocket.js' => 'e292eaf4',
- 'rsrc/externals/javelin/lib/Workflow.js' => '28cfbdd0',
+ 'rsrc/externals/javelin/lib/Workflow.js' => '0eb34d1d',
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8',
'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b',
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '837a7d68',
@@ -457,7 +457,7 @@
'rsrc/js/application/uiexample/gesture-example.js' => '558829c2',
'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
'rsrc/js/core/Busy.js' => '59a7976a',
- 'rsrc/js/core/DragAndDropFileUpload.js' => '81f182b5',
+ 'rsrc/js/core/DragAndDropFileUpload.js' => '58dea2fa',
'rsrc/js/core/DraggableList.js' => '5a13c79f',
'rsrc/js/core/FileUpload.js' => '680ea2c8',
'rsrc/js/core/Hovercard.js' => '1bd28176',
@@ -467,7 +467,7 @@
'rsrc/js/core/Notification.js' => 'ccf1cbf8',
'rsrc/js/core/Prefab.js' => 'e67df814',
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
- 'rsrc/js/core/TextAreaUtils.js' => '5813016a',
+ 'rsrc/js/core/TextAreaUtils.js' => '320810c8',
'rsrc/js/core/Title.js' => 'df5e11d2',
'rsrc/js/core/ToolTip.js' => '6323f942',
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
@@ -478,7 +478,7 @@
'rsrc/js/core/behavior-crop.js' => 'fa0f4fc2',
'rsrc/js/core/behavior-dark-console.js' => 'f411b6ae',
'rsrc/js/core/behavior-device.js' => 'b5b36110',
- 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '4f6a4b4e',
+ 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '484a6e22',
'rsrc/js/core/behavior-error-log.js' => '6882e80a',
'rsrc/js/core/behavior-fancy-datepicker.js' => '568931f3',
'rsrc/js/core/behavior-file-tree.js' => '88236f00',
@@ -496,7 +496,7 @@
'rsrc/js/core/behavior-object-selector.js' => '49b73b36',
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03',
- 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '340c8eff',
+ 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '116cf19b',
'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207',
'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b',
'rsrc/js/core/behavior-remarkup-preview.js' => '4b700e9e',
@@ -514,6 +514,7 @@
'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
'rsrc/js/core/phtize.js' => 'd254d646',
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '54733475',
+ 'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb',
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
'rsrc/js/phui/behavior-phui-profile-menu.js' => '12884df9',
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
@@ -578,7 +579,7 @@
'javelin-behavior-aphlict-status' => 'ea681761',
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
'javelin-behavior-aphront-crop' => 'fa0f4fc2',
- 'javelin-behavior-aphront-drag-and-drop-textarea' => '4f6a4b4e',
+ 'javelin-behavior-aphront-drag-and-drop-textarea' => '484a6e22',
'javelin-behavior-aphront-form-disable-on-submit' => '5c54cbf3',
'javelin-behavior-aphront-more' => 'a80d0378',
'javelin-behavior-audio-source' => '59b251eb',
@@ -654,7 +655,7 @@
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
'javelin-behavior-phabricator-object-selector' => '49b73b36',
'javelin-behavior-phabricator-oncopy' => '2926fff2',
- 'javelin-behavior-phabricator-remarkup-assist' => '340c8eff',
+ 'javelin-behavior-phabricator-remarkup-assist' => '116cf19b',
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
'javelin-behavior-phabricator-search-typeahead' => '06c32383',
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
@@ -665,6 +666,7 @@
'javelin-behavior-pholio-mock-edit' => '246dc085',
'javelin-behavior-pholio-mock-view' => 'fbe497e7',
'javelin-behavior-phui-dropdown-menu' => '54733475',
+ 'javelin-behavior-phui-file-upload' => 'b003d4fb',
'javelin-behavior-phui-hovercards' => 'bcaccd64',
'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
'javelin-behavior-phui-profile-menu' => '12884df9',
@@ -743,7 +745,7 @@
'javelin-workboard-card' => 'c587b80f',
'javelin-workboard-column' => 'bae58312',
'javelin-workboard-controller' => '55baf5ed',
- 'javelin-workflow' => '28cfbdd0',
+ 'javelin-workflow' => '0eb34d1d',
'lightbox-attachment-css' => '7acac05d',
'maniphest-batch-editor' => 'b0f0b6d5',
'maniphest-report-css' => '9b9580b7',
@@ -763,7 +765,7 @@
'phabricator-core-css' => 'd0801452',
'phabricator-countdown-css' => '16c52f5c',
'phabricator-dashboard-css' => 'bc6f2127',
- 'phabricator-drag-and-drop-file-upload' => '81f182b5',
+ 'phabricator-drag-and-drop-file-upload' => '58dea2fa',
'phabricator-draggable-list' => '5a13c79f',
'phabricator-fatal-config-template-css' => '8e6c6fcd',
'phabricator-feed-css' => 'ecd4ec57',
@@ -787,7 +789,7 @@
'phabricator-slowvote-css' => 'a94b7230',
'phabricator-source-code-view-css' => 'cbeef983',
'phabricator-standard-page-view' => 'e709f6d0',
- 'phabricator-textareautils' => '5813016a',
+ 'phabricator-textareautils' => '320810c8',
'phabricator-title' => 'df5e11d2',
'phabricator-tooltip' => '6323f942',
'phabricator-ui-example-css' => '528b19de',
@@ -975,10 +977,31 @@
'javelin-dom',
'javelin-router',
),
+ '0eb34d1d' => array(
+ 'javelin-stratcom',
+ 'javelin-request',
+ 'javelin-dom',
+ 'javelin-vector',
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-mask',
+ 'javelin-uri',
+ 'javelin-routable',
+ ),
'0f764c35' => array(
'javelin-install',
'javelin-util',
),
+ '116cf19b' => array(
+ 'javelin-behavior',
+ 'javelin-stratcom',
+ 'javelin-dom',
+ 'phabricator-phtize',
+ 'phabricator-textareautils',
+ 'javelin-workflow',
+ 'javelin-vector',
+ 'phuix-autocomplete',
+ ),
'12884df9' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1075,17 +1098,6 @@
'phabricator-drag-and-drop-file-upload',
'phabricator-draggable-list',
),
- '28cfbdd0' => array(
- 'javelin-stratcom',
- 'javelin-request',
- 'javelin-dom',
- 'javelin-vector',
- 'javelin-install',
- 'javelin-util',
- 'javelin-mask',
- 'javelin-uri',
- 'javelin-routable',
- ),
'2926fff2' => array(
'javelin-behavior',
'javelin-dom',
@@ -1116,21 +1128,16 @@
'2ee659ce' => array(
'javelin-install',
),
- '327a00d1' => array(
- 'javelin-behavior',
- 'javelin-stratcom',
+ '320810c8' => array(
+ 'javelin-install',
'javelin-dom',
- 'javelin-workflow',
+ 'javelin-vector',
),
- '340c8eff' => array(
+ '327a00d1' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
- 'phabricator-phtize',
- 'phabricator-textareautils',
'javelin-workflow',
- 'javelin-vector',
- 'phuix-autocomplete',
),
'3ab51e2c' => array(
'javelin-behavior',
@@ -1199,6 +1206,12 @@
'javelin-dom',
'javelin-workflow',
),
+ '484a6e22' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'phabricator-drag-and-drop-file-upload',
+ 'phabricator-textareautils',
+ ),
'49b73b36' => array(
'javelin-behavior',
'javelin-dom',
@@ -1216,12 +1229,6 @@
'javelin-stratcom',
'javelin-dom',
),
- '4f6a4b4e' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'phabricator-drag-and-drop-file-upload',
- 'phabricator-textareautils',
- ),
'4fbbc3e9' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1319,10 +1326,13 @@
'javelin-request',
'javelin-util',
),
- '5813016a' => array(
+ '58dea2fa' => array(
'javelin-install',
+ 'javelin-util',
+ 'javelin-request',
'javelin-dom',
- 'javelin-vector',
+ 'javelin-uri',
+ 'phabricator-file-upload',
),
'59a7976a' => array(
'javelin-install',
@@ -1516,14 +1526,6 @@
'javelin-vector',
'javelin-stratcom',
),
- '81f182b5' => array(
- 'javelin-install',
- 'javelin-util',
- 'javelin-request',
- 'javelin-dom',
- 'javelin-uri',
- 'phabricator-file-upload',
- ),
'834a1173' => array(
'javelin-behavior',
'javelin-scrollbar',
@@ -1740,6 +1742,12 @@
'javelin-util',
'phabricator-busy',
),
+ 'b003d4fb' => array(
+ 'javelin-behavior',
+ 'javelin-stratcom',
+ 'javelin-dom',
+ 'phuix-dropdown-menu',
+ ),
'b064af76' => array(
'javelin-behavior',
'javelin-stratcom',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1594,6 +1594,7 @@
'PHUIFeedStoryExample' => 'applications/uiexample/examples/PHUIFeedStoryExample.php',
'PHUIFeedStoryView' => 'view/phui/PHUIFeedStoryView.php',
'PHUIFormDividerControl' => 'view/form/control/PHUIFormDividerControl.php',
+ 'PHUIFormFileControl' => 'view/form/control/PHUIFormFileControl.php',
'PHUIFormFreeformDateControl' => 'view/form/control/PHUIFormFreeformDateControl.php',
'PHUIFormIconSetControl' => 'view/form/control/PHUIFormIconSetControl.php',
'PHUIFormInsetView' => 'view/form/PHUIFormInsetView.php',
@@ -5996,6 +5997,7 @@
'PHUIFeedStoryExample' => 'PhabricatorUIExample',
'PHUIFeedStoryView' => 'AphrontView',
'PHUIFormDividerControl' => 'AphrontFormControl',
+ 'PHUIFormFileControl' => 'AphrontFormControl',
'PHUIFormFreeformDateControl' => 'AphrontFormControl',
'PHUIFormIconSetControl' => 'AphrontFormControl',
'PHUIFormInsetView' => 'AphrontView',
diff --git a/src/applications/files/controller/PhabricatorFileDropUploadController.php b/src/applications/files/controller/PhabricatorFileDropUploadController.php
--- a/src/applications/files/controller/PhabricatorFileDropUploadController.php
+++ b/src/applications/files/controller/PhabricatorFileDropUploadController.php
@@ -56,7 +56,7 @@
$file_phid = $result['filePHID'];
if ($file_phid) {
$file = $this->loadFile($file_phid);
- $result += $this->getFileDictionary($file);
+ $result += $file->getDragAndDropDictionary();
}
return id(new AphrontAjaxResponse())->setContent($result);
@@ -84,7 +84,7 @@
} else {
$result = array(
'complete' => true,
- ) + $this->getFileDictionary($file);
+ ) + $file->getDragAndDropDictionary();
}
return id(new AphrontAjaxResponse())->setContent($result);
@@ -99,18 +99,10 @@
'isExplicitUpload' => true,
));
- $result = $this->getFileDictionary($file);
+ $result = $file->getDragAndDropDictionary();
return id(new AphrontAjaxResponse())->setContent($result);
}
- private function getFileDictionary(PhabricatorFile $file) {
- return array(
- 'id' => $file->getID(),
- 'phid' => $file->getPHID(),
- 'uri' => $file->getBestURI(),
- );
- }
-
private function loadFile($file_phid) {
$viewer = $this->getViewer();
diff --git a/src/applications/files/controller/PhabricatorFileUploadDialogController.php b/src/applications/files/controller/PhabricatorFileUploadDialogController.php
--- a/src/applications/files/controller/PhabricatorFileUploadDialogController.php
+++ b/src/applications/files/controller/PhabricatorFileUploadDialogController.php
@@ -6,12 +6,51 @@
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
- return $this->newDialog()
- ->setTitle(pht('Upload File'))
- ->appendChild(pht(
- 'To add files, drag and drop them into the comment text area.'))
- ->addCancelButton('/', pht('Close'));
+ $e_file = true;
+ $errors = array();
+ if ($request->isDialogFormPost()) {
+ $file_phids = $request->getStrList('filePHIDs');
+ if ($file_phids) {
+ $files = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($file_phids)
+ ->setRaisePolicyExceptions(true)
+ ->execute();
+ } else {
+ $files = array();
+ }
+
+ if ($files) {
+ $results = array();
+ foreach ($files as $file) {
+ $results[] = $file->getDragAndDropDictionary();
+ }
+
+ $content = array(
+ 'files' => $results,
+ );
+ return id(new AphrontAjaxResponse())->setContent($content);
+ } else {
+ $e_file = pht('Required');
+ $errors[] = pht('You must choose a file to upload.');
+ }
+ }
+
+ $form = id(new AphrontFormView())
+ ->appendChild(
+ id(new PHUIFormFileControl())
+ ->setName('filePHIDs')
+ ->setLabel(pht('Upload File'))
+ ->setAllowMultiple(true)
+ ->setError($e_file));
+
+ return $this->newDialog()
+ ->setTitle(pht('File'))
+ ->setErrors($errors)
+ ->appendForm($form)
+ ->addSubmitButton(pht('Upload'))
+ ->addCancelButton('/');
}
}
diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php
--- a/src/applications/files/storage/PhabricatorFile.php
+++ b/src/applications/files/storage/PhabricatorFile.php
@@ -851,6 +851,14 @@
return $supported;
}
+ public function getDragAndDropDictionary() {
+ return array(
+ 'id' => $this->getID(),
+ 'phid' => $this->getPHID(),
+ 'uri' => $this->getBestURI(),
+ );
+ }
+
public function instantiateStorageEngine() {
return self::buildEngine($this->getStorageEngine());
}
diff --git a/src/view/form/control/PHUIFormFileControl.php b/src/view/form/control/PHUIFormFileControl.php
new file mode 100644
--- /dev/null
+++ b/src/view/form/control/PHUIFormFileControl.php
@@ -0,0 +1,44 @@
+<?php
+
+final class PHUIFormFileControl
+ extends AphrontFormControl {
+
+ private $allowMultiple;
+
+ protected function getCustomControlClass() {
+ return 'phui-form-file-upload';
+ }
+
+ public function setAllowMultiple($allow_multiple) {
+ $this->allowMultiple = $allow_multiple;
+ return $this;
+ }
+
+ public function getAllowMultiple() {
+ return $this->allowMultiple;
+ }
+
+ protected function renderInput() {
+ $file_id = $this->getID();
+
+ Javelin::initBehavior(
+ 'phui-file-upload',
+ array(
+ 'fileInputID' => $file_id,
+ 'inputName' => $this->getName(),
+ 'uploadURI' => '/file/dropupload/',
+ 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
+ ));
+
+ return phutil_tag(
+ 'input',
+ array(
+ 'type' => 'file',
+ 'multiple' => $this->getAllowMultiple() ? 'multiple' : null,
+ 'name' => $this->getName().'.raw',
+ 'id' => $file_id,
+ 'disabled' => $this->getDisabled() ? 'disabled' : null,
+ ));
+ }
+
+}
diff --git a/webroot/rsrc/externals/javelin/lib/Workflow.js b/webroot/rsrc/externals/javelin/lib/Workflow.js
--- a/webroot/rsrc/externals/javelin/lib/Workflow.js
+++ b/webroot/rsrc/externals/javelin/lib/Workflow.js
@@ -25,7 +25,7 @@
this.setData(data || {});
},
- events : ['error', 'finally', 'submit'],
+ events : ['error', 'finally', 'submit', 'start'],
statics : {
_stack : [],
@@ -54,6 +54,9 @@
}
var workflow = new JX.Workflow(form.getAttribute('action'), {});
+
+ workflow._form = form;
+
workflow.setDataWithListOfPairs(pairs);
workflow.setMethod(form.getAttribute('method'));
workflow.listen('finally', function() {
@@ -137,9 +140,14 @@
data.push([button.name, button.value || true]);
var active = JX.Workflow._getActiveWorkflow();
+
+ active._form = form;
+
var e = active.invoke('submit', {form: form, data: data});
if (!e.getStopped()) {
- active._destroy();
+ // NOTE: Don't remove the current dialog yet because additional
+ // handlers may still want to access the nodes.
+
active
.setURI(form.getAttribute('action') || active.getURI())
.setDataWithListOfPairs(data)
@@ -156,7 +164,41 @@
_root : null,
_pushed : false,
_data : null,
+
+ _form: null,
+ _paused: 0,
+ _nextCallback: null,
+
+ getSourceForm: function() {
+ return this._form;
+ },
+
+ pause: function() {
+ this._paused++;
+ return this;
+ },
+
+ resume: function() {
+ if (!this._paused) {
+ JX.$E('Resuming a workflow which is not paused!');
+ }
+
+ this._paused--;
+
+ if (!this._paused) {
+ var next = this._nextCallback;
+ this._nextCallback = null;
+ if (next) {
+ next();
+ }
+ }
+
+ return this;
+ },
+
_onload : function(r) {
+ this._destroy();
+
// It is permissible to send back a falsey redirect to force a page
// reload, so we need to take this branch if the key is present.
if (r && (typeof r.redirect != 'undefined')) {
@@ -247,7 +289,19 @@
this._root = null;
}
},
+
start : function() {
+ var next = JX.bind(this, this._send);
+
+ this.pause();
+ this._nextCallback = next;
+
+ this.invoke('start', this);
+
+ this.resume();
+ },
+
+ _send: function() {
var uri = this.getURI();
var method = this.getMethod();
var r = new JX.Request(uri, JX.bind(this, this._onload));
@@ -291,6 +345,11 @@
return this;
},
+ addData: function(key, value) {
+ this._data.push([key, value]);
+ return this;
+ },
+
setDataWithListOfPairs : function(list_of_pairs) {
this._data = list_of_pairs;
return this;
diff --git a/webroot/rsrc/js/core/DragAndDropFileUpload.js b/webroot/rsrc/js/core/DragAndDropFileUpload.js
--- a/webroot/rsrc/js/core/DragAndDropFileUpload.js
+++ b/webroot/rsrc/js/core/DragAndDropFileUpload.js
@@ -155,7 +155,7 @@
var files = e.getRawEvent().dataTransfer.files;
for (var ii = 0; ii < files.length; ii++) {
- this._sendRequest(files[ii]);
+ this.sendRequest(files[ii]);
}
// Force depth to 0.
@@ -216,7 +216,7 @@
if (!spec.name) {
spec.name = 'pasted_file';
}
- this._sendRequest(spec);
+ this.sendRequest(spec);
}
}));
}
@@ -224,7 +224,7 @@
this.setIsEnabled(true);
},
- _sendRequest : function(spec) {
+ sendRequest : function(spec) {
var file = new JX.PhabricatorFileUpload()
.setRawFileObject(spec)
.setName(spec.name)
diff --git a/webroot/rsrc/js/core/TextAreaUtils.js b/webroot/rsrc/js/core/TextAreaUtils.js
--- a/webroot/rsrc/js/core/TextAreaUtils.js
+++ b/webroot/rsrc/js/core/TextAreaUtils.js
@@ -62,6 +62,26 @@
JX.TextAreaUtils.setSelectionRange(area, start, end);
},
+
+ /**
+ * Insert a reference to a given uploaded file into a textarea.
+ */
+ insertFileReference: function(area, file) {
+ var ref = '{F' + file.getID() + '}';
+
+ // If we're inserting immediately after a "}" (usually, another file
+ // reference), put some newlines before our token so that multiple file
+ // uploads get laid out more nicely.
+ var range = JX.TextAreaUtils.getSelectionRange(area);
+ var before = area.value.substring(0, range.start);
+ if (before.match(/\}$/)) {
+ ref = '\n\n' + ref;
+ }
+
+ JX.TextAreaUtils.setSelectionText(area, ref, false);
+ },
+
+
/**
* Get the document pixel positions of the beginning and end of a character
* range in a textarea.
diff --git a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js
--- a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js
+++ b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js
@@ -10,32 +10,23 @@
var target = JX.$(config.target);
- function onupload(f) {
- var ref = '{F' + f.getID() + '}';
-
- // If we're inserting immediately after a "}" (usually, another file
- // reference), put some newlines before our token so that multiple file
- // uploads get laid out more nicely.
- var range = JX.TextAreaUtils.getSelectionRange(target);
- var before = target.value.substring(0, range.start);
- if (before.match(/\}$/)) {
- ref = '\n\n' + ref;
- }
-
- JX.TextAreaUtils.setSelectionText(target, ref, false);
- }
-
if (JX.PhabricatorDragAndDropFileUpload.isSupported()) {
var drop = new JX.PhabricatorDragAndDropFileUpload(target)
.setURI(config.uri)
.setChunkThreshold(config.chunkThreshold);
+
drop.listen('didBeginDrag', function() {
JX.DOM.alterClass(target, config.activatedClass, true);
});
+
drop.listen('didEndDrag', function() {
JX.DOM.alterClass(target, config.activatedClass, false);
});
- drop.listen('didUpload', onupload);
+
+ drop.listen('didUpload', function(file) {
+ JX.TextAreaUtils.insertFileReference(target, file);
+ });
+
drop.start();
}
diff --git a/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js b/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js
--- a/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js
+++ b/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js
@@ -194,7 +194,21 @@
.start();
break;
case 'fa-cloud-upload':
- new JX.Workflow('/file/uploaddialog/').start();
+ new JX.Workflow('/file/uploaddialog/')
+ .setHandler(function(response) {
+ var files = response.files;
+ for (var ii = 0; ii < files.length; ii++) {
+ var file = files[ii];
+
+ var upload = new JX.PhabricatorFileUpload()
+ .setID(file.id)
+ .setPHID(file.phid)
+ .setURI(file.uri);
+
+ JX.TextAreaUtils.insertFileReference(area, upload);
+ }
+ })
+ .start();
break;
case 'fa-arrows-alt':
if (edit_mode == 'fa-arrows-alt') {
diff --git a/webroot/rsrc/js/phui/behavior-phui-file-upload.js b/webroot/rsrc/js/phui/behavior-phui-file-upload.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/phui/behavior-phui-file-upload.js
@@ -0,0 +1,80 @@
+/**
+ * @provides javelin-behavior-phui-file-upload
+ * @requires javelin-behavior
+ * javelin-stratcom
+ * javelin-dom
+ * phuix-dropdown-menu
+ */
+
+JX.behavior('phui-file-upload', function(config) {
+
+ function startUpload(workflow, input) {
+ var files = input.files;
+
+ if (!files || !files.length) {
+ return;
+ }
+
+ var state = {
+ workflow: workflow,
+ input: input,
+ waiting: 0,
+ phids: []
+ };
+
+ var callback = JX.bind(null, didUpload, state);
+
+ var dummy = input;
+ var uploader = new JX.PhabricatorDragAndDropFileUpload(dummy)
+ .setURI(config.uploadURI)
+ .setChunkThreshold(config.chunkThreshold);
+
+ uploader.listen('didUpload', callback);
+ uploader.start();
+
+ workflow.pause();
+ for (var ii = 0; ii < files.length; ii++) {
+ state.waiting++;
+ uploader.sendRequest(files[ii]);
+ }
+ }
+
+ function didUpload(state, file) {
+ state.phids.push(file.getPHID());
+ state.waiting--;
+
+ if (state.waiting) {
+ return;
+ }
+
+ state.workflow
+ .addData(config.inputName, state.phids.join(', '))
+ .resume();
+ }
+
+ JX.Workflow.listen('start', function(workflow) {
+ var form = workflow.getSourceForm();
+ if (!form) {
+ return;
+ }
+
+ var input;
+ try {
+ input = JX.$(config.fileInputID);
+ } catch (ex) {
+ return;
+ }
+
+ var local_form = JX.DOM.findAbove(input, 'form');
+ if (!local_form) {
+ return;
+ }
+
+ if (local_form !== form) {
+ return;
+ }
+
+ startUpload(workflow, input);
+ });
+
+});

File Metadata

Mime Type
text/plain
Expires
Sun, Jan 12, 10:07 PM (21 h, 10 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6987901
Default Alt Text
D15953.id38413.diff (24 KB)

Event Timeline