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

Improve some Spaces behaviors

Summary:
Ref T8449. Try out some more subtle behaviors:

- Make the "Space" control part of the policy control, so the UI shows "Visible To: [Space][Policy]". I think this helps make the role of spaces more clear. It also makes them easier to implement.
- Don't show the default space in headers: instead, show nothing.
- If the user has access to only one space, pretend spaces don't exist (no edit controls, no header stuff).

This might be confusing, but I think most of the time it will all align fairly well with user expectation.

Test Plan:
- Viewed a list of pastes (saw Space with non-default space, no space with default space, no space with user in only one space).
- Viewed a paste (saw Space with non-default space, saw no space with default space, saw no space with user in only one space).
- Edited spaces on objects (control as privileged user, no control as locked user).
- Created a new paste in a space (got space select as privileged user, no select as locked user).

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T8449

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

+116 -65
+1 -6
src/applications/paste/controller/PhabricatorPasteEditController.php
··· 167 167 ->setObject($paste) 168 168 ->execute(); 169 169 170 - $form->appendControl( 171 - id(new PhabricatorSpacesControl()) 172 - ->setObject($paste) 173 - ->setValue($v_space) 174 - ->setName('spacePHID')); 175 - 176 170 $form->appendChild( 177 171 id(new AphrontFormPolicyControl()) 178 172 ->setUser($user) ··· 180 174 ->setPolicyObject($paste) 181 175 ->setPolicies($policies) 182 176 ->setValue($v_view_policy) 177 + ->setSpacePHID($v_space) 183 178 ->setName('can_view')); 184 179 185 180 $form->appendChild(
+9
src/applications/people/storage/PhabricatorUser.php
··· 758 758 // TODO: We might let the user switch which space they're "in" later on; 759 759 // for now just use the global space if one exists. 760 760 761 + // If the viewer has access to the default space, use that. 761 762 $spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($this); 762 763 foreach ($spaces as $space) { 763 764 if ($space->getIsDefaultNamespace()) { 764 765 return $space->getPHID(); 765 766 } 767 + } 768 + 769 + // Otherwise, use the space with the lowest ID that they have access to. 770 + // This just tends to keep the default stable and predictable over time, 771 + // so adding a new space won't change behavior for users. 772 + if ($spaces) { 773 + $spaces = msort($spaces, 'getID'); 774 + return head($spaces)->getPHID(); 766 775 } 767 776 768 777 return null;
+11
src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php
··· 90 90 return (bool)self::getAllSpaces(); 91 91 } 92 92 93 + public static function getViewerSpacesExist(PhabricatorUser $viewer) { 94 + if (!self::getSpacesExist()) { 95 + return false; 96 + } 97 + 98 + // If the viewer has access to only one space, pretend spaces simply don't 99 + // exist. 100 + $spaces = self::getViewerSpaces($viewer); 101 + return (count($spaces) > 1); 102 + } 103 + 93 104 public static function getAllSpaces() { 94 105 $cache = PhabricatorCaches::getRequestCache(); 95 106 $cache_key = self::KEY_ALL;
+13
src/applications/spaces/view/PHUISpacesNamespaceContextView.php
··· 21 21 return null; 22 22 } 23 23 24 + // If the viewer can't see spaces, pretend they don't exist. 24 25 $viewer = $this->getUser(); 26 + if (!PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer)) { 27 + return null; 28 + } 29 + 30 + // If this is the default space, don't show a space label. 31 + $default = PhabricatorSpacesNamespaceQuery::getDefaultSpace(); 32 + if ($default) { 33 + if ($default->getPHID() == $space_phid) { 34 + return null; 35 + } 36 + } 37 + 25 38 return phutil_tag( 26 39 'span', 27 40 array(
-49
src/applications/spaces/view/PhabricatorSpacesControl.php
··· 1 - <?php 2 - 3 - final class PhabricatorSpacesControl extends AphrontFormControl { 4 - 5 - private $object; 6 - 7 - protected function shouldRender() { 8 - // Render this control only if some Spaces exist. 9 - return PhabricatorSpacesNamespaceQuery::getAllSpaces(); 10 - } 11 - 12 - public function setObject(PhabricatorSpacesInterface $object) { 13 - $this->object = $object; 14 - return $this; 15 - } 16 - 17 - protected function getCustomControlClass() { 18 - return ''; 19 - } 20 - 21 - protected function getOptions() { 22 - $viewer = $this->getUser(); 23 - $viewer_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($viewer); 24 - 25 - $map = mpull($viewer_spaces, 'getNamespaceName', 'getPHID'); 26 - asort($map); 27 - 28 - return $map; 29 - } 30 - 31 - protected function renderInput() { 32 - $viewer = $this->getUser(); 33 - 34 - $this->setLabel(pht('Space')); 35 - 36 - $value = $this->getValue(); 37 - if ($value === null) { 38 - $value = $viewer->getDefaultSpacePHID(); 39 - } 40 - 41 - return AphrontFormSelectControl::renderSelectTag( 42 - $value, 43 - $this->getOptions(), 44 - array( 45 - 'name' => $this->getName(), 46 - )); 47 - } 48 - 49 - }
+25 -10
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 360 360 case PhabricatorTransactions::TYPE_SPACE: 361 361 $space_phid = $xaction->getNewValue(); 362 362 if (!strlen($space_phid)) { 363 - // If an install has no Spaces, we might end up with the empty string 364 - // here instead of a strict `null`. Just make this work like callers 365 - // might reasonably expect. 366 - return null; 363 + // If an install has no Spaces or the Spaces controls are not visible 364 + // to the viewer, we might end up with the empty string here instead 365 + // of a strict `null`, because some controller just used `getStr()` 366 + // to read the space PHID from the request. 367 + // Just make this work like callers might reasonably expect so we 368 + // don't need to handle this specially in every EditController. 369 + return $this->getActor()->getDefaultSpacePHID(); 367 370 } else { 368 371 return $space_phid; 369 372 } ··· 2002 2005 $transaction_type) { 2003 2006 $errors = array(); 2004 2007 2005 - $all_spaces = PhabricatorSpacesNamespaceQuery::getAllSpaces(); 2006 - $viewer_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces( 2007 - $this->getActor()); 2008 + $actor = $this->getActor(); 2009 + 2010 + $has_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($actor); 2011 + $actor_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($actor); 2008 2012 foreach ($xactions as $xaction) { 2009 2013 $space_phid = $xaction->getNewValue(); 2010 2014 2011 2015 if ($space_phid === null) { 2012 - if (!$all_spaces) { 2016 + if (!$has_spaces) { 2013 2017 // The install doesn't have any spaces, so this is fine. 2014 2018 continue; 2015 2019 } ··· 2026 2030 2027 2031 // If the PHID isn't `null`, it needs to be a valid space that the 2028 2032 // viewer can see. 2029 - if (empty($viewer_spaces[$space_phid])) { 2033 + if (empty($actor_spaces[$space_phid])) { 2030 2034 $errors[] = new PhabricatorApplicationTransactionValidationError( 2031 2035 $transaction_type, 2032 2036 pht('Invalid'), ··· 2045 2049 PhabricatorLiskDAO $object, 2046 2050 array $xactions) { 2047 2051 2048 - return clone $object; 2052 + $copy = clone $object; 2053 + 2054 + foreach ($xactions as $xaction) { 2055 + switch ($xaction->getTransactionType()) { 2056 + case PhabricatorTransactions::TYPE_SPACE: 2057 + $space_phid = $this->getTransactionNewValue($object, $xaction); 2058 + $copy->setSpacePHID($space_phid); 2059 + break; 2060 + } 2061 + } 2062 + 2063 + return $copy; 2049 2064 } 2050 2065 2051 2066 protected function validateAllTransactions(
+57
src/view/form/control/AphrontFormPolicyControl.php
··· 5 5 private $object; 6 6 private $capability; 7 7 private $policies; 8 + private $spacePHID; 8 9 9 10 public function setPolicyObject(PhabricatorPolicyInterface $object) { 10 11 $this->object = $object; ··· 15 16 assert_instances_of($policies, 'PhabricatorPolicy'); 16 17 $this->policies = $policies; 17 18 return $this; 19 + } 20 + 21 + public function setSpacePHID($space_phid) { 22 + $this->spacePHID = $space_phid; 23 + return $this; 24 + } 25 + 26 + public function getSpacePHID() { 27 + return $this->spacePHID; 18 28 } 19 29 20 30 public function setCapability($capability) { ··· 187 197 $selected_icon = idx($selected, 'icon'); 188 198 $selected_name = idx($selected, 'name'); 189 199 200 + $spaces_control = $this->buildSpacesControl(); 201 + 190 202 return phutil_tag( 191 203 'div', 192 204 array( 193 205 ), 194 206 array( 207 + $spaces_control, 195 208 javelin_tag( 196 209 'a', 197 210 array( ··· 231 244 return 'custom:placeholder'; 232 245 } 233 246 247 + private function buildSpacesControl() { 248 + if ($this->capability != PhabricatorPolicyCapability::CAN_VIEW) { 249 + return null; 250 + } 251 + 252 + if (!($this->object instanceof PhabricatorSpacesInterface)) { 253 + return null; 254 + } 255 + 256 + $viewer = $this->getUser(); 257 + if (!PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer)) { 258 + return null; 259 + } 260 + 261 + $space_phid = $this->getSpacePHID(); 262 + if ($space_phid === null) { 263 + $space_phid = $viewer->getDefaultSpacePHID(); 264 + } 265 + 266 + $select = AphrontFormSelectControl::renderSelectTag( 267 + $space_phid, 268 + $this->getSpaceOptions(), 269 + array( 270 + 'name' => 'spacePHID', 271 + )); 272 + 273 + return $select; 274 + } 275 + 276 + protected function getSpaceOptions() { 277 + $viewer = $this->getUser(); 278 + $viewer_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($viewer); 279 + 280 + $map = array(); 281 + foreach ($viewer_spaces as $space) { 282 + $map[$space->getPHID()] = pht( 283 + 'Space %s: %s', 284 + $space->getMonogram(), 285 + $space->getNamespaceName()); 286 + } 287 + asort($map); 288 + 289 + return $map; 290 + } 234 291 }