Page MenuHomePhabricator

D9561.id.diff
No OneTemporary

D9561.id.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -8,7 +8,7 @@
'names' =>
array(
'core.pkg.css' => 'a8d8a51c',
- 'core.pkg.js' => '07b01d4f',
+ 'core.pkg.js' => '920e18bd',
'darkconsole.pkg.js' => 'ca8671ce',
'differential.pkg.css' => '4a93db37',
'differential.pkg.js' => 'eca39a2c',
@@ -202,7 +202,7 @@
'rsrc/externals/javelin/lib/Router.js' => '29274e2b',
'rsrc/externals/javelin/lib/URI.js' => 'd9a9b862',
'rsrc/externals/javelin/lib/Vector.js' => 'bd0aedcd',
- 'rsrc/externals/javelin/lib/Workflow.js' => '09b15cf1',
+ 'rsrc/externals/javelin/lib/Workflow.js' => '9a24d9c4',
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8',
'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b',
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '2295d074',
@@ -468,7 +468,7 @@
'rsrc/js/core/behavior-object-selector.js' => 'e6f67523',
'rsrc/js/core/behavior-oncopy.js' => 'c3e218fe',
'rsrc/js/core/behavior-phabricator-nav.js' => 'b5842a5e',
- 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'ba22863c',
+ 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '8ddb4ee2',
'rsrc/js/core/behavior-refresh-csrf.js' => '7814b593',
'rsrc/js/core/behavior-remarkup-preview.js' => 'f7379f45',
'rsrc/js/core/behavior-reorder-applications.js' => 'a8e3795d',
@@ -612,7 +612,7 @@
'javelin-behavior-phabricator-notification-example' => 'c51a6616',
'javelin-behavior-phabricator-object-selector' => 'e6f67523',
'javelin-behavior-phabricator-oncopy' => 'c3e218fe',
- 'javelin-behavior-phabricator-remarkup-assist' => 'ba22863c',
+ 'javelin-behavior-phabricator-remarkup-assist' => '8ddb4ee2',
'javelin-behavior-phabricator-reveal-content' => '8f24abfc',
'javelin-behavior-phabricator-search-typeahead' => 'fbeabd1e',
'javelin-behavior-phabricator-show-all-transactions' => '7c273581',
@@ -682,7 +682,7 @@
'javelin-view-interpreter' => '0c33c1a0',
'javelin-view-renderer' => '6c2b09a2',
'javelin-view-visitor' => 'efe49472',
- 'javelin-workflow' => '09b15cf1',
+ 'javelin-workflow' => '9a24d9c4',
'lightbox-attachment-css' => '7acac05d',
'maniphest-batch-editor' => '8f380ebc',
'maniphest-report-css' => '6fc16517',
@@ -871,18 +871,6 @@
array(
0 => 'javelin-install',
),
- '09b15cf1' =>
- array(
- 0 => 'javelin-stratcom',
- 1 => 'javelin-request',
- 2 => 'javelin-dom',
- 3 => 'javelin-vector',
- 4 => 'javelin-install',
- 5 => 'javelin-util',
- 6 => 'javelin-mask',
- 7 => 'javelin-uri',
- 8 => 'javelin-routable',
- ),
'0a3f3021' =>
array(
0 => 'javelin-behavior',
@@ -1478,6 +1466,17 @@
2 => 'javelin-stratcom',
3 => 'javelin-uri',
),
+ '8ddb4ee2' =>
+ array(
+ 0 => 'javelin-behavior',
+ 1 => 'javelin-stratcom',
+ 2 => 'javelin-dom',
+ 3 => 'phabricator-phtize',
+ 4 => 'phabricator-textareautils',
+ 5 => 'javelin-workflow',
+ 6 => 'javelin-vector',
+ 7 => 'javelin-util',
+ ),
'8ef9ab58' =>
array(
0 => 'javelin-behavior',
@@ -1537,6 +1536,18 @@
3 => 'javelin-dom',
4 => 'phabricator-draggable-list',
),
+ '9a24d9c4' =>
+ array(
+ 0 => 'javelin-stratcom',
+ 1 => 'javelin-request',
+ 2 => 'javelin-dom',
+ 3 => 'javelin-vector',
+ 4 => 'javelin-install',
+ 5 => 'javelin-util',
+ 6 => 'javelin-mask',
+ 7 => 'javelin-uri',
+ 8 => 'javelin-routable',
+ ),
'9b9197be' =>
array(
0 => 'javelin-behavior',
@@ -1712,16 +1723,6 @@
0 => 'javelin-install',
1 => 'javelin-dom',
),
- 'ba22863c' =>
- array(
- 0 => 'javelin-behavior',
- 1 => 'javelin-stratcom',
- 2 => 'javelin-dom',
- 3 => 'phabricator-phtize',
- 4 => 'phabricator-textareautils',
- 5 => 'javelin-workflow',
- 6 => 'javelin-vector',
- ),
'bd0aedcd' =>
array(
0 => 'javelin-install',
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
@@ -5,14 +5,62 @@
public function processRequest() {
$request = $this->getRequest();
- $user = $request->getUser();
+ $viewer = $request->getUser();
- $dialog = id(new AphrontDialogView())
- ->setUser($user)
+ $severity = AphrontErrorView::SEVERITY_NOTICE;
+ $errors = array(
+ pht(
+ 'To upload files more easily, drag and drop them directly into '.
+ 'the text of your comment.'));
+
+ $e_file = true;
+ if ($request->isFormPost()) {
+ $errors = array();
+ $severity = AphrontErrorView::SEVERITY_ERROR;
+
+ if (!$request->getFileExists('file')) {
+ $e_file = pht('Required');
+ $errors[] = pht('You must select a file to upload.');
+ } else {
+ try {
+ $file = PhabricatorFile::newFromPHPUpload(
+ $_FILES['file'],
+ array(
+ 'authorPHID' => $viewer->getPHID(),
+ 'isExplicitUpload' => true,
+ ));
+ } catch (Exception $ex) {
+ $e_file = pht('Error');
+ $errors[] = $ex->getMessage();
+ }
+ }
+
+ if (!$errors) {
+ return id(new AphrontAjaxResponse())->setContent(
+ array(
+ 'id' => $file->getID(),
+ 'phid' => $file->getPHID(),
+ 'uri' => $file->getBestURI(),
+ ));
+ }
+ }
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild(
+ id(new AphrontFormFileControl())
+ ->setName('file')
+ ->setError($e_file));
+
+ $dialog = $this->newDialog()
+ ->setFormEncoding('multipart/form-data')
->setTitle(pht('Upload File'))
- ->appendChild(pht(
- 'To add files, drag and drop them into the comment text area.'))
- ->addCancelButton('/', pht('Close'));
+ ->setWidth(AphrontDialogView::WIDTH_FORM)
+ ->setErrors($errors)
+ ->setErrorSeverity($severity)
+ ->appendChild($form->buildLayoutView())
+ ->addCancelButton('/', pht('Close'))
+ ->addSubmitButton(pht('Upload File'));
return id(new AphrontDialogResponse())->setDialog($dialog);
}
diff --git a/src/view/AphrontDialogView.php b/src/view/AphrontDialogView.php
--- a/src/view/AphrontDialogView.php
+++ b/src/view/AphrontDialogView.php
@@ -22,17 +22,28 @@
private $errors = array();
private $flush;
private $validationException;
-
+ private $errorSeverity = AphrontErrorView::SEVERITY_ERROR;
+ private $formEncoding;
const WIDTH_DEFAULT = 'default';
const WIDTH_FORM = 'form';
const WIDTH_FULL = 'full';
+ public function setFormEncoding($form_encoding) {
+ $this->formEncoding = $form_encoding;
+ return $this;
+ }
+
public function setMethod($method) {
$this->method = $method;
return $this;
}
+ public function setErrorSeverity($error_severity) {
+ $this->errorSeverity = $error_severity;
+ return $this;
+ }
+
public function setIsStandalone($is_standalone) {
$this->isStandalone = $is_standalone;
return $this;
@@ -240,9 +251,10 @@
);
$form_attributes = array(
- 'action' => $this->submitURI,
- 'method' => $this->method,
- 'id' => $this->formID,
+ 'action' => $this->submitURI,
+ 'method' => $this->method,
+ 'id' => $this->formID,
+ 'enctype' => $this->formEncoding,
);
$hidden_inputs = array();
@@ -287,7 +299,9 @@
if ($errors) {
$children = array(
- id(new AphrontErrorView())->setErrors($errors),
+ id(new AphrontErrorView())
+ ->setErrors($errors)
+ ->setSeverity($this->errorSeverity),
$children);
}
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
@@ -33,9 +33,19 @@
statics : {
_stack : [],
newFromForm : function(form, data) {
- var pairs = JX.DOM.convertFormToListOfPairs(form);
- for (var k in data) {
- pairs.push([k, data[k]]);
+
+ var workflow = new JX.Workflow(form.getAttribute('action'), {});
+ workflow.setMethod(form.getAttribute('method'));
+
+ // If the form is "multipart/form-data", use FormData to marshall
+ if (form.enctype == 'multipart/form-data') {
+ workflow.setRawData(new FormData(form));
+ } else {
+ var pairs = JX.DOM.convertFormToListOfPairs(form);
+ for (var k in data) {
+ pairs.push([k, data[k]]);
+ }
+ workflow.setDataWithListOfPairs(pairs);
}
// Disable form elements during the request
@@ -51,15 +61,13 @@
}
}
- var workflow = new JX.Workflow(form.getAttribute('action'), {});
- workflow.setDataWithListOfPairs(pairs);
- workflow.setMethod(form.getAttribute('method'));
workflow.listen('finally', function() {
// Re-enable form elements
for (var ii = 0; ii < inputs.length; ii++) {
inputs[ii] && (inputs[ii].disabled = false);
}
});
+
return workflow;
},
newFromLink : function(link) {
@@ -130,17 +138,35 @@
return;
}
- var data = JX.DOM.convertFormToListOfPairs(form);
- data.push([button.name, button.value || true]);
+ var data;
+ var raw_data;
+ if (form.enctype == 'multipart/form-data') {
+ raw_data = new FormData(form);
+ raw_data.append(button.name, button.value || 'true');
+ } else {
+ data = JX.DOM.convertFormToListOfPairs(form);
+ data.push([button.name, button.value || true]);
+ }
var active = JX.Workflow._getActiveWorkflow();
- var e = active.invoke('submit', {form: form, data: data});
+ var submit_data = {
+ form: form,
+ data: data,
+ rawData: raw_data
+ };
+
+ var e = active.invoke('submit', submit_data);
if (!e.getStopped()) {
active._destroy();
- active
- .setURI(form.getAttribute('action') || active.getURI())
- .setDataWithListOfPairs(data)
- .start();
+ active.setURI(form.getAttribute('action') || active.getURI());
+
+ if (raw_data) {
+ active.setRawData(raw_data);
+ } else {
+ active.setDataWithListOfPairs(data);
+ }
+
+ active.start();
}
},
_getActiveWorkflow : function() {
@@ -153,6 +179,7 @@
_root : null,
_pushed : false,
_data : null,
+ _rawData: null,
_onload : function(r) {
// 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.
@@ -240,13 +267,23 @@
var uri = this.getURI();
var method = this.getMethod();
var r = new JX.Request(uri, JX.bind(this, this._onload));
- var list_of_pairs = this._data;
- list_of_pairs.push(['__wflow__', true]);
- r.setDataWithListOfPairs(list_of_pairs);
- r.setDataSerializer(this.getDataSerializer());
+
+ // If we have raw data, use that. Normally, this means we're submitting
+ // a form which includes a file input.
+ if (this._rawData) {
+ this._rawData.append('__wflow__', 'true');
+ r.setRawData(this._rawData);
+ } else {
+ var list_of_pairs = this._data;
+ list_of_pairs.push(['__wflow__', true]);
+ r.setDataWithListOfPairs(list_of_pairs);
+ r.setDataSerializer(this.getDataSerializer());
+ }
+
if (method) {
r.setMethod(method);
}
+
r.listen('finally', JX.bind(this, this.invoke, 'finally'));
r.listen('error', JX.bind(this, function(error) {
var e = this.invoke('error', error);
@@ -257,6 +294,7 @@
// user to "/error/". We could emit a blanket 'workflow-failed' type
// event instead.
}));
+
r.send();
},
@@ -280,6 +318,11 @@
return this;
},
+ setRawData: function(form_data) {
+ this._rawData = form_data;
+ return this;
+ },
+
setDataWithListOfPairs : function(list_of_pairs) {
this._data = list_of_pairs;
return this;
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
@@ -7,6 +7,7 @@
* phabricator-textareautils
* javelin-workflow
* javelin-vector
+ * javelin-util
*/
JX.behavior('phabricator-remarkup-assist', function(config) {
@@ -93,6 +94,11 @@
range.start + l.length + m.length);
}
+ function onupload(area, response) {
+ var ref = '{F' + response.id + '}';
+ JX.TextAreaUtils.setSelectionText(area, ref);
+ }
+
function assist(area, action, root) {
// If the user has some text selected, we'll try to use that (for example,
// if they have a word selected and want to bold it). Otherwise we'll insert
@@ -150,7 +156,9 @@
.start();
break;
case 'fa-cloud-upload':
- new JX.Workflow('/file/uploaddialog/').start();
+ new JX.Workflow('/file/uploaddialog/')
+ .setHandler(JX.bind(null, onupload, area))
+ .start();
break;
case 'fa-arrows-alt':
if (edit_mode == 'fa-arrows-alt') {

File Metadata

Mime Type
text/plain
Expires
Sat, May 18, 4:33 AM (2 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6275032
Default Alt Text
D9561.id.diff (13 KB)

Event Timeline