mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 14:00:56 +01:00
Improve drag-and-drop uploader
Summary: Make it discoverable, show uploading progress, show file thumbnails, allow you to remove files, make it a generic form component. Test Plan: Uploaded ducks Reviewed By: tomo Reviewers: aran, tomo, jungejason, tuomaspelkonen CC: anjali, aran, epriestley, tomo Differential Revision: 334
This commit is contained in:
parent
8af5bb117d
commit
109a202b6c
16 changed files with 413 additions and 121 deletions
|
@ -7,6 +7,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
celerity_register_resource_map(array(
|
celerity_register_resource_map(array(
|
||||||
|
'aphront-attached-file-view-css' =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/79bc2c2e/rsrc/css/aphront/attached-file-view.css',
|
||||||
|
'type' => 'css',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/css/aphront/attached-file-view.css',
|
||||||
|
),
|
||||||
'aphront-crumbs-view-css' =>
|
'aphront-crumbs-view-css' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/9009e6bd/rsrc/css/aphront/crumbs-view.css',
|
'uri' => '/res/9009e6bd/rsrc/css/aphront/crumbs-view.css',
|
||||||
|
@ -45,7 +54,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'aphront-form-view-css' =>
|
'aphront-form-view-css' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/dadf31b1/rsrc/css/aphront/form-view.css',
|
'uri' => '/res/38a347da/rsrc/css/aphront/form-view.css',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -270,6 +279,16 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/javelin/lib/behavior.js',
|
'disk' => '/rsrc/js/javelin/lib/behavior.js',
|
||||||
),
|
),
|
||||||
|
0 =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js',
|
||||||
|
'type' => 'js',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-install',
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/js/javelin/docs/Base.js',
|
||||||
|
),
|
||||||
'javelin-behavior-aphront-basic-tokenizer' =>
|
'javelin-behavior-aphront-basic-tokenizer' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/bce3961b/rsrc/js/application/core/behavior-tokenizer.js',
|
'uri' => '/res/bce3961b/rsrc/js/application/core/behavior-tokenizer.js',
|
||||||
|
@ -284,6 +303,19 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/core/behavior-tokenizer.js',
|
'disk' => '/rsrc/js/application/core/behavior-tokenizer.js',
|
||||||
),
|
),
|
||||||
|
'javelin-behavior-aphront-drag-and-drop' =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/170115f4/rsrc/js/application/core/behavior-drag-and-drop.js',
|
||||||
|
'type' => 'js',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-behavior',
|
||||||
|
1 => 'javelin-dom',
|
||||||
|
2 => 'javelin-util',
|
||||||
|
3 => 'phabricator-drag-and-drop-file-upload',
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/js/application/core/behavior-drag-and-drop.js',
|
||||||
|
),
|
||||||
'javelin-behavior-aphront-form-disable-on-submit' =>
|
'javelin-behavior-aphront-form-disable-on-submit' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/6c659ede/rsrc/js/application/core/behavior-form.js',
|
'uri' => '/res/6c659ede/rsrc/js/application/core/behavior-form.js',
|
||||||
|
@ -477,18 +509,6 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js',
|
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js',
|
||||||
),
|
),
|
||||||
'javelin-behavior-maniphest-transaction-drag-and-drop' =>
|
|
||||||
array(
|
|
||||||
'uri' => '/res/5bf1f40c/rsrc/js/application/maniphest/behavior-transaction-drag-and-drop.js',
|
|
||||||
'type' => 'js',
|
|
||||||
'requires' =>
|
|
||||||
array(
|
|
||||||
0 => 'javelin-behavior',
|
|
||||||
1 => 'javelin-dom',
|
|
||||||
2 => 'phabricator-drag-and-drop-file-upload',
|
|
||||||
),
|
|
||||||
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-drag-and-drop.js',
|
|
||||||
),
|
|
||||||
'javelin-behavior-maniphest-transaction-expand' =>
|
'javelin-behavior-maniphest-transaction-expand' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/966410de/rsrc/js/application/maniphest/behavior-transaction-expand.js',
|
'uri' => '/res/966410de/rsrc/js/application/maniphest/behavior-transaction-expand.js',
|
||||||
|
@ -671,16 +691,6 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/javelin/lib/control/typeahead/Typeahead.js',
|
'disk' => '/rsrc/js/javelin/lib/control/typeahead/Typeahead.js',
|
||||||
),
|
),
|
||||||
0 =>
|
|
||||||
array(
|
|
||||||
'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js',
|
|
||||||
'type' => 'js',
|
|
||||||
'requires' =>
|
|
||||||
array(
|
|
||||||
0 => 'javelin-install',
|
|
||||||
),
|
|
||||||
'disk' => '/rsrc/js/javelin/docs/Base.js',
|
|
||||||
),
|
|
||||||
'javelin-typeahead-normalizer' =>
|
'javelin-typeahead-normalizer' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/a5d60e3c/rsrc/js/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js',
|
'uri' => '/res/a5d60e3c/rsrc/js/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js',
|
||||||
|
@ -888,7 +898,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'phabricator-drag-and-drop-file-upload' =>
|
'phabricator-drag-and-drop-file-upload' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/711efc61/rsrc/js/application/core/DragAndDropFileUpload.js',
|
'uri' => '/res/63a06ad9/rsrc/js/application/core/DragAndDropFileUpload.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -1025,7 +1035,26 @@ celerity_register_resource_map(array(
|
||||||
'uri' => '/res/pkg/33f413ef/typeahead.pkg.js',
|
'uri' => '/res/pkg/33f413ef/typeahead.pkg.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
),
|
),
|
||||||
'ac3f56cc' =>
|
'd985d27a' =>
|
||||||
|
array (
|
||||||
|
'name' => 'javelin.pkg.js',
|
||||||
|
'symbols' =>
|
||||||
|
array (
|
||||||
|
0 => 'javelin-util',
|
||||||
|
1 => 'javelin-install',
|
||||||
|
2 => 'javelin-event',
|
||||||
|
3 => 'javelin-stratcom',
|
||||||
|
4 => 'javelin-behavior',
|
||||||
|
5 => 'javelin-request',
|
||||||
|
6 => 'javelin-vector',
|
||||||
|
7 => 'javelin-dom',
|
||||||
|
8 => 'javelin-json',
|
||||||
|
9 => 'javelin-uri',
|
||||||
|
),
|
||||||
|
'uri' => '/res/pkg/d985d27a/javelin.pkg.js',
|
||||||
|
'type' => 'js',
|
||||||
|
),
|
||||||
|
'e8e3f8ab' =>
|
||||||
array (
|
array (
|
||||||
'name' => 'core.pkg.css',
|
'name' => 'core.pkg.css',
|
||||||
'symbols' =>
|
'symbols' =>
|
||||||
|
@ -1046,28 +1075,9 @@ celerity_register_resource_map(array(
|
||||||
13 => 'phabricator-remarkup-css',
|
13 => 'phabricator-remarkup-css',
|
||||||
14 => 'syntax-highlighting-css',
|
14 => 'syntax-highlighting-css',
|
||||||
),
|
),
|
||||||
'uri' => '/res/pkg/ac3f56cc/core.pkg.css',
|
'uri' => '/res/pkg/e8e3f8ab/core.pkg.css',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
),
|
),
|
||||||
'd985d27a' =>
|
|
||||||
array (
|
|
||||||
'name' => 'javelin.pkg.js',
|
|
||||||
'symbols' =>
|
|
||||||
array (
|
|
||||||
0 => 'javelin-util',
|
|
||||||
1 => 'javelin-install',
|
|
||||||
2 => 'javelin-event',
|
|
||||||
3 => 'javelin-stratcom',
|
|
||||||
4 => 'javelin-behavior',
|
|
||||||
5 => 'javelin-request',
|
|
||||||
6 => 'javelin-vector',
|
|
||||||
7 => 'javelin-dom',
|
|
||||||
8 => 'javelin-json',
|
|
||||||
9 => 'javelin-uri',
|
|
||||||
),
|
|
||||||
'uri' => '/res/pkg/d985d27a/javelin.pkg.js',
|
|
||||||
'type' => 'js',
|
|
||||||
),
|
|
||||||
'ed383f69' =>
|
'ed383f69' =>
|
||||||
array (
|
array (
|
||||||
'name' => 'differential.pkg.js',
|
'name' => 'differential.pkg.js',
|
||||||
|
@ -1085,15 +1095,15 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'reverse' =>
|
'reverse' =>
|
||||||
array (
|
array (
|
||||||
'aphront-crumbs-view-css' => 'ac3f56cc',
|
'aphront-crumbs-view-css' => 'e8e3f8ab',
|
||||||
'aphront-dialog-view-css' => 'ac3f56cc',
|
'aphront-dialog-view-css' => 'e8e3f8ab',
|
||||||
'aphront-form-view-css' => 'ac3f56cc',
|
'aphront-form-view-css' => 'e8e3f8ab',
|
||||||
'aphront-list-filter-view-css' => 'ac3f56cc',
|
'aphront-list-filter-view-css' => 'e8e3f8ab',
|
||||||
'aphront-panel-view-css' => 'ac3f56cc',
|
'aphront-panel-view-css' => 'e8e3f8ab',
|
||||||
'aphront-side-nav-view-css' => 'ac3f56cc',
|
'aphront-side-nav-view-css' => 'e8e3f8ab',
|
||||||
'aphront-table-view-css' => 'ac3f56cc',
|
'aphront-table-view-css' => 'e8e3f8ab',
|
||||||
'aphront-tokenizer-control-css' => 'ac3f56cc',
|
'aphront-tokenizer-control-css' => 'e8e3f8ab',
|
||||||
'aphront-typeahead-control-css' => 'ac3f56cc',
|
'aphront-typeahead-control-css' => 'e8e3f8ab',
|
||||||
'differential-changeset-view-css' => '1ac25e8a',
|
'differential-changeset-view-css' => '1ac25e8a',
|
||||||
'differential-core-view-css' => '1ac25e8a',
|
'differential-core-view-css' => '1ac25e8a',
|
||||||
'differential-revision-add-comment-css' => '1ac25e8a',
|
'differential-revision-add-comment-css' => '1ac25e8a',
|
||||||
|
@ -1128,11 +1138,11 @@ celerity_register_resource_map(array(
|
||||||
'javelin-util' => 'd985d27a',
|
'javelin-util' => 'd985d27a',
|
||||||
'javelin-vector' => 'd985d27a',
|
'javelin-vector' => 'd985d27a',
|
||||||
'javelin-workflow' => '122a6b6d',
|
'javelin-workflow' => '122a6b6d',
|
||||||
'phabricator-core-buttons-css' => 'ac3f56cc',
|
'phabricator-core-buttons-css' => 'e8e3f8ab',
|
||||||
'phabricator-core-css' => 'ac3f56cc',
|
'phabricator-core-css' => 'e8e3f8ab',
|
||||||
'phabricator-directory-css' => 'ac3f56cc',
|
'phabricator-directory-css' => 'e8e3f8ab',
|
||||||
'phabricator-remarkup-css' => 'ac3f56cc',
|
'phabricator-remarkup-css' => 'e8e3f8ab',
|
||||||
'phabricator-standard-page-view' => 'ac3f56cc',
|
'phabricator-standard-page-view' => 'e8e3f8ab',
|
||||||
'syntax-highlighting-css' => 'ac3f56cc',
|
'syntax-highlighting-css' => 'e8e3f8ab',
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
|
@ -13,6 +13,7 @@ phutil_register_library_map(array(
|
||||||
'Aphront404Response' => 'aphront/response/404',
|
'Aphront404Response' => 'aphront/response/404',
|
||||||
'AphrontAjaxResponse' => 'aphront/response/ajax',
|
'AphrontAjaxResponse' => 'aphront/response/ajax',
|
||||||
'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
|
'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
|
||||||
|
'AphrontAttachedFileView' => 'view/control/attachedfile',
|
||||||
'AphrontController' => 'aphront/controller',
|
'AphrontController' => 'aphront/controller',
|
||||||
'AphrontCrumbsView' => 'view/layout/crumbs',
|
'AphrontCrumbsView' => 'view/layout/crumbs',
|
||||||
'AphrontDatabaseConnection' => 'storage/connection/base',
|
'AphrontDatabaseConnection' => 'storage/connection/base',
|
||||||
|
@ -26,6 +27,7 @@ phutil_register_library_map(array(
|
||||||
'AphrontFormCheckboxControl' => 'view/form/control/checkbox',
|
'AphrontFormCheckboxControl' => 'view/form/control/checkbox',
|
||||||
'AphrontFormControl' => 'view/form/control/base',
|
'AphrontFormControl' => 'view/form/control/base',
|
||||||
'AphrontFormDividerControl' => 'view/form/control/divider',
|
'AphrontFormDividerControl' => 'view/form/control/divider',
|
||||||
|
'AphrontFormDragAndDropUploadControl' => 'view/form/control/draganddropupload',
|
||||||
'AphrontFormFileControl' => 'view/form/control/file',
|
'AphrontFormFileControl' => 'view/form/control/file',
|
||||||
'AphrontFormMarkupControl' => 'view/form/control/markup',
|
'AphrontFormMarkupControl' => 'view/form/control/markup',
|
||||||
'AphrontFormPasswordControl' => 'view/form/control/password',
|
'AphrontFormPasswordControl' => 'view/form/control/password',
|
||||||
|
@ -528,6 +530,7 @@ phutil_register_library_map(array(
|
||||||
'Aphront400Response' => 'AphrontResponse',
|
'Aphront400Response' => 'AphrontResponse',
|
||||||
'Aphront404Response' => 'AphrontResponse',
|
'Aphront404Response' => 'AphrontResponse',
|
||||||
'AphrontAjaxResponse' => 'AphrontResponse',
|
'AphrontAjaxResponse' => 'AphrontResponse',
|
||||||
|
'AphrontAttachedFileView' => 'AphrontView',
|
||||||
'AphrontCrumbsView' => 'AphrontView',
|
'AphrontCrumbsView' => 'AphrontView',
|
||||||
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
|
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
|
||||||
'AphrontDefaultApplicationController' => 'AphrontController',
|
'AphrontDefaultApplicationController' => 'AphrontController',
|
||||||
|
@ -538,6 +541,7 @@ phutil_register_library_map(array(
|
||||||
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
||||||
'AphrontFormControl' => 'AphrontView',
|
'AphrontFormControl' => 'AphrontView',
|
||||||
'AphrontFormDividerControl' => 'AphrontFormControl',
|
'AphrontFormDividerControl' => 'AphrontFormControl',
|
||||||
|
'AphrontFormDragAndDropUploadControl' => 'AphrontFormControl',
|
||||||
'AphrontFormFileControl' => 'AphrontFormControl',
|
'AphrontFormFileControl' => 'AphrontFormControl',
|
||||||
'AphrontFormMarkupControl' => 'AphrontFormControl',
|
'AphrontFormMarkupControl' => 'AphrontFormControl',
|
||||||
'AphrontFormPasswordControl' => 'AphrontFormControl',
|
'AphrontFormPasswordControl' => 'AphrontFormControl',
|
||||||
|
|
|
@ -30,12 +30,13 @@ class PhabricatorFileDropUploadController extends PhabricatorFileController {
|
||||||
'name' => $request->getStr('name'),
|
'name' => $request->getStr('name'),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$view = new AphrontAttachedFileView();
|
||||||
|
$view->setFile($file);
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())->setContent(
|
return id(new AphrontAjaxResponse())->setContent(
|
||||||
array(
|
array(
|
||||||
'name' => $file->getName(),
|
|
||||||
'phid' => $file->getPHID(),
|
'phid' => $file->getPHID(),
|
||||||
'size' => $file->getByteSize(),
|
'html' => $view->render(),
|
||||||
'uri' => $file->getViewURI(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||||
phutil_require_module('phabricator', 'applications/files/controller/base');
|
phutil_require_module('phabricator', 'applications/files/controller/base');
|
||||||
phutil_require_module('phabricator', 'applications/files/storage/file');
|
phutil_require_module('phabricator', 'applications/files/storage/file');
|
||||||
|
phutil_require_module('phabricator', 'view/control/attachedfile');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
|
@ -202,6 +202,10 @@ class PhabricatorFile extends PhabricatorFileDAO {
|
||||||
return PhabricatorFileURI::getViewURIForPHID($this->getPHID());
|
return PhabricatorFileURI::getViewURIForPHID($this->getPHID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getThumb60x45URI() {
|
||||||
|
return '/file/xform/thumb-60x45/'.$this->getPHID().'/';
|
||||||
|
}
|
||||||
|
|
||||||
public function isViewableInBrowser() {
|
public function isViewableInBrowser() {
|
||||||
return ($this->getViewableMimeType() !== null);
|
return ($this->getViewableMimeType() !== null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,6 +222,8 @@ class ManiphestTaskDetailController extends ManiphestController {
|
||||||
$draft_text = null;
|
$draft_text = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$panel_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
$comment_form = new AphrontFormView();
|
$comment_form = new AphrontFormView();
|
||||||
$comment_form
|
$comment_form
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
|
@ -286,12 +288,11 @@ class ManiphestTaskDetailController extends ManiphestController {
|
||||||
->setValue($draft_text)
|
->setValue($draft_text)
|
||||||
->setID('transaction-comments'))
|
->setID('transaction-comments'))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormMarkupControl())
|
id(new AphrontFormDragAndDropUploadControl())
|
||||||
->setLabel('Attached Files')
|
->setLabel('Attached Files')
|
||||||
->setValue(
|
->setName('files')
|
||||||
'<div id="file-list">'.
|
->setDragAndDropTarget($panel_id)
|
||||||
'None'.
|
->setActivatedClass('aphront-panel-view-drag-and-drop'))
|
||||||
'</div>'))
|
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue('Avast!'));
|
->setValue('Avast!'));
|
||||||
|
@ -335,22 +336,11 @@ class ManiphestTaskDetailController extends ManiphestController {
|
||||||
'map' => $control_map,
|
'map' => $control_map,
|
||||||
));
|
));
|
||||||
|
|
||||||
$id = celerity_generate_unique_node_id();
|
|
||||||
|
|
||||||
$comment_panel = new AphrontPanelView();
|
$comment_panel = new AphrontPanelView();
|
||||||
$comment_panel->appendChild($comment_form);
|
$comment_panel->appendChild($comment_form);
|
||||||
$comment_panel->setID($id);
|
$comment_panel->setID($panel_id);
|
||||||
$comment_panel->addClass('aphront-panel-accent');
|
$comment_panel->addClass('aphront-panel-accent');
|
||||||
$comment_panel->setHeader('Leap Into Action');
|
$comment_panel->setHeader('Weigh In');
|
||||||
|
|
||||||
Javelin::initBehavior(
|
|
||||||
'maniphest-transaction-drag-and-drop',
|
|
||||||
array(
|
|
||||||
'target' => $id,
|
|
||||||
'activatedClass' => 'aphront-panel-view-drag-and-drop',
|
|
||||||
'uri' => '/file/dropupload/',
|
|
||||||
'list' => 'file-list',
|
|
||||||
));
|
|
||||||
|
|
||||||
$preview_panel =
|
$preview_panel =
|
||||||
'<div class="aphront-panel-preview">
|
'<div class="aphront-panel-preview">
|
||||||
|
|
|
@ -21,8 +21,8 @@ phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||||
phutil_require_module('phabricator', 'view/form/base');
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/draganddropupload');
|
||||||
phutil_require_module('phabricator', 'view/form/control/file');
|
phutil_require_module('phabricator', 'view/form/control/file');
|
||||||
phutil_require_module('phabricator', 'view/form/control/markup');
|
|
||||||
phutil_require_module('phabricator', 'view/form/control/select');
|
phutil_require_module('phabricator', 'view/form/control/select');
|
||||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
phutil_require_module('phabricator', 'view/form/control/textarea');
|
phutil_require_module('phabricator', 'view/form/control/textarea');
|
||||||
|
|
73
src/view/control/attachedfile/AphrontAttachedFileView.php
Normal file
73
src/view/control/attachedfile/AphrontAttachedFileView.php
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class AphrontAttachedFileView extends AphrontView {
|
||||||
|
|
||||||
|
private $file;
|
||||||
|
|
||||||
|
public function setFile(PhabricatorFile $file) {
|
||||||
|
$this->file = $file;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
require_celerity_resource('aphront-attached-file-view-css');
|
||||||
|
|
||||||
|
$file = $this->file;
|
||||||
|
$phid = $file->getPHID();
|
||||||
|
|
||||||
|
$thumb = phutil_render_tag(
|
||||||
|
'img',
|
||||||
|
array(
|
||||||
|
'src' => $file->getThumb60x45URI(),
|
||||||
|
'width' => 60,
|
||||||
|
'height' => 45,
|
||||||
|
));
|
||||||
|
|
||||||
|
$name = phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $file->getViewURI(),
|
||||||
|
'target' => '_blank',
|
||||||
|
),
|
||||||
|
phutil_escape_html($file->getName()));
|
||||||
|
$size = number_format($file->getByteSize()).' bytes';
|
||||||
|
|
||||||
|
$remove = javelin_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'class' => 'button grey',
|
||||||
|
'sigil' => 'aphront-attached-file-view-remove',
|
||||||
|
// NOTE: Using 'ref' here instead of 'meta' because the file upload
|
||||||
|
// endpoint doesn't receive request metadata and thus can't generate
|
||||||
|
// a valid response with node metadata.
|
||||||
|
'ref' => $file->getPHID(),
|
||||||
|
),
|
||||||
|
"\xE2\x9C\x96"); // "Heavy Multiplication X"
|
||||||
|
|
||||||
|
return
|
||||||
|
'<table class="aphront-attached-file-view">
|
||||||
|
<tr>
|
||||||
|
<td>'.$thumb.'</td>
|
||||||
|
<th><strong>'.$name.'</strong><br />'.$size.'</th>
|
||||||
|
<td class="aphront-attached-file-view-remove">'.$remove.'</td>
|
||||||
|
</tr>
|
||||||
|
</table>';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/view/control/attachedfile/__init__.php
Normal file
16
src/view/control/attachedfile/__init__.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||||
|
phutil_require_module('phabricator', 'view/base');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('AphrontAttachedFileView.php');
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AphrontFormDragAndDropUploadControl extends AphrontFormControl {
|
||||||
|
|
||||||
|
private $dragAndDropTarget;
|
||||||
|
private $activatedClass;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->setControlID(celerity_generate_unique_node_id());
|
||||||
|
$this->setControlStyle('display: none;');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCustomControlClass() {
|
||||||
|
return 'aphront-form-drag-and-drop-upload';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDragAndDropTarget($id) {
|
||||||
|
$this->dragAndDropTarget = $id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setActivatedClass($class) {
|
||||||
|
$this->activatedClass = $class;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderInput() {
|
||||||
|
require_celerity_resource('aphront-attached-file-view-css');
|
||||||
|
|
||||||
|
$list_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
|
$files = $this->getValue();
|
||||||
|
$value = array();
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$view = new AphrontAttachedFileView();
|
||||||
|
$view->setFile($file);
|
||||||
|
$value[$file->getPHID()] = array(
|
||||||
|
'phid' => $file->getPHID(),
|
||||||
|
'html' => $view->render(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'aphront-drag-and-drop',
|
||||||
|
array(
|
||||||
|
'control' => $this->getControlID(),
|
||||||
|
'name' => $this->getName(),
|
||||||
|
'value' => nonempty($value, null),
|
||||||
|
'list' => $list_id,
|
||||||
|
'uri' => '/file/dropupload/',
|
||||||
|
'target' => $this->dragAndDropTarget,
|
||||||
|
'activatedClass' => $this->activatedClass,
|
||||||
|
));
|
||||||
|
|
||||||
|
return phutil_render_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'id' => $list_id,
|
||||||
|
'class' => 'aphront-form-drag-and-drop-file-list',
|
||||||
|
),
|
||||||
|
'');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
src/view/form/control/draganddropupload/__init__.php
Normal file
18
src/view/form/control/draganddropupload/__init__.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||||
|
phutil_require_module('phabricator', 'view/control/attachedfile');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/base');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('AphrontFormDragAndDropUploadControl.php');
|
24
webroot/rsrc/css/aphront/attached-file-view.css
Normal file
24
webroot/rsrc/css/aphront/attached-file-view.css
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* @provides aphront-attached-file-view-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.aphront-attached-file-view {
|
||||||
|
border: 1px solid #aaaaaa;
|
||||||
|
background: #f9f9f9;
|
||||||
|
width: 100%;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aphront-attached-file-view td,
|
||||||
|
.aphront-attached-file-view th {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aphront-attached-file-view th {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aphront-attached-file-view-remove {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
|
@ -116,3 +116,13 @@ table.aphront-form-control-checkbox-layout th {
|
||||||
background: #f3f3f3;
|
background: #f3f3f3;
|
||||||
border: 1px solid #afafaf;
|
border: 1px solid #afafaf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aphront-form-drag-and-drop-file-list {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-and-drop-instructions {
|
||||||
|
color: #333333;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,14 @@ JX.install('PhabricatorDragAndDropFileUpload', {
|
||||||
|
|
||||||
events : ['willUpload', 'didUpload'],
|
events : ['willUpload', 'didUpload'],
|
||||||
|
|
||||||
|
statics : {
|
||||||
|
isSupported : function() {
|
||||||
|
// TODO: Is there a better capability test for this? This seems okay in
|
||||||
|
// Safari, Firefox and Chrome.
|
||||||
|
return !!window.FileList;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
members : {
|
members : {
|
||||||
_node : null,
|
_node : null,
|
||||||
_depth : 0,
|
_depth : 0,
|
||||||
|
|
92
webroot/rsrc/js/application/core/behavior-drag-and-drop.js
Normal file
92
webroot/rsrc/js/application/core/behavior-drag-and-drop.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-behavior-aphront-drag-and-drop
|
||||||
|
* @requires javelin-behavior
|
||||||
|
* javelin-dom
|
||||||
|
* javelin-util
|
||||||
|
* phabricator-drag-and-drop-file-upload
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.behavior('aphront-drag-and-drop', function(config) {
|
||||||
|
|
||||||
|
// The control renders hidden by default; if we don't have support for
|
||||||
|
// drag-and-drop just leave it hidden.
|
||||||
|
if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the control, since we have browser support.
|
||||||
|
JX.$(config.control).style.display = '';
|
||||||
|
|
||||||
|
var files = config.value || {};
|
||||||
|
var pending = 0;
|
||||||
|
|
||||||
|
var list = JX.$(config.list);
|
||||||
|
|
||||||
|
var drop = new JX.PhabricatorDragAndDropFileUpload(JX.$(config.target))
|
||||||
|
.setActivatedClass(config.activatedClass)
|
||||||
|
.setURI(config.uri);
|
||||||
|
|
||||||
|
drop.listen('willUpload', function(f) {
|
||||||
|
pending++;
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
|
||||||
|
drop.listen('didUpload', function(f) {
|
||||||
|
files[f.phid] = f;
|
||||||
|
|
||||||
|
// This redraws "Upload complete!"
|
||||||
|
pending--;
|
||||||
|
redraw(true);
|
||||||
|
|
||||||
|
// This redraws the instructions.
|
||||||
|
JX.defer(redraw, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
drop.start();
|
||||||
|
redraw();
|
||||||
|
|
||||||
|
JX.DOM.listen(
|
||||||
|
list,
|
||||||
|
'click',
|
||||||
|
'aphront-attached-file-view-remove',
|
||||||
|
function(e) {
|
||||||
|
e.kill();
|
||||||
|
delete files[e.getTarget().getAttribute('ref')];
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
|
||||||
|
function redraw(completed) {
|
||||||
|
var items = [];
|
||||||
|
for (var k in files) {
|
||||||
|
var file = files[k];
|
||||||
|
items.push(JX.$N('div', {}, JX.$H(file.html)));
|
||||||
|
items.push(JX.$N(
|
||||||
|
'input',
|
||||||
|
{
|
||||||
|
type: "hidden",
|
||||||
|
name: config.name + "[" + file.phid + "]",
|
||||||
|
value: file.phid
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
var status;
|
||||||
|
if (!pending) {
|
||||||
|
if (completed) {
|
||||||
|
status = JX.$H('<strong>Upload complete!</strong>');
|
||||||
|
} else {
|
||||||
|
arrow = String.fromCharCode(0x21EA);
|
||||||
|
status = JX.$H(
|
||||||
|
arrow + ' <strong>Drag and Drop</strong> files here to upload them.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status = JX.$H(
|
||||||
|
'Uploading <strong>' + parseInt(pending, 10) + '<strong> files...');
|
||||||
|
}
|
||||||
|
status = JX.$N('div', {className: 'drag-and-drop-instructions'}, status);
|
||||||
|
|
||||||
|
items.push(status);
|
||||||
|
JX.DOM.setContent(list, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
/**
|
|
||||||
* @provides javelin-behavior-maniphest-transaction-drag-and-drop
|
|
||||||
* @requires javelin-behavior
|
|
||||||
* javelin-dom
|
|
||||||
* phabricator-drag-and-drop-file-upload
|
|
||||||
*/
|
|
||||||
|
|
||||||
JX.behavior('maniphest-transaction-drag-and-drop', function(config) {
|
|
||||||
|
|
||||||
var files = [];
|
|
||||||
|
|
||||||
var drop = new JX.PhabricatorDragAndDropFileUpload(JX.$(config.target))
|
|
||||||
.setActivatedClass(config.activatedClass)
|
|
||||||
.setURI(config.uri);
|
|
||||||
|
|
||||||
drop.listen('didUpload', function(f) {
|
|
||||||
files.push(f);
|
|
||||||
redraw();
|
|
||||||
});
|
|
||||||
|
|
||||||
drop.start();
|
|
||||||
|
|
||||||
function redraw() {
|
|
||||||
var items = [];
|
|
||||||
for (var ii = 0; ii < files.length; ii++) {
|
|
||||||
items.push(JX.$N('div', {}, files[ii].name));
|
|
||||||
items.push(JX.$N(
|
|
||||||
'input',
|
|
||||||
{
|
|
||||||
type: "hidden",
|
|
||||||
name: "files[" + files[ii].phid + "]",
|
|
||||||
value: files[ii].phid
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
JX.DOM.setContent(JX.$(config.list), items);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
Loading…
Reference in a new issue