mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 00:42:41 +01:00
Rough implementation of drag-and-drop file uploads
Summary: This gets all the major pieces working. Allows you to drag-and-drop files in Differential and Phriction, and embed files in remarkup with {Fxxx} references. See also task. I'm explicitly not documenting this yet since it's still pretty rough. Test Plan: Dragged and dropped stuff into Differential and Phriction. Reviewed By: jungejason Reviewers: jungejason, tuomaspelkonen, aran, tomo Commenters: tomo CC: aran, tomo, jungejason Differential Revision: 674
This commit is contained in:
parent
35d03d36c7
commit
9d3f33a7a6
13 changed files with 198 additions and 53 deletions
|
@ -54,7 +54,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'aphront-form-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/8ee16aba/rsrc/css/aphront/form-view.css',
|
||||
'uri' => '/res/c79fd668/rsrc/css/aphront/form-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -117,7 +117,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'aphront-table-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/3ac9ba50/rsrc/css/aphront/table-view.css',
|
||||
'uri' => '/res/f4f39a2e/rsrc/css/aphront/table-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -331,6 +331,18 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-drag-and-drop.js',
|
||||
),
|
||||
'javelin-behavior-aphront-drag-and-drop-textarea' =>
|
||||
array(
|
||||
'uri' => '/res/fa7527f9/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'phabricator-drag-and-drop-file-upload',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
|
||||
),
|
||||
'javelin-behavior-aphront-form-disable-on-submit' =>
|
||||
array(
|
||||
'uri' => '/res/6c659ede/rsrc/js/application/core/behavior-form.js',
|
||||
|
@ -637,6 +649,37 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-keyboard-shortcuts.js',
|
||||
),
|
||||
'javelin-behavior-phriction-document-preview' =>
|
||||
array(
|
||||
'uri' => '/res/f1665ecd/rsrc/js/application/phriction/phriction-document-preview.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
3 => 'phabricator-shaped-request',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/phriction/phriction-document-preview.js',
|
||||
),
|
||||
'javelin-behavior-projects-resource-editor' =>
|
||||
array(
|
||||
'uri' => '/res/a54d5616/rsrc/js/application/projects/projects-resource-editor.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'phabricator-prefab',
|
||||
2 => 'multirow-row-manager',
|
||||
3 => 'javelin-tokenizer',
|
||||
4 => 'javelin-typeahead-preloaded-source',
|
||||
5 => 'javelin-typeahead',
|
||||
6 => 'javelin-dom',
|
||||
7 => 'javelin-json',
|
||||
8 => 'javelin-util',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/projects/projects-resource-editor.js',
|
||||
),
|
||||
0 =>
|
||||
array(
|
||||
'uri' => '/res/1da00bfe/rsrc/js/javelin/lib/__tests__/URI.js',
|
||||
|
@ -675,37 +718,6 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-watch-anchor.js',
|
||||
),
|
||||
'javelin-behavior-phriction-document-preview' =>
|
||||
array(
|
||||
'uri' => '/res/f1665ecd/rsrc/js/application/phriction/phriction-document-preview.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
3 => 'phabricator-shaped-request',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/phriction/phriction-document-preview.js',
|
||||
),
|
||||
'javelin-behavior-projects-resource-editor' =>
|
||||
array(
|
||||
'uri' => '/res/a54d5616/rsrc/js/application/projects/projects-resource-editor.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'phabricator-prefab',
|
||||
2 => 'multirow-row-manager',
|
||||
3 => 'javelin-tokenizer',
|
||||
4 => 'javelin-typeahead-preloaded-source',
|
||||
5 => 'javelin-typeahead',
|
||||
6 => 'javelin-dom',
|
||||
7 => 'javelin-json',
|
||||
8 => 'javelin-util',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/projects/projects-resource-editor.js',
|
||||
),
|
||||
'javelin-behavior-refresh-csrf' =>
|
||||
array(
|
||||
'uri' => '/res/39aa51f7/rsrc/js/application/core/behavior-refresh-csrf.js',
|
||||
|
@ -1305,7 +1317,7 @@ celerity_register_resource_map(array(
|
|||
'uri' => '/res/pkg/95b66c1a/differential.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'b3fd9e3f' =>
|
||||
'a841d3be' =>
|
||||
array (
|
||||
'name' => 'core.pkg.css',
|
||||
'symbols' =>
|
||||
|
@ -1326,7 +1338,7 @@ celerity_register_resource_map(array(
|
|||
13 => 'phabricator-remarkup-css',
|
||||
14 => 'syntax-highlighting-css',
|
||||
),
|
||||
'uri' => '/res/pkg/b3fd9e3f/core.pkg.css',
|
||||
'uri' => '/res/pkg/a841d3be/core.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'd0713563' =>
|
||||
|
@ -1362,15 +1374,15 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'reverse' =>
|
||||
array (
|
||||
'aphront-crumbs-view-css' => 'b3fd9e3f',
|
||||
'aphront-dialog-view-css' => 'b3fd9e3f',
|
||||
'aphront-form-view-css' => 'b3fd9e3f',
|
||||
'aphront-list-filter-view-css' => 'b3fd9e3f',
|
||||
'aphront-panel-view-css' => 'b3fd9e3f',
|
||||
'aphront-side-nav-view-css' => 'b3fd9e3f',
|
||||
'aphront-table-view-css' => 'b3fd9e3f',
|
||||
'aphront-tokenizer-control-css' => 'b3fd9e3f',
|
||||
'aphront-typeahead-control-css' => 'b3fd9e3f',
|
||||
'aphront-crumbs-view-css' => 'a841d3be',
|
||||
'aphront-dialog-view-css' => 'a841d3be',
|
||||
'aphront-form-view-css' => 'a841d3be',
|
||||
'aphront-list-filter-view-css' => 'a841d3be',
|
||||
'aphront-panel-view-css' => 'a841d3be',
|
||||
'aphront-side-nav-view-css' => 'a841d3be',
|
||||
'aphront-table-view-css' => 'a841d3be',
|
||||
'aphront-tokenizer-control-css' => 'a841d3be',
|
||||
'aphront-typeahead-control-css' => 'a841d3be',
|
||||
'differential-changeset-view-css' => '95b66c1a',
|
||||
'differential-core-view-css' => '95b66c1a',
|
||||
'differential-revision-add-comment-css' => '95b66c1a',
|
||||
|
@ -1407,13 +1419,13 @@ celerity_register_resource_map(array(
|
|||
'javelin-util' => '307df223',
|
||||
'javelin-vector' => '307df223',
|
||||
'javelin-workflow' => 'd0713563',
|
||||
'phabricator-core-buttons-css' => 'b3fd9e3f',
|
||||
'phabricator-core-css' => 'b3fd9e3f',
|
||||
'phabricator-directory-css' => 'b3fd9e3f',
|
||||
'phabricator-core-buttons-css' => 'a841d3be',
|
||||
'phabricator-core-css' => 'a841d3be',
|
||||
'phabricator-directory-css' => 'a841d3be',
|
||||
'phabricator-keyboard-shortcut' => 'd0713563',
|
||||
'phabricator-keyboard-shortcut-manager' => 'd0713563',
|
||||
'phabricator-remarkup-css' => 'b3fd9e3f',
|
||||
'phabricator-standard-page-view' => 'b3fd9e3f',
|
||||
'syntax-highlighting-css' => 'b3fd9e3f',
|
||||
'phabricator-remarkup-css' => 'a841d3be',
|
||||
'phabricator-standard-page-view' => 'a841d3be',
|
||||
'syntax-highlighting-css' => 'a841d3be',
|
||||
),
|
||||
));
|
||||
|
|
|
@ -477,6 +477,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorRefreshCSRFController' => 'applications/auth/controller/refresh',
|
||||
'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential',
|
||||
'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion',
|
||||
'PhabricatorRemarkupRuleEmbedFile' => 'infrastructure/markup/remarkup/markuprule/embedobject',
|
||||
'PhabricatorRemarkupRuleImageMacro' => 'infrastructure/markup/remarkup/markuprule/imagemacro',
|
||||
'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest',
|
||||
'PhabricatorRemarkupRuleMention' => 'infrastructure/markup/remarkup/markuprule/mention',
|
||||
|
@ -1013,6 +1014,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
|
||||
'PhabricatorRemarkupRuleDifferential' => 'PhabricatorRemarkupRuleObjectName',
|
||||
'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule',
|
||||
'PhabricatorRemarkupRuleEmbedFile' => 'PhutilRemarkupRule',
|
||||
'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule',
|
||||
'PhabricatorRemarkupRuleManiphest' => 'PhabricatorRemarkupRuleObjectName',
|
||||
'PhabricatorRemarkupRuleMention' => 'PhutilRemarkupRule',
|
||||
|
|
|
@ -106,6 +106,7 @@ final class DifferentialAddCommentView extends AphrontView {
|
|||
->setName('comment')
|
||||
->setID('comment-content')
|
||||
->setLabel('Comment')
|
||||
->setEnableDragAndDropFileUploads(true)
|
||||
->setValue($this->draft))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
|
|
|
@ -100,6 +100,7 @@ class PhabricatorMarkupEngine {
|
|||
|
||||
$rules[] = new PhutilRemarkupRuleHyperlink();
|
||||
|
||||
$rules[] = new PhabricatorRemarkupRuleEmbedFile();
|
||||
$rules[] = new PhabricatorRemarkupRuleDifferential();
|
||||
$rules[] = new PhabricatorRemarkupRuleDiffusion();
|
||||
$rules[] = new PhabricatorRemarkupRuleManiphest();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/differential');
|
||||
phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/diffusion');
|
||||
phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/embedobject');
|
||||
phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/imagemacro');
|
||||
phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/maniphest');
|
||||
phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/mention');
|
||||
|
|
|
@ -193,6 +193,7 @@ class PhrictionEditController
|
|||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
||||
->setName('content')
|
||||
->setID('document-textarea')
|
||||
->setEnableDragAndDropFileUploads(true)
|
||||
->setCaption($remarkup_reference))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @group markup
|
||||
*/
|
||||
class PhabricatorRemarkupRuleEmbedFile
|
||||
extends PhutilRemarkupRule {
|
||||
|
||||
public function apply($text) {
|
||||
return preg_replace_callback(
|
||||
"@{F(\d+)}@",
|
||||
array($this, 'markupEmbedFile'),
|
||||
$text);
|
||||
}
|
||||
|
||||
public function markupEmbedFile($matches) {
|
||||
|
||||
$file = null;
|
||||
if ($matches[1]) {
|
||||
// TODO: This is pretty inefficient if there are a bunch of files.
|
||||
$file = id(new PhabricatorFile())->load($matches[1]);
|
||||
}
|
||||
|
||||
if ($file) {
|
||||
return $this->getEngine()->storeText(
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $file->getViewURI(),
|
||||
'target' => '_blank',
|
||||
),
|
||||
phutil_render_tag(
|
||||
'img',
|
||||
array(
|
||||
'src' => $file->getThumb160x120URI(),
|
||||
))));
|
||||
} else {
|
||||
return $matches[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/files/storage/file');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
phutil_require_module('phutil', 'markup/engine/remarkup/markuprule/base');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorRemarkupRuleEmbedFile.php');
|
|
@ -72,13 +72,12 @@ class PhabricatorRemarkupRuleMention
|
|||
$username = strtolower($matches[1]);
|
||||
$exists = isset($this->actualUsers[$username]);
|
||||
|
||||
$real = $this->actualUsers[$username]['realName'];
|
||||
|
||||
$class = $exists
|
||||
? 'phabricator-remarkup-mention-exists'
|
||||
: 'phabricator-remarkup-mention-unknown';
|
||||
|
||||
if ($exists) {
|
||||
$real = $this->actualUsers[$username]['realName'];
|
||||
$tag = phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
|
|
|
@ -23,6 +23,7 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
|
|||
const HEIGHT_VERY_TALL = 'very-tall';
|
||||
|
||||
private $height;
|
||||
private $enableDragAndDropFileUploads;
|
||||
|
||||
public function setHeight($height) {
|
||||
$this->height = $height;
|
||||
|
@ -33,6 +34,11 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
|
|||
return 'aphront-form-control-textarea';
|
||||
}
|
||||
|
||||
public function setEnableDragAndDropFileUploads($enable) {
|
||||
$this->enableDragAndDropFileUploads = $enable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function renderInput() {
|
||||
|
||||
$height_class = null;
|
||||
|
@ -44,6 +50,20 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
|
|||
break;
|
||||
}
|
||||
|
||||
$id = $this->getID();
|
||||
if ($this->enableDragAndDropFileUploads) {
|
||||
if (!$id) {
|
||||
$id = celerity_generate_unique_node_id();
|
||||
}
|
||||
Javelin::initBehavior(
|
||||
'aphront-drag-and-drop-textarea',
|
||||
array(
|
||||
'target' => $id,
|
||||
'activatedClass' => 'aphront-textarea-drag-and-drop',
|
||||
'uri' => '/file/dropupload/',
|
||||
));
|
||||
}
|
||||
|
||||
return phutil_render_tag(
|
||||
'textarea',
|
||||
array(
|
||||
|
@ -51,7 +71,7 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
|
|||
'disabled' => $this->getDisabled() ? 'disabled' : null,
|
||||
'class' => $height_class,
|
||||
'style' => $this->getControlStyle(),
|
||||
'id' => $this->getID(),
|
||||
'id' => $id,
|
||||
),
|
||||
phutil_escape_html($this->getValue()));
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||
phutil_require_module('phabricator', 'view/form/control/base');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
|
|
@ -133,3 +133,8 @@ table.aphront-form-control-checkbox-layout th {
|
|||
font-size: 11px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.aphront-textarea-drag-and-drop {
|
||||
background: #99ff99;
|
||||
border-color: #669966;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* @provides javelin-behavior-aphront-drag-and-drop-textarea
|
||||
* @requires javelin-behavior
|
||||
* javelin-dom
|
||||
* phabricator-drag-and-drop-file-upload
|
||||
*/
|
||||
|
||||
JX.behavior('aphront-drag-and-drop-textarea', function(config) {
|
||||
|
||||
if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var target = JX.$(config.target);
|
||||
var drop = new JX.PhabricatorDragAndDropFileUpload(target)
|
||||
.setActivatedClass(config.activatedClass)
|
||||
.setURI(config.uri);
|
||||
|
||||
drop.listen('didUpload', function(f) {
|
||||
// TODO: Implement some fancy cursor position stuff in Javelin so we
|
||||
// can drop it in wherever the cursor is.
|
||||
target.value = target.value + "\n{F" + f.id + "}";
|
||||
});
|
||||
|
||||
drop.start();
|
||||
});
|
||||
|
Loading…
Reference in a new issue