@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.

Introduce "bridged" objects

Summary:
Ref T10537. These are objects which are bound to some external object, like a Maniphest task which is a representation of a GitHub issue.

This doesn't do much yet and may change, but my thinking is:

- I'm putting these on-object instead of on edges because I think we want to actively change the UI for them (e.g., clearly call out that the object is bridged) but don't want every page to need to do extra queries in the common case where zero bridged objects exist anywhere in the system.
- I'm making these one-to-one, more or less: an issue can't be bridged to a bunch of tasks, nor can a bunch of tasks be bridged to a single issue. Pretty sure this makes sense? I can't come up with any reasonable, realistic cases where you want a single GitHub issue to publish to multiple different tasks in Maniphest.
- Technically, one type of each bridgable object could be bridged, but I expect this to never actually occur. Hopefully.

Test Plan: Ran storage upgrade, loaded some pages.

Reviewers: chad

Reviewed By: chad

Subscribers: Luke081515.2

Maniphest Tasks: T10537

Differential Revision: https://secure.phabricator.com/D15502

+99 -1
+2
resources/sql/autopatches/20160321.nuance.01.taskbridge.sql
··· 1 + ALTER TABLE {$NAMESPACE}_maniphest.maniphest_task 2 + ADD bridgedObjectPHID VARBINARY(64);
+2
src/__phutil_library_map__.php
··· 845 845 'DoorkeeperBridgeGitHubIssue' => 'applications/doorkeeper/bridge/DoorkeeperBridgeGitHubIssue.php', 846 846 'DoorkeeperBridgeJIRA' => 'applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php', 847 847 'DoorkeeperBridgeJIRATestCase' => 'applications/doorkeeper/bridge/__tests__/DoorkeeperBridgeJIRATestCase.php', 848 + 'DoorkeeperBridgedObjectInterface' => 'applications/doorkeeper/bridge/DoorkeeperBridgedObjectInterface.php', 848 849 'DoorkeeperDAO' => 'applications/doorkeeper/storage/DoorkeeperDAO.php', 849 850 'DoorkeeperExternalObject' => 'applications/doorkeeper/storage/DoorkeeperExternalObject.php', 850 851 'DoorkeeperExternalObjectQuery' => 'applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php', ··· 5636 5637 'PhabricatorSpacesInterface', 5637 5638 'PhabricatorConduitResultInterface', 5638 5639 'PhabricatorFulltextInterface', 5640 + 'DoorkeeperBridgedObjectInterface', 5639 5641 ), 5640 5642 'ManiphestTaskAssignHeraldAction' => 'HeraldAction', 5641 5643 'ManiphestTaskAssignOtherHeraldAction' => 'ManiphestTaskAssignHeraldAction',
+8
src/applications/doorkeeper/bridge/DoorkeeperBridgedObjectInterface.php
··· 1 + <?php 2 + 3 + interface DoorkeeperBridgedObjectInterface { 4 + 5 + public function getBridgedObject(); 6 + public function attachBridgedObject(DoorkeeperExternalObject $object = null); 7 + 8 + }
+13
src/applications/maniphest/query/ManiphestTaskQuery.php
··· 19 19 private $dateModifiedBefore; 20 20 private $subpriorityMin; 21 21 private $subpriorityMax; 22 + private $bridgedObjectPHIDs; 22 23 23 24 private $fullTextSearch = ''; 24 25 ··· 205 206 206 207 public function needProjectPHIDs($bool) { 207 208 $this->needProjectPHIDs = $bool; 209 + return $this; 210 + } 211 + 212 + public function withBridgedObjectPHIDs(array $phids) { 213 + $this->bridgedObjectPHIDs = $phids; 208 214 return $this; 209 215 } 210 216 ··· 415 421 $conn, 416 422 'task.subpriority <= %f', 417 423 $this->subpriorityMax); 424 + } 425 + 426 + if ($this->bridgedObjectPHIDs !== null) { 427 + $where[] = qsprintf( 428 + $conn, 429 + 'task.bridgedObjectPHID IN (%Ls)', 430 + $this->bridgedObjectPHIDs); 418 431 } 419 432 420 433 return $where;
+23 -1
src/applications/maniphest/storage/ManiphestTask.php
··· 15 15 PhabricatorProjectInterface, 16 16 PhabricatorSpacesInterface, 17 17 PhabricatorConduitResultInterface, 18 - PhabricatorFulltextInterface { 18 + PhabricatorFulltextInterface, 19 + DoorkeeperBridgedObjectInterface { 19 20 20 21 const MARKUP_FIELD_DESCRIPTION = 'markup:desc'; 21 22 ··· 36 37 37 38 protected $ownerOrdering; 38 39 protected $spacePHID; 40 + protected $bridgedObjectPHID; 39 41 protected $properties = array(); 40 42 protected $points; 41 43 ··· 43 45 private $groupByProjectPHID = self::ATTACHABLE; 44 46 private $customFields = self::ATTACHABLE; 45 47 private $edgeProjectPHIDs = self::ATTACHABLE; 48 + private $bridgedObject = self::ATTACHABLE; 46 49 47 50 public static function initializeNewTask(PhabricatorUser $actor) { 48 51 $app = id(new PhabricatorApplicationQuery()) ··· 82 85 'originalEmailSource' => 'text255?', 83 86 'subpriority' => 'double', 84 87 'points' => 'double?', 88 + 'bridgedObjectPHID' => 'phid?', 85 89 ), 86 90 self::CONFIG_KEY_SCHEMA => array( 87 91 'key_phid' => null, ··· 115 119 ), 116 120 'key_title' => array( 117 121 'columns' => array('title(64)'), 122 + ), 123 + 'key_bridgedobject' => array( 124 + 'columns' => array('bridgedObjectPHID'), 125 + 'unique' => true, 118 126 ), 119 127 ), 120 128 ) + parent::getConfiguration(); ··· 502 510 503 511 public function newFulltextEngine() { 504 512 return new ManiphestTaskFulltextEngine(); 513 + } 514 + 515 + 516 + /* -( DoorkeeperBridgedObjectInterface )----------------------------------- */ 517 + 518 + 519 + public function getBridgedObject() { 520 + return $this->assertAttached($this->bridgedObject); 521 + } 522 + 523 + public function attachBridgedObject( 524 + DoorkeeperExternalObject $object = null) { 525 + $this->bridgedObject = $object; 526 + return $this; 505 527 } 506 528 507 529 }
+51
src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php
··· 234 234 235 235 if ($page) { 236 236 $maybe_visible = $this->willFilterPage($page); 237 + if ($maybe_visible) { 238 + $maybe_visible = $this->applyWillFilterPageExtensions($maybe_visible); 239 + } 237 240 } else { 238 241 $maybe_visible = array(); 239 242 } ··· 670 673 671 674 $viewer = $this->getViewer(); 672 675 return PhabricatorApplication::isClassInstalledForViewer($class, $viewer); 676 + } 677 + 678 + private function applyWillFilterPageExtensions(array $page) { 679 + $bridges = array(); 680 + foreach ($page as $key => $object) { 681 + if ($object instanceof DoorkeeperBridgedObjectInterface) { 682 + $bridges[$key] = $object; 683 + } 684 + } 685 + 686 + if ($bridges) { 687 + $external_phids = array(); 688 + foreach ($bridges as $bridge) { 689 + $external_phid = $bridge->getBridgedObjectPHID(); 690 + if ($external_phid) { 691 + $external_phids[$key] = $external_phid; 692 + } 693 + } 694 + 695 + if ($external_phids) { 696 + $external_objects = id(new DoorkeeperExternalObjectQuery()) 697 + ->setViewer($this->getViewer()) 698 + ->withPHIDs($external_phids) 699 + ->execute(); 700 + $external_objects = mpull($external_objects, null, 'getPHID'); 701 + } else { 702 + $external_objects = array(); 703 + } 704 + 705 + foreach ($bridges as $key => $bridge) { 706 + $external_phid = idx($external_phids, $key); 707 + if (!$external_phid) { 708 + $bridge->attachBridgedObject(null); 709 + continue; 710 + } 711 + 712 + $external_object = idx($external_objects, $external_phid); 713 + if (!$external_object) { 714 + $this->didRejectResult($bridge); 715 + unset($page[$key]); 716 + continue; 717 + } 718 + 719 + $bridge->attachBridgedObject($external_object); 720 + } 721 + } 722 + 723 + return $page; 673 724 } 674 725 675 726 }