@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+174 -82
+21 -21
resources/celerity/map.php
··· 8 8 return array( 9 9 'names' => array( 10 10 'core.pkg.css' => '9a9b59ca', 11 - 'core.pkg.js' => '348d5193', 11 + 'core.pkg.js' => '493cc6e6', 12 12 'darkconsole.pkg.js' => '8ab24e01', 13 13 'differential.pkg.css' => '3500921f', 14 - 'differential.pkg.js' => 'c0506961', 14 + 'differential.pkg.js' => '890046d3', 15 15 'diffusion.pkg.css' => '591664fa', 16 16 'diffusion.pkg.js' => '0115b37c', 17 17 'maniphest.pkg.css' => '68d4dd3d', ··· 442 442 'rsrc/js/application/uiexample/gesture-example.js' => '558829c2', 443 443 'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5', 444 444 'rsrc/js/core/Busy.js' => '59a7976a', 445 - 'rsrc/js/core/DragAndDropFileUpload.js' => '7fa4b248', 445 + 'rsrc/js/core/DragAndDropFileUpload.js' => '07de8873', 446 446 'rsrc/js/core/DraggableList.js' => 'a16ec1c6', 447 447 'rsrc/js/core/FileUpload.js' => '477359c8', 448 448 'rsrc/js/core/Hovercard.js' => '7e8468ae', ··· 468 468 'rsrc/js/core/behavior-file-tree.js' => '88236f00', 469 469 'rsrc/js/core/behavior-form.js' => '5c54cbf3', 470 470 'rsrc/js/core/behavior-gesture.js' => '3ab51e2c', 471 - 'rsrc/js/core/behavior-global-drag-and-drop.js' => 'bbdf75ca', 471 + 'rsrc/js/core/behavior-global-drag-and-drop.js' => '3f6075ff', 472 472 'rsrc/js/core/behavior-high-security-warning.js' => '8fc1c918', 473 473 'rsrc/js/core/behavior-history-install.js' => '7ee2b591', 474 474 'rsrc/js/core/behavior-hovercard.js' => 'f36e01af', ··· 593 593 'javelin-behavior-durable-column' => '657c2b50', 594 594 'javelin-behavior-error-log' => '6882e80a', 595 595 'javelin-behavior-fancy-datepicker' => 'c51ae228', 596 - 'javelin-behavior-global-drag-and-drop' => 'bbdf75ca', 596 + 'javelin-behavior-global-drag-and-drop' => '3f6075ff', 597 597 'javelin-behavior-herald-rule-editor' => '7ebaeed3', 598 598 'javelin-behavior-high-security-warning' => '8fc1c918', 599 599 'javelin-behavior-history-install' => '7ee2b591', ··· 726 726 'phabricator-core-css' => '76e8ee93', 727 727 'phabricator-countdown-css' => '86b7b0a0', 728 728 'phabricator-dashboard-css' => '17937d22', 729 - 'phabricator-drag-and-drop-file-upload' => '7fa4b248', 729 + 'phabricator-drag-and-drop-file-upload' => '07de8873', 730 730 'phabricator-draggable-list' => 'a16ec1c6', 731 731 'phabricator-fatal-config-template-css' => '8e6c6fcd', 732 732 'phabricator-feed-css' => 'b513b5f4', ··· 868 868 'phabricator-title', 869 869 'phabricator-shaped-request', 870 870 'conpherence-thread-manager', 871 + ), 872 + '07de8873' => array( 873 + 'javelin-install', 874 + 'javelin-util', 875 + 'javelin-request', 876 + 'javelin-dom', 877 + 'javelin-uri', 878 + 'phabricator-file-upload', 871 879 ), 872 880 '08883e8b' => array( 873 881 'javelin-behavior', ··· 1080 1088 'javelin-dom', 1081 1089 'phortune-credit-card-form', 1082 1090 ), 1091 + '3f6075ff' => array( 1092 + 'javelin-behavior', 1093 + 'javelin-dom', 1094 + 'javelin-uri', 1095 + 'javelin-mask', 1096 + 'phabricator-drag-and-drop-file-upload', 1097 + ), 1083 1098 '40a6a403' => array( 1084 1099 'javelin-install', 1085 1100 'javelin-dom', ··· 1413 1428 'javelin-behavior', 1414 1429 'javelin-history', 1415 1430 ), 1416 - '7fa4b248' => array( 1417 - 'javelin-install', 1418 - 'javelin-util', 1419 - 'javelin-request', 1420 - 'javelin-dom', 1421 - 'javelin-uri', 1422 - 'phabricator-file-upload', 1423 - ), 1424 1431 82439934 => array( 1425 1432 'javelin-behavior', 1426 1433 'javelin-dom', ··· 1721 1728 'javelin-behavior', 1722 1729 'javelin-stratcom', 1723 1730 'javelin-dom', 1724 - ), 1725 - 'bbdf75ca' => array( 1726 - 'javelin-behavior', 1727 - 'javelin-dom', 1728 - 'javelin-uri', 1729 - 'javelin-mask', 1730 - 'phabricator-drag-and-drop-file-upload', 1731 1731 ), 1732 1732 'bd4c8dca' => array( 1733 1733 'javelin-install',
+4
src/applications/base/controller/PhabricatorController.php
··· 57 57 return false; 58 58 } 59 59 60 + public function isGlobalDragAndDropUploadEnabled() { 61 + return false; 62 + } 63 + 60 64 public function willBeginExecution() { 61 65 $request = $this->getRequest(); 62 66
+4 -6
src/applications/files/controller/PhabricatorFileListController.php
··· 2 2 3 3 final class PhabricatorFileListController extends PhabricatorFileController { 4 4 5 - private $key; 6 - 7 5 public function shouldAllowPublic() { 8 6 return true; 9 7 } 10 8 11 - public function willProcessRequest(array $data) { 12 - $this->key = idx($data, 'key'); 9 + public function isGlobalDragAndDropUploadEnabled() { 10 + return true; 13 11 } 14 12 15 - public function processRequest() { 13 + public function handleRequest(AphrontRequest $request) { 16 14 $controller = id(new PhabricatorApplicationSearchController()) 17 - ->setQueryKey($this->key) 15 + ->setQueryKey($request->getURIData('key')) 18 16 ->setSearchEngine(new PhabricatorFileSearchEngine()) 19 17 ->setNavigation($this->buildSideNavView()); 20 18
+5 -2
src/applications/files/controller/PhabricatorFileUploadController.php
··· 2 2 3 3 final class PhabricatorFileUploadController extends PhabricatorFileController { 4 4 5 - public function processRequest() { 6 - $request = $this->getRequest(); 5 + public function isGlobalDragAndDropUploadEnabled() { 6 + return true; 7 + } 8 + 9 + public function handleRequest(AphrontRequest $request) { 7 10 $viewer = $request->getUser(); 8 11 9 12 $file = PhabricatorFile::initializeNewFile();
+12 -1
src/applications/files/view/PhabricatorGlobalUploadTargetView.php
··· 1 1 <?php 2 2 3 + /** 4 + * IMPORTANT: If you use this, make sure to implement 5 + * 6 + * public function isGlobalDragAndDropUploadEnabled() { 7 + * return true; 8 + * } 9 + * 10 + * on the controller(s) that render this class...! This is necessary 11 + * to make sure Quicksand works properly with the javascript in this 12 + * UI. 13 + */ 3 14 final class PhabricatorGlobalUploadTargetView extends AphrontView { 4 15 5 16 private $showIfSupportedID; ··· 19 30 return null; 20 31 } 21 32 22 - $instructions_id = celerity_generate_unique_node_id(); 33 + $instructions_id = 'phabricator-global-drag-and-drop-upload-instructions'; 23 34 24 35 require_celerity_resource('global-drag-and-drop-css'); 25 36
+5 -6
src/applications/home/controller/PhabricatorHomeMainController.php
··· 2 2 3 3 final class PhabricatorHomeMainController extends PhabricatorHomeController { 4 4 5 - private $only; 6 5 private $minipanels = array(); 7 6 8 7 public function shouldAllowPublic() { 9 8 return true; 10 9 } 11 10 12 - public function willProcessRequest(array $data) { 13 - $this->only = idx($data, 'only'); 11 + public function isGlobalDragAndDropUploadEnabled() { 12 + return true; 14 13 } 15 14 16 - public function processRequest() { 17 - $user = $this->getRequest()->getUser(); 15 + public function handleRequest(AphrontRequest $request) { 16 + $user = $request->getUser(); 18 17 19 18 $dashboard = PhabricatorDashboardInstall::getDashboard( 20 19 $user, ··· 42 41 $content = $this->buildMainResponse($projects); 43 42 } 44 43 45 - if (!$this->only) { 44 + if (!$request->getURIData('only')) { 46 45 $nav = $this->buildNav(); 47 46 $nav->appendChild( 48 47 array(
+3 -1
src/view/page/PhabricatorStandardPageView.php
··· 605 605 606 606 private function buildQuicksandConfig() { 607 607 $viewer = $this->getRequest()->getUser(); 608 + $controller = $this->getController(); 608 609 609 610 $dropdown_query = id(new AphlictDropdownDataQuery()) 610 611 ->setViewer($viewer); ··· 624 625 $rendered_dropdowns[$application_class] = 625 626 $application->buildMainMenuExtraNodes( 626 627 $viewer, 627 - $this->getController()); 628 + $controller); 628 629 } 629 630 630 631 $console_config = null; ··· 638 639 $dropdown_query->getNotificationData(), 639 640 $dropdown_query->getConpherenceData(), 640 641 ), 642 + 'globalDragAndDrop' => $controller->isGlobalDragAndDropUploadEnabled(), 641 643 'aphlictDropdowns' => $rendered_dropdowns, 642 644 'consoleConfig' => $console_config, 643 645 ) + $this->buildAphlictListenConfigData();
+39 -2
webroot/rsrc/js/core/DragAndDropFileUpload.js
··· 40 40 members : { 41 41 _node : null, 42 42 _depth : 0, 43 + _isEnabled: false, 44 + 45 + setIsEnabled: function(bool) { 46 + this._isEnabled = bool; 47 + return this; 48 + }, 49 + 50 + getIsEnabled: function() { 51 + return this._isEnabled; 52 + }, 53 + 43 54 _updateDepth : function(delta) { 44 55 if (this._depth === 0 && delta > 0) { 45 56 this.invoke('didBeginDrag'); ··· 54 65 55 66 start : function() { 56 67 68 + 57 69 // TODO: move this to JX.DOM.contains()? 58 70 function contains(container, child) { 59 71 do { ··· 73 85 'click', 74 86 null, 75 87 JX.bind(this, function (e) { 88 + if (!this.getIsEnabled()) { 89 + return; 90 + } 76 91 if (this._depth) { 77 92 e.kill(); 78 93 // Force depth to 0. ··· 87 102 'dragenter', 88 103 null, 89 104 JX.bind(this, function(e) { 105 + if (!this.getIsEnabled()) { 106 + return; 107 + } 108 + 90 109 if (contains(this._node, e.getTarget())) { 91 110 this._updateDepth(1); 92 111 } ··· 97 116 'dragleave', 98 117 null, 99 118 JX.bind(this, function(e) { 119 + if (!this.getIsEnabled()) { 120 + return; 121 + } 122 + 100 123 if (contains(this._node, e.getTarget())) { 101 124 this._updateDepth(-1); 102 125 } ··· 106 129 this._node, 107 130 'dragover', 108 131 null, 109 - function(e) { 132 + JX.bind(this, function(e) { 133 + if (!this.getIsEnabled()) { 134 + return; 135 + } 136 + 110 137 // NOTE: We must set this, or Chrome refuses to drop files from the 111 138 // download shelf. 112 139 e.getRawEvent().dataTransfer.dropEffect = 'copy'; 113 140 e.kill(); 114 - }); 141 + })); 115 142 116 143 JX.DOM.listen( 117 144 this._node, 118 145 'drop', 119 146 null, 120 147 JX.bind(this, function(e) { 148 + if (!this.getIsEnabled()) { 149 + return; 150 + } 151 + 121 152 e.kill(); 122 153 123 154 var files = e.getRawEvent().dataTransfer.files; ··· 135 166 'paste', 136 167 null, 137 168 JX.bind(this, function(e) { 169 + if (!this.getIsEnabled()) { 170 + return; 171 + } 172 + 138 173 var clipboard = e.getRawEvent().clipboardData; 139 174 if (!clipboard) { 140 175 return; ··· 168 203 } 169 204 })); 170 205 } 206 + 207 + this.setIsEnabled(true); 171 208 }, 172 209 173 210 _sendRequest : function(spec) {
+81 -43
webroot/rsrc/js/core/behavior-global-drag-and-drop.js
··· 7 7 * phabricator-drag-and-drop-file-upload 8 8 */ 9 9 10 - JX.behavior('global-drag-and-drop', function(config) { 10 + JX.behavior('global-drag-and-drop', function(config, statics) { 11 11 if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) { 12 12 return; 13 13 } 14 14 15 - var pending = 0; 16 - var files = []; 17 - var errors = false; 15 + function init() { 16 + statics.pending = 0; 17 + statics.files = []; 18 + statics.errors = false; 19 + statics.enabled = true; 20 + 21 + if (config.ifSupported) { 22 + JX.$(config.ifSupported).style.display = ''; 23 + } 24 + 25 + var page = JX.$('phabricator-standard-page'); 26 + statics.drop = new JX.PhabricatorDragAndDropFileUpload(page) 27 + .setURI(config.uploadURI) 28 + .setViewPolicy(config.viewPolicy) 29 + .setChunkThreshold(config.chunkThreshold); 30 + 31 + install_extra_listeners(); 32 + 33 + statics.drop.start(); 18 34 19 - if (config.ifSupported) { 20 - JX.$(config.ifSupported).style.display = ''; 35 + return true; 21 36 } 22 37 23 - var page = JX.$('phabricator-standard-page'); 24 - var drop = new JX.PhabricatorDragAndDropFileUpload(page) 25 - .setURI(config.uploadURI) 26 - .setViewPolicy(config.viewPolicy) 27 - .setChunkThreshold(config.chunkThreshold); 38 + function install_extra_listeners() { 39 + statics.drop.listen('didBeginDrag', function() { 40 + if (!statics.enabled) { 41 + return; 42 + } 43 + JX.Mask.show('global-upload-mask'); 44 + JX.DOM.show(JX.$(config.instructions)); 45 + }); 28 46 29 - drop.listen('didBeginDrag', function() { 30 - JX.Mask.show('global-upload-mask'); 31 - JX.DOM.show(JX.$(config.instructions)); 32 - }); 47 + statics.drop.listen('didEndDrag', function() { 48 + if (!statics.enabled) { 49 + return; 50 + } 51 + JX.Mask.hide('global-upload-mask'); 52 + JX.DOM.hide(JX.$(config.instructions)); 53 + }); 33 54 34 - drop.listen('didEndDrag', function() { 35 - JX.Mask.hide('global-upload-mask'); 36 - JX.DOM.hide(JX.$(config.instructions)); 37 - }); 55 + statics.drop.listen('willUpload', function() { 56 + if (!statics.enabled) { 57 + return; 58 + } 59 + statics.pending++; 60 + }); 38 61 39 - drop.listen('willUpload', function() { 40 - pending++; 41 - }); 62 + statics.drop.listen('didUpload', function(f) { 63 + if (!statics.enabled) { 64 + return; 65 + } 66 + statics.files.push(f); 67 + 68 + statics.pending--; 69 + if (statics.pending === 0 && !statics.errors) { 70 + // If whatever the user dropped in has finished uploading, send them to 71 + // their uploads. 72 + var uri; 73 + uri = JX.$U(config.browseURI); 74 + var ids = []; 75 + for (var ii = 0; ii < statics.files.length; ii++) { 76 + ids.push(statics.files[ii].getID()); 77 + } 78 + uri.setQueryParam('h', ids.join(',')); 42 79 43 - drop.listen('didUpload', function(f) { 44 - files.push(f); 80 + statics.files = []; 45 81 46 - pending--; 47 - if (pending === 0 && !errors) { 48 - // If whatever the user dropped in has finished uploading, send them to 49 - // their uploads. 50 - var uri; 51 - uri = JX.$U(config.browseURI); 52 - var ids = []; 53 - for (var ii = 0; ii < files.length; ii++) { 54 - ids.push(files[ii].getID()); 82 + uri.go(); 55 83 } 56 - uri.setQueryParam('h', ids.join(',')); 84 + }); 57 85 58 - files = []; 86 + statics.drop.listen('didError', function() { 87 + if (!statics.enabled) { 88 + return; 89 + } 90 + statics.pending--; 91 + statics.errors = true; 92 + }); 93 + } 59 94 60 - uri.go(); 61 - } 62 - }); 95 + statics.init = statics.init || init(); 63 96 64 - drop.listen('didError', function() { 65 - pending--; 66 - errors = true; 67 - }); 97 + JX.Stratcom.listen( 98 + 'quicksand-redraw', 99 + null, 100 + function (e) { 101 + e.kill(); 68 102 69 - drop.start(); 103 + var data = e.getData(); 104 + var toggle = data.newResponse.globalDragAndDrop; 105 + statics.enabled = toggle; 106 + statics.drop.setIsEnabled(toggle); 107 + }); 70 108 });