Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14658786
D15953.id38413.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
24 KB
Referenced Files
None
Subscribers
None
D15953.id38413.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D15953: Provide an `<input type="file">` control in Remarkup for mobile and users with esoteric windowing systems
Attached
Detach File
Event Timeline
Log In to Comment