diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '937616c0', + 'core.pkg.css' => '970b3ceb', 'core.pkg.js' => 'adc34883', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '5c459f92', @@ -151,7 +151,7 @@ 'rsrc/css/phui/phui-comment-form.css' => '68a2d99a', 'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0', 'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf', - 'rsrc/css/phui/phui-curtain-object-ref-view.css' => '12404744', + 'rsrc/css/phui/phui-curtain-object-ref-view.css' => '5f752bdb', 'rsrc/css/phui/phui-curtain-view.css' => '68c5efb6', 'rsrc/css/phui/phui-document-pro.css' => 'b9613a10', 'rsrc/css/phui/phui-document-summary.css' => 'b068eed1', @@ -845,7 +845,7 @@ 'phui-comment-form-css' => '68a2d99a', 'phui-comment-panel-css' => 'ec4e31c0', 'phui-crumbs-view-css' => '614f43cf', - 'phui-curtain-object-ref-view-css' => '12404744', + 'phui-curtain-object-ref-view-css' => '5f752bdb', 'phui-curtain-view-css' => '68c5efb6', 'phui-document-summary-view-css' => 'b068eed1', 'phui-document-view-css' => '52b748a5', diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php --- a/src/applications/phid/PhabricatorObjectHandle.php +++ b/src/applications/phid/PhabricatorObjectHandle.php @@ -32,6 +32,7 @@ private $tokenIcon; private $commandLineObjectName; private $mailStampName; + private $capabilities = array(); public function setIcon($icon) { $this->icon = $icon; @@ -388,6 +389,68 @@ return idx($types, $this->getType()); } + public function hasCapabilities() { + return ($this->getType() === PhabricatorPeopleUserPHIDType::TYPECONST); + } + + public function attachCapability( + PhabricatorPolicyInterface $object, + $capability, + $has_capability) { + + if (!$this->hasCapabilities()) { + throw new Exception( + pht( + 'Attempting to attach capability ("%s") for object ("%s") to '. + 'handle, but this handle (of type "%s") can not have '. + 'capabilities.', + $capability, + get_class($object), + $this->getType())); + } + + $object_key = $this->getObjectCapabilityKey($object); + $this->capabilities[$object_key][$capability] = $has_capability; + + return $this; + } + + public function hasViewCapability(PhabricatorPolicyInterface $object) { + return $this->hasCapability($object, PhabricatorPolicyCapability::CAN_VIEW); + } + + private function hasCapability( + PhabricatorPolicyInterface $object, + $capability) { + + $object_key = $this->getObjectCapabilityKey($object); + + if (!isset($this->capabilities[$object_key][$capability])) { + throw new Exception( + pht( + 'Attempting to test capability "%s" for handle of type "%s", but '. + 'this capability has not been attached.', + $capability, + $this->getType())); + } + + return $this->capabilities[$object_key][$capability]; + } + + private function getObjectCapabilityKey(PhabricatorPolicyInterface $object) { + $object_phid = $object->getPHID(); + + if (!$object_phid) { + throw new Exception( + pht( + 'Object (of class "%s") has no PHID, so handles can not interact '. + 'with capabilities for it.', + get_class($object))); + } + + return $object_phid; + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/policy/filter/PhabricatorPolicyFilterSet.php b/src/applications/policy/filter/PhabricatorPolicyFilterSet.php --- a/src/applications/policy/filter/PhabricatorPolicyFilterSet.php +++ b/src/applications/policy/filter/PhabricatorPolicyFilterSet.php @@ -102,4 +102,66 @@ $this->queue = array(); } + public static function loadHandleViewCapabilities( + $viewer, + $handles, + array $objects) { + + $capabilities = array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + + assert_instances_of($objects, 'PhabricatorPolicyInterface'); + + if (!$objects) { + return; + } + + $viewer_map = array(); + foreach ($handles as $handle_key => $handle) { + if (!$handle->hasCapabilities()) { + continue; + } + $viewer_map[$handle->getPHID()] = $handle_key; + } + + if (!$viewer_map) { + return; + } + + $users = id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->withPHIDs(array_keys($viewer_map)) + ->execute(); + $users = mpull($users, null, 'getPHID'); + + $filter_set = new self(); + + foreach ($users as $user_phid => $user) { + foreach ($objects as $object) { + foreach ($capabilities as $capability) { + $filter_set->addCapability($user, $object, $capability); + } + } + } + + foreach ($users as $user_phid => $user) { + $handle_key = $viewer_map[$user_phid]; + $handle = $handles[$handle_key]; + foreach ($objects as $object) { + foreach ($capabilities as $capability) { + $has_capability = $filter_set->hasCapability( + $user, + $object, + $capability); + + $handle->attachCapability( + $object, + $capability, + $has_capability); + } + } + } + } + } diff --git a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php --- a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php +++ b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php @@ -62,17 +62,30 @@ $handles = $viewer->loadHandles($visible_phids); } + PhabricatorPolicyFilterSet::loadHandleViewCapabilities( + $viewer, + $handles, + array($object)); + $ref_list = id(new PHUICurtainObjectRefListView()) ->setViewer($viewer) ->setEmptyMessage(pht('None')); foreach ($visible_phids as $phid) { + $handle = $handles[$phid]; + $ref = $ref_list->newObjectRefView() - ->setHandle($handles[$phid]); + ->setHandle($handle); if ($phid === $viewer_phid) { $ref->setHighlighted(true); } + + if ($handle->hasCapabilities()) { + if (!$handle->hasViewCapability($object)) { + $ref->setExiled(true); + } + } } if ($show_all) { diff --git a/src/view/phui/PHUICurtainObjectRefView.php b/src/view/phui/PHUICurtainObjectRefView.php --- a/src/view/phui/PHUICurtainObjectRefView.php +++ b/src/view/phui/PHUICurtainObjectRefView.php @@ -6,6 +6,7 @@ private $handle; private $epoch; private $highlighted; + private $exiled; public function setHandle(PhabricatorObjectHandle $handle) { $this->handle = $handle; @@ -22,6 +23,11 @@ return $this; } + public function setExiled($is_exiled) { + $this->exiled = $is_exiled; + return $this; + } + protected function getTagAttributes() { $classes = array(); $classes[] = 'phui-curtain-object-ref-view'; @@ -29,6 +35,11 @@ if ($this->highlighted) { $classes[] = 'phui-curtain-object-ref-view-highlighted'; } + + if ($this->exiled) { + $classes[] = 'phui-curtain-object-ref-view-exiled'; + } + $classes = implode(' ', $classes); return array( @@ -60,6 +71,24 @@ $more_rows[] = phutil_tag('tr', array(), $epoch_cells); } + if ($this->exiled) { + $exiled_view = array( + id(new PHUIIconView())->setIcon('fa-eye-slash red'), + ' ', + pht('No View Permission'), + ); + + $exiled_cells = array(); + $exiled_cells[] = phutil_tag( + 'td', + array( + 'class' => 'phui-curtain-object-ref-view-exiled-cell', + ), + $exiled_view); + + $more_rows[] = phutil_tag('tr', array(), $exiled_cells); + } + $header_cells = array(); $image_view = $this->newImage(); diff --git a/webroot/rsrc/css/phui/phui-curtain-object-ref-view.css b/webroot/rsrc/css/phui/phui-curtain-object-ref-view.css --- a/webroot/rsrc/css/phui/phui-curtain-object-ref-view.css +++ b/webroot/rsrc/css/phui/phui-curtain-object-ref-view.css @@ -12,6 +12,10 @@ border-radius: 3px; } +.phui-curtain-object-ref-view + .phui-curtain-object-ref-view { + margin-top: 1px; +} + .phui-curtain-object-ref-view-image-cell { min-width: 32px; padding-bottom: 24px; @@ -82,3 +86,12 @@ .phui-curtain-object-ref-view-highlighted { background: {$bluebackground}; } + +.phui-curtain-object-ref-view-exiled { + background: {$lightred}; + opacity: 0.75; +} + +.phui-curtain-object-ref-view-exiled-cell { + color: {$red}; +}