Page MenuHomePhabricator

D15202.id36699.diff
No OneTemporary

D15202.id36699.diff

diff --git a/resources/builtin/image-526x526.png b/resources/builtin/image-526x526.png
new file mode 100644
index 0000000000000000000000000000000000000000..853539d21aec0c1af03257e10ceeab52a50bcc0b
GIT binary patch
literal 2435
zc%17D@N?(olHy`uVBq!ia0y~yVB!N|4kn;T{7s=eASGGi8c`CQpH@<ySd_}(n3A8A
zs^FQMn4TJxnwU~qcrw+7fq_%i)5S5Q;?|qH{@KEn5(f$=`($w}ZC%FAwf6QQxvqKT
z-!FXDtN!%h^@qIbFIV>Ur(SQadTTvFZr20(BV`AhCuwx}3MyviWbNkvxzTyTzlqz}
z=hoO=Ffg8bCh<T`?I-Wsmwws*k7H_hoci(W<vaX&EDY{dKVQ|}jAeL`62#aL!k{3^
z;K9m(!eqcMfvya~#G)Uc1W7iII%Q-yC65SWYO;;CHwa#Q|NZ~J*XQ%L|Ni;;`RDkx
zK`YOkJ^T0DZ~4e)@`6E(u7VR^lvtfUeOi2K*VL&~<JOzo*}c1`BpNVdWzzcx4;DnN
z-4?aBXlIPp)N^HX4<A0f<J_M=H8yp>R75>kMZEW%THg5J;$nCAYf5tqHfbDr^eD;d
zXZAVvl@0IxD-+7fz9lH^z2k0n>0|KxdpTzNpP5YH`mrx~;`WDcZ*RA@w%%xMYHDh1
zJh}e6MW(<+F1L5_z0n6UmMLwCTH7yge~m3k`B>}2kB=-2TRm8V7?1k9?z!VGv~Bx#
z?WtZ|4mn$HzYVsYsH6yTNz6aBps4BR!gVg~FnIIw^78EL?D#Lr`<Z}Jm8&|A>ztPA
zb=!a6e`QDSjkE1NAD*0?oPK`Z+c$4qbbWx@dnTAz9Z6o2l$e;9l(fiLKzgGzd$8`K
z!sKLS>5A{~Vt3#Dm$dfK@f1l-JI13OexicSK`TrC{`xARdFkM#3D3{ZKhF6wcIAy+
zv)SS6Vs<_e4geb2@ug^fkl~+2^ZSlJKHSb9zrQYa_0>=DjGOlD`}geZ?C9-zPoMAT
z?YUOJ;SQ5q!KxGC8?>E2DbJogJ9Ej2@I8xp_2c$<JpZvlXBn5qKaavsvpqiE&Dy$a
z`}XN-{S`eomC7C`{G0MeDPdIu)2Z~Phn|nP%irCZId9&(605z9H#+WGNK|@!+_0v1
zB~zDuXisnN(yd*}=T}~Ty*>Ya-+Mh>-J|bby?XWVzmm|$m|$Soy2>v-{j~b~yWHGd
zS#!xHPv1qYomTe#{(kw*uKO}R{`mNKwt4=pUSMe2h-xyc@97sg{{8#+%g-~CmFG@7
z*Z(}~nw!>wA1gZ^F1Y`Geod6fqhr0&8#nIWJ$sg+P4Nzpz8D_%yLsQQd~GW^^x?_F
z^OB0920}^<d*kw}tAF!&U%uE`vCcGi2~(Gi&ywTp{Bm=i=Vvefn)?{!G_yV53byS$
zV(X%{U`E^jprb$6D@y=P4EPoKy1Q!X`SonTq!GCK>brY;XV=YmHz(M&N3GZGa6)5<
z!@`i&$@c$0y>921*GsXqI=yM-%41JYo0^NQY<RTd`s?fK{#JZ_b(MQ()EVK6oqwj+
zH-tDCF6(;r<jIoX4CxQP3sWP*W6#$lW_av2jyh#zIHfh32xIQg(%Bccuj;)UL&2Q?
cED{W|hi4jFPcfeZY*jIMy85}Sb4q9e0AAhZX#fBK
literal 0
Hc$@<O00001
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -8,10 +8,10 @@
return array(
'names' => array(
'core.pkg.css' => 'a7d4cf8f',
- 'core.pkg.js' => 'ef5e33db',
+ 'core.pkg.js' => '808ae845',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9',
- 'differential.pkg.js' => '5c2ba922',
+ 'differential.pkg.js' => '6b42b4bc',
'diffusion.pkg.css' => 'f45955ed',
'diffusion.pkg.js' => '3a9a8bfa',
'maniphest.pkg.css' => '4845691a',
@@ -155,7 +155,7 @@
'rsrc/css/phui/phui-timeline-view.css' => '2efceff8',
'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b',
'rsrc/css/phui/workboards/phui-workboard.css' => 'b07a5524',
- 'rsrc/css/phui/workboards/phui-workcard.css' => 'adf34f58',
+ 'rsrc/css/phui/workboards/phui-workcard.css' => 'a869098a',
'rsrc/css/phui/workboards/phui-workpanel.css' => 'e1bd8d04',
'rsrc/css/sprite-login.css' => '60e8560e',
'rsrc/css/sprite-menu.css' => '9dd65b92',
@@ -414,7 +414,7 @@
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
'rsrc/js/application/policy/behavior-policy-control.js' => 'd0c516d5',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
- 'rsrc/js/application/projects/behavior-project-boards.js' => '48470f95',
+ 'rsrc/js/application/projects/behavior-project-boards.js' => '5191522f',
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
@@ -446,9 +446,9 @@
'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' => 'ad10aeac',
+ 'rsrc/js/core/DragAndDropFileUpload.js' => 'da044194',
'rsrc/js/core/DraggableList.js' => '8905523d',
- 'rsrc/js/core/FileUpload.js' => '477359c8',
+ 'rsrc/js/core/FileUpload.js' => '680ea2c8',
'rsrc/js/core/Hovercard.js' => '1bd28176',
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
@@ -654,7 +654,7 @@
'javelin-behavior-phui-profile-menu' => '12884df9',
'javelin-behavior-policy-control' => 'd0c516d5',
'javelin-behavior-policy-rule-editor' => '5e9f347c',
- 'javelin-behavior-project-boards' => '48470f95',
+ 'javelin-behavior-project-boards' => '5191522f',
'javelin-behavior-project-create' => '065227cc',
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
'javelin-behavior-recurring-edit' => '5f1c4d5f',
@@ -741,11 +741,11 @@
'phabricator-core-css' => '5b3563c8',
'phabricator-countdown-css' => 'e7544472',
'phabricator-dashboard-css' => 'eb458607',
- 'phabricator-drag-and-drop-file-upload' => 'ad10aeac',
+ 'phabricator-drag-and-drop-file-upload' => 'da044194',
'phabricator-draggable-list' => '8905523d',
'phabricator-fatal-config-template-css' => '8e6c6fcd',
'phabricator-feed-css' => 'ecd4ec57',
- 'phabricator-file-upload' => '477359c8',
+ 'phabricator-file-upload' => '680ea2c8',
'phabricator-filetree-view-css' => 'fccf9f82',
'phabricator-flag-css' => '5337623f',
'phabricator-keyboard-shortcut' => '1ae869f2',
@@ -832,7 +832,7 @@
'phui-timeline-view-css' => '2efceff8',
'phui-two-column-view-css' => 'c75bfc5b',
'phui-workboard-view-css' => 'b07a5524',
- 'phui-workcard-view-css' => 'adf34f58',
+ 'phui-workcard-view-css' => 'a869098a',
'phui-workpanel-view-css' => 'e1bd8d04',
'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => '8cf6d262',
@@ -1133,11 +1133,6 @@
'javelin-dom',
'javelin-workflow',
),
- '477359c8' => array(
- 'javelin-install',
- 'javelin-dom',
- 'phabricator-notification',
- ),
47830651 => array(
'javelin-behavior',
'javelin-dom',
@@ -1154,15 +1149,6 @@
'javelin-dom',
'javelin-workflow',
),
- '48470f95' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-vector',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'phabricator-draggable-list',
- ),
'49b73b36' => array(
'javelin-behavior',
'javelin-dom',
@@ -1204,6 +1190,16 @@
'javelin-typeahead-source',
'javelin-util',
),
+ '5191522f' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-vector',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'phabricator-draggable-list',
+ 'phabricator-drag-and-drop-file-upload',
+ ),
'519705ea' => array(
'javelin-install',
'javelin-dom',
@@ -1331,6 +1327,11 @@
'javelin-request',
'javelin-workflow',
),
+ '680ea2c8' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ 'phabricator-notification',
+ ),
'6882e80a' => array(
'javelin-dom',
),
@@ -1674,14 +1675,6 @@
'javelin-util',
'phabricator-busy',
),
- 'ad10aeac' => array(
- 'javelin-install',
- 'javelin-util',
- 'javelin-request',
- 'javelin-dom',
- 'javelin-uri',
- 'phabricator-file-upload',
- ),
'b064af76' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1897,6 +1890,14 @@
'javelin-util',
'phabricator-shaped-request',
),
+ 'da044194' => array(
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-request',
+ 'javelin-dom',
+ 'javelin-uri',
+ 'phabricator-file-upload',
+ ),
'dbbf48b6' => array(
'javelin-behavior',
'javelin-stratcom',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2880,6 +2880,7 @@
'PhabricatorProjectConfiguredCustomField' => 'applications/project/customfield/PhabricatorProjectConfiguredCustomField.php',
'PhabricatorProjectController' => 'applications/project/controller/PhabricatorProjectController.php',
'PhabricatorProjectCoreTestCase' => 'applications/project/__tests__/PhabricatorProjectCoreTestCase.php',
+ 'PhabricatorProjectCoverController' => 'applications/project/controller/PhabricatorProjectCoverController.php',
'PhabricatorProjectCustomField' => 'applications/project/customfield/PhabricatorProjectCustomField.php',
'PhabricatorProjectCustomFieldNumericIndex' => 'applications/project/storage/PhabricatorProjectCustomFieldNumericIndex.php',
'PhabricatorProjectCustomFieldStorage' => 'applications/project/storage/PhabricatorProjectCustomFieldStorage.php',
@@ -7304,6 +7305,7 @@
),
'PhabricatorProjectController' => 'PhabricatorController',
'PhabricatorProjectCoreTestCase' => 'PhabricatorTestCase',
+ 'PhabricatorProjectCoverController' => 'PhabricatorProjectController',
'PhabricatorProjectCustomField' => 'PhabricatorCustomField',
'PhabricatorProjectCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
'PhabricatorProjectCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php
--- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php
+++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php
@@ -28,6 +28,7 @@
$types[] = ManiphestTransaction::TYPE_UNBLOCK;
$types[] = ManiphestTransaction::TYPE_PARENT;
$types[] = ManiphestTransaction::TYPE_COLUMN;
+ $types[] = ManiphestTransaction::TYPE_COVER_IMAGE;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
@@ -66,6 +67,8 @@
return $xaction->getOldValue();
case ManiphestTransaction::TYPE_SUBPRIORITY:
return $object->getSubpriority();
+ case ManiphestTransaction::TYPE_COVER_IMAGE:
+ return $object->getCoverImageFilePHID();
case ManiphestTransaction::TYPE_MERGED_INTO:
case ManiphestTransaction::TYPE_MERGED_FROM:
return null;
@@ -92,6 +95,7 @@
case ManiphestTransaction::TYPE_MERGED_INTO:
case ManiphestTransaction::TYPE_MERGED_FROM:
case ManiphestTransaction::TYPE_UNBLOCK:
+ case ManiphestTransaction::TYPE_COVER_IMAGE:
return $xaction->getNewValue();
case ManiphestTransaction::TYPE_PARENT:
case ManiphestTransaction::TYPE_COLUMN:
@@ -161,6 +165,32 @@
case ManiphestTransaction::TYPE_MERGED_INTO:
$object->setStatus(ManiphestTaskStatus::getDuplicateStatus());
return;
+ case ManiphestTransaction::TYPE_COVER_IMAGE:
+ $file_phid = $xaction->getNewValue();
+
+ if ($file_phid) {
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($this->getActor())
+ ->withPHIDs(array($file_phid))
+ ->executeOne();
+ } else {
+ $file = null;
+ }
+
+ if (!$file || !$file->isTransformableImage()) {
+ $object->setProperty('cover.filePHID', null);
+ $object->setProperty('cover.thumbnailPHID', null);
+ return;
+ }
+
+ $xform_key = PhabricatorFileThumbnailTransform::TRANSFORM_WORKCARD;
+
+ $xform = PhabricatorFileTransform::getTransformByKey($xform_key)
+ ->executeTransform($file);
+
+ $object->setProperty('cover.filePHID', $file->getPHID());
+ $object->setProperty('cover.thumbnailPHID', $xform->getPHID());
+ return;
case ManiphestTransaction::TYPE_MERGED_FROM:
case ManiphestTransaction::TYPE_PARENT:
case ManiphestTransaction::TYPE_COLUMN:
@@ -819,6 +849,41 @@
}
}
break;
+ case ManiphestTransaction::TYPE_COVER_IMAGE:
+ foreach ($xactions as $xaction) {
+ $old = $xaction->getOldValue();
+ $new = $xaction->getNewValue();
+ if (!$new) {
+ continue;
+ }
+
+ if ($new === $old) {
+ continue;
+ }
+
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($this->getActor())
+ ->withPHIDs(array($new))
+ ->executeOne();
+ if (!$file) {
+ $errors[] = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht('File "%s" is not valid.', $new),
+ $xaction);
+ continue;
+ }
+
+ if (!$file->isTransformableImage()) {
+ $errors[] = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht('File "%s" is not a valid image file.', $new),
+ $xaction);
+ continue;
+ }
+ }
+ break;
}
return $errors;
@@ -941,5 +1006,19 @@
->executeOne();
}
+ protected function extractFilePHIDsFromCustomTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+ $phids = parent::extractFilePHIDsFromCustomTransaction($object, $xaction);
+
+ switch ($xaction->getTransactionType()) {
+ case ManiphestTransaction::TYPE_COVER_IMAGE:
+ $phids[] = $xaction->getNewValue();
+ break;
+ }
+
+ return $phids;
+ }
+
}
diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php
--- a/src/applications/maniphest/storage/ManiphestTask.php
+++ b/src/applications/maniphest/storage/ManiphestTask.php
@@ -226,6 +226,10 @@
return idx($this->properties, $key, $default);
}
+ public function getCoverImageFilePHID() {
+ return idx($this->properties, 'cover.filePHID');
+ }
+
public function getCoverImageThumbnailPHID() {
return idx($this->properties, 'cover.thumbnailPHID');
}
diff --git a/src/applications/maniphest/storage/ManiphestTransaction.php b/src/applications/maniphest/storage/ManiphestTransaction.php
--- a/src/applications/maniphest/storage/ManiphestTransaction.php
+++ b/src/applications/maniphest/storage/ManiphestTransaction.php
@@ -16,6 +16,7 @@
const TYPE_UNBLOCK = 'unblock';
const TYPE_PARENT = 'parent';
const TYPE_COLUMN = 'column';
+ const TYPE_COVER_IMAGE = 'cover-image';
// NOTE: this type is deprecated. Keep it around for legacy installs
// so any transactions render correctly.
@@ -162,6 +163,9 @@
sort($new_cols);
return ($old_cols === $new_cols);
+ case self::TYPE_COVER_IMAGE:
+ // At least for now, don't show these.
+ return true;
}
return parent::shouldHide();
diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php
--- a/src/applications/project/application/PhabricatorProjectApplication.php
+++ b/src/applications/project/application/PhabricatorProjectApplication.php
@@ -72,6 +72,7 @@
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhabricatorProjectBoardViewController',
'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController',
+ 'cover/' => 'PhabricatorProjectCoverController',
'board/(?P<projectID>[1-9]\d*)/' => array(
'edit/(?:(?P<id>\d+)/)?'
=> 'PhabricatorProjectColumnEditController',
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -219,6 +219,9 @@
'projectPHID' => $project->getPHID(),
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
'createURI' => $this->getCreateURI(),
+ 'uploadURI' => '/file/dropupload/',
+ 'coverURI' => $this->getApplicationURI('cover/'),
+ 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
'order' => $this->sortKey,
);
$this->initBehavior(
diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php
--- a/src/applications/project/controller/PhabricatorProjectController.php
+++ b/src/applications/project/controller/PhabricatorProjectController.php
@@ -147,4 +147,53 @@
return $this;
}
+ protected function newCardResponse($board_phid, $object_phid) {
+ $viewer = $this->getViewer();
+
+ $project = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($board_phid))
+ ->executeOne();
+ if (!$project) {
+ return new Aphront404Response();
+ }
+
+ // Reload the object so it reflects edits which have been applied.
+ $object = id(new ManiphestTaskQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($object_phid))
+ ->needProjectPHIDs(true)
+ ->executeOne();
+ if (!$object) {
+ return new Aphront404Response();
+ }
+
+ $except_phids = array($board_phid);
+ if ($project->getHasSubprojects() || $project->getHasMilestones()) {
+ $descendants = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withAncestorProjectPHIDs($except_phids)
+ ->execute();
+ foreach ($descendants as $descendant) {
+ $except_phids[] = $descendant->getPHID();
+ }
+ }
+
+ $rendering_engine = id(new PhabricatorBoardRenderingEngine())
+ ->setViewer($viewer)
+ ->setObjects(array($object))
+ ->setExcludedProjectPHIDs($except_phids);
+
+ $card = $rendering_engine->renderCard($object->getPHID());
+
+ $item = $card->getItem();
+ $item->addClass('phui-workcard');
+
+ return id(new AphrontAjaxResponse())
+ ->setContent(
+ array(
+ 'task' => $item,
+ ));
+ }
+
}
diff --git a/src/applications/project/controller/PhabricatorProjectCoverController.php b/src/applications/project/controller/PhabricatorProjectCoverController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectCoverController.php
@@ -0,0 +1,53 @@
+<?php
+
+final class PhabricatorProjectCoverController
+ extends PhabricatorProjectController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $request->validateCSRF();
+
+ $board_phid = $request->getStr('boardPHID');
+ $object_phid = $request->getStr('objectPHID');
+ $file_phid = $request->getStr('filePHID');
+
+ $object = id(new ManiphestTaskQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($object_phid))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$object) {
+ return new Aphront404Response();
+ }
+
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($file_phid))
+ ->executeOne();
+ if (!$file) {
+ return new Aphront404Response();
+ }
+
+ $xactions = array();
+
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(ManiphestTransaction::TYPE_COVER_IMAGE)
+ ->setNewValue($file->getPHID());
+
+ $editor = id(new ManiphestTransactionEditor())
+ ->setActor($viewer)
+ ->setContinueOnMissingFields(true)
+ ->setContinueOnNoEffect(true)
+ ->setContentSourceFromRequest($request);
+
+ $editor->applyTransactions($object, $xactions);
+
+ return $this->newCardResponse($board_phid, $object_phid);
+ }
+
+}
diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php
--- a/src/applications/project/controller/PhabricatorProjectMoveController.php
+++ b/src/applications/project/controller/PhabricatorProjectMoveController.php
@@ -7,13 +7,14 @@
$viewer = $request->getViewer();
$id = $request->getURIData('id');
+ $request->validateCSRF();
+
$column_phid = $request->getStr('columnPHID');
$object_phid = $request->getStr('objectPHID');
$after_phid = $request->getStr('afterPHID');
$before_phid = $request->getStr('beforePHID');
$order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER);
-
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->requireCapabilities(
@@ -175,39 +176,7 @@
$editor->applyTransactions($object, $xactions);
- // Reload the object so it reflects edits which have been applied.
- $object = id(new ManiphestTaskQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($object_phid))
- ->needProjectPHIDs(true)
- ->executeOne();
-
- $except_phids = array($board_phid);
- if ($project->getHasSubprojects() || $project->getHasMilestones()) {
- $descendants = id(new PhabricatorProjectQuery())
- ->setViewer($viewer)
- ->withAncestorProjectPHIDs($except_phids)
- ->execute();
- foreach ($descendants as $descendant) {
- $except_phids[] = $descendant->getPHID();
- }
- }
-
- $rendering_engine = id(new PhabricatorBoardRenderingEngine())
- ->setViewer($viewer)
- ->setObjects(array($object))
- ->setExcludedProjectPHIDs($except_phids);
-
- $card = $rendering_engine->renderCard($object->getPHID());
-
- $item = $card->getItem();
- $item->addClass('phui-workcard');
-
- return id(new AphrontAjaxResponse())
- ->setContent(
- array(
- 'task' => $item,
- ));
+ return $this->newCardResponse($board_phid, $object_phid);
}
}
diff --git a/webroot/rsrc/css/phui/workboards/phui-workcard.css b/webroot/rsrc/css/phui/workboards/phui-workcard.css
--- a/webroot/rsrc/css/phui/workboards/phui-workcard.css
+++ b/webroot/rsrc/css/phui/workboards/phui-workcard.css
@@ -106,6 +106,10 @@
width: 263px;
}
+.phui-workcard.phui-object-item.phui-workcard-upload-target {
+ background-color: {$sh-greenbackground};
+}
+
/* - Draggable Colors --------------------------------------------------------*/
diff --git a/webroot/rsrc/js/application/projects/behavior-project-boards.js b/webroot/rsrc/js/application/projects/behavior-project-boards.js
--- a/webroot/rsrc/js/application/projects/behavior-project-boards.js
+++ b/webroot/rsrc/js/application/projects/behavior-project-boards.js
@@ -7,6 +7,7 @@
* javelin-stratcom
* javelin-workflow
* phabricator-draggable-list
+ * phabricator-drag-and-drop-file-upload
*/
JX.behavior('project-boards', function(config, statics) {
@@ -348,6 +349,39 @@
init_board();
}
});
+
+ if (JX.PhabricatorDragAndDropFileUpload.isSupported()) {
+ var drop = new JX.PhabricatorDragAndDropFileUpload('project-card')
+ .setURI(config.uploadURI)
+ .setChunkThreshold(config.chunkThreshold);
+
+ drop.listen('didBeginDrag', function(node) {
+ JX.DOM.alterClass(node, 'phui-workcard-upload-target', true);
+ });
+
+ drop.listen('didEndDrag', function(node) {
+ JX.DOM.alterClass(node, 'phui-workcard-upload-target', false);
+ });
+
+ drop.listen('didUpload', function(file) {
+ var node = file.getTargetNode();
+
+ var data = {
+ boardPHID: statics.projectPHID,
+ objectPHID: JX.Stratcom.getData(node).objectPHID,
+ filePHID: file.getPHID()
+ };
+
+ new JX.Workflow(config.coverURI, data)
+ .setHandler(function(r) {
+ JX.DOM.replace(node, JX.$H(r.task));
+ })
+ .start();
+ });
+
+ drop.start();
+ }
+
return true;
}
diff --git a/webroot/rsrc/js/core/DragAndDropFileUpload.js b/webroot/rsrc/js/core/DragAndDropFileUpload.js
--- a/webroot/rsrc/js/core/DragAndDropFileUpload.js
+++ b/webroot/rsrc/js/core/DragAndDropFileUpload.js
@@ -11,8 +11,12 @@
JX.install('PhabricatorDragAndDropFileUpload', {
- construct : function(node) {
- this._node = node;
+ construct : function(target) {
+ if (JX.DOM.isNode(target)) {
+ this._node = target;
+ } else {
+ this._sigil = target;
+ }
},
events : [
@@ -39,6 +43,7 @@
members : {
_node : null,
+ _sigil: null,
_depth : 0,
_isEnabled: false,
@@ -53,18 +58,21 @@
_updateDepth : function(delta) {
if (this._depth === 0 && delta > 0) {
- this.invoke('didBeginDrag');
+ this.invoke('didBeginDrag', this._getTarget());
}
this._depth += delta;
if (this._depth === 0 && delta < 0) {
- this.invoke('didEndDrag');
+ this.invoke('didEndDrag', this._getTarget());
}
},
- start : function() {
+ _getTarget: function() {
+ return this._target || this._node;
+ },
+ start : function() {
// TODO: move this to JX.DOM.contains()?
function contains(container, child) {
@@ -80,87 +88,87 @@
// Firefox has some issues sometimes; implement this click handler so
// the user can recover. See T5188.
- JX.DOM.listen(
- this._node,
- 'click',
- null,
- JX.bind(this, function (e) {
- if (!this.getIsEnabled()) {
- return;
- }
- if (this._depth) {
- e.kill();
- // Force depth to 0.
- this._updateDepth(-this._depth);
- }
- }));
+ var on_click = JX.bind(this, function (e) {
+ if (!this.getIsEnabled()) {
+ return;
+ }
+
+ if (this._depth) {
+ e.kill();
+ // Force depth to 0.
+ this._updateDepth(-this._depth);
+ }
+ });
// We track depth so that the _node may have children inside of it and
// not become unselected when they are dragged over.
- JX.DOM.listen(
- this._node,
- 'dragenter',
- null,
- JX.bind(this, function(e) {
- if (!this.getIsEnabled()) {
- return;
- }
+ var on_dragenter = JX.bind(this, function(e) {
+ if (!this.getIsEnabled()) {
+ return;
+ }
- if (contains(this._node, e.getTarget())) {
- this._updateDepth(1);
- }
- }));
-
- JX.DOM.listen(
- this._node,
- 'dragleave',
- null,
- JX.bind(this, function(e) {
- if (!this.getIsEnabled()) {
- return;
- }
+ if (!this._node && !this._depth) {
+ this._target = e.getNode(this._sigil);
+ }
- if (contains(this._node, e.getTarget())) {
- this._updateDepth(-1);
- }
- }));
-
- JX.DOM.listen(
- this._node,
- 'dragover',
- null,
- JX.bind(this, function(e) {
- if (!this.getIsEnabled()) {
- return;
- }
+ if (contains(this._getTarget(), e.getTarget())) {
+ this._updateDepth(1);
+ }
+ });
- // 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;
- }
+ var on_dragleave = JX.bind(this, function(e) {
+ if (!this.getIsEnabled()) {
+ return;
+ }
- e.kill();
+ if (contains(this._getTarget(), e.getTarget())) {
+ this._updateDepth(-1);
+ }
+ });
- var files = e.getRawEvent().dataTransfer.files;
- for (var ii = 0; ii < files.length; ii++) {
- this._sendRequest(files[ii]);
- }
+ var on_dragover = JX.bind(this, function(e) {
+ if (!this.getIsEnabled()) {
+ return;
+ }
- // Force depth to 0.
- this._updateDepth(-this._depth);
- }));
+ // NOTE: We must set this, or Chrome refuses to drop files from the
+ // download shelf.
+ e.getRawEvent().dataTransfer.dropEffect = 'copy';
+ e.kill();
+ });
+
+ var on_drop = JX.bind(this, function(e) {
+ if (!this.getIsEnabled()) {
+ return;
+ }
+
+ e.kill();
+
+ var files = e.getRawEvent().dataTransfer.files;
+ for (var ii = 0; ii < files.length; ii++) {
+ this._sendRequest(files[ii]);
+ }
+
+ // Force depth to 0.
+ this._updateDepth(-this._depth);
+ });
+
+ if (this._node) {
+ JX.DOM.listen(this._node, 'click', null, on_click);
+ JX.DOM.listen(this._node, 'dragenter', null, on_dragenter);
+ JX.DOM.listen(this._node, 'dragleave', null, on_dragleave);
+ JX.DOM.listen(this._node, 'dragover', null, on_dragover);
+ JX.DOM.listen(this._node, 'drop', null, on_drop);
+ } else {
+ JX.Stratcom.listen('click', this._sigil, on_click);
+ JX.Stratcom.listen('dragenter', this._sigil, on_dragenter);
+ JX.Stratcom.listen('dragleave', this._sigil, on_dragleave);
+ JX.Stratcom.listen('dragover', this._sigil, on_dragover);
+ JX.Stratcom.listen('drop', this._sigil, on_drop);
+ }
- if (JX.PhabricatorDragAndDropFileUpload.isPasteSupported()) {
+ if (JX.PhabricatorDragAndDropFileUpload.isPasteSupported() &&
+ this._node) {
JX.DOM.listen(
this._node,
'paste',
@@ -399,6 +407,7 @@
.setURI(r.uri)
.setMarkup(r.html)
.setStatus('done')
+ .setTargetNode(this._getTarget())
.update();
this.invoke('didUpload', file);
diff --git a/webroot/rsrc/js/core/FileUpload.js b/webroot/rsrc/js/core/FileUpload.js
--- a/webroot/rsrc/js/core/FileUpload.js
+++ b/webroot/rsrc/js/core/FileUpload.js
@@ -23,6 +23,7 @@
URI: null,
status: null,
markup: null,
+ targetNode: null,
error: null
},

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 19, 2:42 AM (18 h, 58 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6905747
Default Alt Text
D15202.id36699.diff (29 KB)

Event Timeline