1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-13 23:38:34 +01:00

Quicksand - make things work correctly with global drag and drop upload

Summary: Fixes T7685. This required making the global drag and drop behavior able to "uninstall" itself so to speak, and then it re-installs it self as necessary.

Test Plan:
Did the following all successfully

 - uploaded a file to homepage
 - homepage -> differential -- no way to upload via drag and drop
 - homepage -> differential -> homepage -- uploaded a file
 - homepage -> differential -> browser back button to homepage -- uploaded a file

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T7685

Differential Revision: https://secure.phabricator.com/D12534
This commit is contained in:
Bob Trahan 2015-04-23 15:08:35 -07:00
parent cf154ae9f4
commit e40aa8f782
9 changed files with 179 additions and 87 deletions

View file

@ -8,10 +8,10 @@
return array(
'names' => array(
'core.pkg.css' => '9a9b59ca',
'core.pkg.js' => '348d5193',
'core.pkg.js' => '493cc6e6',
'darkconsole.pkg.js' => '8ab24e01',
'differential.pkg.css' => '3500921f',
'differential.pkg.js' => 'c0506961',
'differential.pkg.js' => '890046d3',
'diffusion.pkg.css' => '591664fa',
'diffusion.pkg.js' => '0115b37c',
'maniphest.pkg.css' => '68d4dd3d',
@ -442,7 +442,7 @@ return array(
'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' => '7fa4b248',
'rsrc/js/core/DragAndDropFileUpload.js' => '07de8873',
'rsrc/js/core/DraggableList.js' => 'a16ec1c6',
'rsrc/js/core/FileUpload.js' => '477359c8',
'rsrc/js/core/Hovercard.js' => '7e8468ae',
@ -468,7 +468,7 @@ return array(
'rsrc/js/core/behavior-file-tree.js' => '88236f00',
'rsrc/js/core/behavior-form.js' => '5c54cbf3',
'rsrc/js/core/behavior-gesture.js' => '3ab51e2c',
'rsrc/js/core/behavior-global-drag-and-drop.js' => 'bbdf75ca',
'rsrc/js/core/behavior-global-drag-and-drop.js' => '3f6075ff',
'rsrc/js/core/behavior-high-security-warning.js' => '8fc1c918',
'rsrc/js/core/behavior-history-install.js' => '7ee2b591',
'rsrc/js/core/behavior-hovercard.js' => 'f36e01af',
@ -593,7 +593,7 @@ return array(
'javelin-behavior-durable-column' => '657c2b50',
'javelin-behavior-error-log' => '6882e80a',
'javelin-behavior-fancy-datepicker' => 'c51ae228',
'javelin-behavior-global-drag-and-drop' => 'bbdf75ca',
'javelin-behavior-global-drag-and-drop' => '3f6075ff',
'javelin-behavior-herald-rule-editor' => '7ebaeed3',
'javelin-behavior-high-security-warning' => '8fc1c918',
'javelin-behavior-history-install' => '7ee2b591',
@ -726,7 +726,7 @@ return array(
'phabricator-core-css' => '76e8ee93',
'phabricator-countdown-css' => '86b7b0a0',
'phabricator-dashboard-css' => '17937d22',
'phabricator-drag-and-drop-file-upload' => '7fa4b248',
'phabricator-drag-and-drop-file-upload' => '07de8873',
'phabricator-draggable-list' => 'a16ec1c6',
'phabricator-fatal-config-template-css' => '8e6c6fcd',
'phabricator-feed-css' => 'b513b5f4',
@ -869,6 +869,14 @@ return array(
'phabricator-shaped-request',
'conpherence-thread-manager',
),
'07de8873' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-dom',
'javelin-uri',
'phabricator-file-upload',
),
'08883e8b' => array(
'javelin-behavior',
'javelin-stratcom',
@ -1080,6 +1088,13 @@ return array(
'javelin-dom',
'phortune-credit-card-form',
),
'3f6075ff' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-mask',
'phabricator-drag-and-drop-file-upload',
),
'40a6a403' => array(
'javelin-install',
'javelin-dom',
@ -1413,14 +1428,6 @@ return array(
'javelin-behavior',
'javelin-history',
),
'7fa4b248' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-dom',
'javelin-uri',
'phabricator-file-upload',
),
82439934 => array(
'javelin-behavior',
'javelin-dom',
@ -1722,13 +1729,6 @@ return array(
'javelin-stratcom',
'javelin-dom',
),
'bbdf75ca' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-mask',
'phabricator-drag-and-drop-file-upload',
),
'bd4c8dca' => array(
'javelin-install',
'javelin-util',

View file

@ -57,6 +57,10 @@ abstract class PhabricatorController extends AphrontController {
return false;
}
public function isGlobalDragAndDropUploadEnabled() {
return false;
}
public function willBeginExecution() {
$request = $this->getRequest();

View file

@ -2,19 +2,17 @@
final class PhabricatorFileListController extends PhabricatorFileController {
private $key;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->key = idx($data, 'key');
public function isGlobalDragAndDropUploadEnabled() {
return true;
}
public function processRequest() {
public function handleRequest(AphrontRequest $request) {
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($this->key)
->setQueryKey($request->getURIData('key'))
->setSearchEngine(new PhabricatorFileSearchEngine())
->setNavigation($this->buildSideNavView());

View file

@ -2,8 +2,11 @@
final class PhabricatorFileUploadController extends PhabricatorFileController {
public function processRequest() {
$request = $this->getRequest();
public function isGlobalDragAndDropUploadEnabled() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$file = PhabricatorFile::initializeNewFile();

View file

@ -1,5 +1,16 @@
<?php
/**
* IMPORTANT: If you use this, make sure to implement
*
* public function isGlobalDragAndDropUploadEnabled() {
* return true;
* }
*
* on the controller(s) that render this class...! This is necessary
* to make sure Quicksand works properly with the javascript in this
* UI.
*/
final class PhabricatorGlobalUploadTargetView extends AphrontView {
private $showIfSupportedID;
@ -19,7 +30,7 @@ final class PhabricatorGlobalUploadTargetView extends AphrontView {
return null;
}
$instructions_id = celerity_generate_unique_node_id();
$instructions_id = 'phabricator-global-drag-and-drop-upload-instructions';
require_celerity_resource('global-drag-and-drop-css');

View file

@ -2,19 +2,18 @@
final class PhabricatorHomeMainController extends PhabricatorHomeController {
private $only;
private $minipanels = array();
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->only = idx($data, 'only');
public function isGlobalDragAndDropUploadEnabled() {
return true;
}
public function processRequest() {
$user = $this->getRequest()->getUser();
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$dashboard = PhabricatorDashboardInstall::getDashboard(
$user,
@ -42,7 +41,7 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController {
$content = $this->buildMainResponse($projects);
}
if (!$this->only) {
if (!$request->getURIData('only')) {
$nav = $this->buildNav();
$nav->appendChild(
array(

View file

@ -605,6 +605,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
private function buildQuicksandConfig() {
$viewer = $this->getRequest()->getUser();
$controller = $this->getController();
$dropdown_query = id(new AphlictDropdownDataQuery())
->setViewer($viewer);
@ -624,7 +625,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
$rendered_dropdowns[$application_class] =
$application->buildMainMenuExtraNodes(
$viewer,
$this->getController());
$controller);
}
$console_config = null;
@ -638,6 +639,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
$dropdown_query->getNotificationData(),
$dropdown_query->getConpherenceData(),
),
'globalDragAndDrop' => $controller->isGlobalDragAndDropUploadEnabled(),
'aphlictDropdowns' => $rendered_dropdowns,
'consoleConfig' => $console_config,
) + $this->buildAphlictListenConfigData();

View file

@ -40,6 +40,17 @@ JX.install('PhabricatorDragAndDropFileUpload', {
members : {
_node : null,
_depth : 0,
_isEnabled: false,
setIsEnabled: function(bool) {
this._isEnabled = bool;
return this;
},
getIsEnabled: function() {
return this._isEnabled;
},
_updateDepth : function(delta) {
if (this._depth === 0 && delta > 0) {
this.invoke('didBeginDrag');
@ -54,6 +65,7 @@ JX.install('PhabricatorDragAndDropFileUpload', {
start : function() {
// TODO: move this to JX.DOM.contains()?
function contains(container, child) {
do {
@ -73,6 +85,9 @@ JX.install('PhabricatorDragAndDropFileUpload', {
'click',
null,
JX.bind(this, function (e) {
if (!this.getIsEnabled()) {
return;
}
if (this._depth) {
e.kill();
// Force depth to 0.
@ -87,6 +102,10 @@ JX.install('PhabricatorDragAndDropFileUpload', {
'dragenter',
null,
JX.bind(this, function(e) {
if (!this.getIsEnabled()) {
return;
}
if (contains(this._node, e.getTarget())) {
this._updateDepth(1);
}
@ -97,6 +116,10 @@ JX.install('PhabricatorDragAndDropFileUpload', {
'dragleave',
null,
JX.bind(this, function(e) {
if (!this.getIsEnabled()) {
return;
}
if (contains(this._node, e.getTarget())) {
this._updateDepth(-1);
}
@ -106,18 +129,26 @@ JX.install('PhabricatorDragAndDropFileUpload', {
this._node,
'dragover',
null,
function(e) {
JX.bind(this, function(e) {
if (!this.getIsEnabled()) {
return;
}
// NOTE: We must set this, or Chrome refuses to drop files from the
// download shelf.
e.getRawEvent().dataTransfer.dropEffect = 'copy';
e.kill();
});
}));
JX.DOM.listen(
this._node,
'drop',
null,
JX.bind(this, function(e) {
if (!this.getIsEnabled()) {
return;
}
e.kill();
var files = e.getRawEvent().dataTransfer.files;
@ -135,6 +166,10 @@ JX.install('PhabricatorDragAndDropFileUpload', {
'paste',
null,
JX.bind(this, function(e) {
if (!this.getIsEnabled()) {
return;
}
var clipboard = e.getRawEvent().clipboardData;
if (!clipboard) {
return;
@ -168,6 +203,8 @@ JX.install('PhabricatorDragAndDropFileUpload', {
}
}));
}
this.setIsEnabled(true);
},
_sendRequest : function(spec) {

View file

@ -7,64 +7,102 @@
* phabricator-drag-and-drop-file-upload
*/
JX.behavior('global-drag-and-drop', function(config) {
JX.behavior('global-drag-and-drop', function(config, statics) {
if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) {
return;
}
var pending = 0;
var files = [];
var errors = false;
function init() {
statics.pending = 0;
statics.files = [];
statics.errors = false;
statics.enabled = true;
if (config.ifSupported) {
JX.$(config.ifSupported).style.display = '';
if (config.ifSupported) {
JX.$(config.ifSupported).style.display = '';
}
var page = JX.$('phabricator-standard-page');
statics.drop = new JX.PhabricatorDragAndDropFileUpload(page)
.setURI(config.uploadURI)
.setViewPolicy(config.viewPolicy)
.setChunkThreshold(config.chunkThreshold);
install_extra_listeners();
statics.drop.start();
return true;
}
var page = JX.$('phabricator-standard-page');
var drop = new JX.PhabricatorDragAndDropFileUpload(page)
.setURI(config.uploadURI)
.setViewPolicy(config.viewPolicy)
.setChunkThreshold(config.chunkThreshold);
drop.listen('didBeginDrag', function() {
JX.Mask.show('global-upload-mask');
JX.DOM.show(JX.$(config.instructions));
});
drop.listen('didEndDrag', function() {
JX.Mask.hide('global-upload-mask');
JX.DOM.hide(JX.$(config.instructions));
});
drop.listen('willUpload', function() {
pending++;
});
drop.listen('didUpload', function(f) {
files.push(f);
pending--;
if (pending === 0 && !errors) {
// If whatever the user dropped in has finished uploading, send them to
// their uploads.
var uri;
uri = JX.$U(config.browseURI);
var ids = [];
for (var ii = 0; ii < files.length; ii++) {
ids.push(files[ii].getID());
function install_extra_listeners() {
statics.drop.listen('didBeginDrag', function() {
if (!statics.enabled) {
return;
}
uri.setQueryParam('h', ids.join(','));
JX.Mask.show('global-upload-mask');
JX.DOM.show(JX.$(config.instructions));
});
files = [];
statics.drop.listen('didEndDrag', function() {
if (!statics.enabled) {
return;
}
JX.Mask.hide('global-upload-mask');
JX.DOM.hide(JX.$(config.instructions));
});
uri.go();
}
});
statics.drop.listen('willUpload', function() {
if (!statics.enabled) {
return;
}
statics.pending++;
});
drop.listen('didError', function() {
pending--;
errors = true;
});
statics.drop.listen('didUpload', function(f) {
if (!statics.enabled) {
return;
}
statics.files.push(f);
drop.start();
statics.pending--;
if (statics.pending === 0 && !statics.errors) {
// If whatever the user dropped in has finished uploading, send them to
// their uploads.
var uri;
uri = JX.$U(config.browseURI);
var ids = [];
for (var ii = 0; ii < statics.files.length; ii++) {
ids.push(statics.files[ii].getID());
}
uri.setQueryParam('h', ids.join(','));
statics.files = [];
uri.go();
}
});
statics.drop.listen('didError', function() {
if (!statics.enabled) {
return;
}
statics.pending--;
statics.errors = true;
});
}
statics.init = statics.init || init();
JX.Stratcom.listen(
'quicksand-redraw',
null,
function (e) {
e.kill();
var data = e.getData();
var toggle = data.newResponse.globalDragAndDrop;
statics.enabled = toggle;
statics.drop.setIsEnabled(toggle);
});
});