diff --git a/src/applications/policy/query/PhabricatorPolicyQuery.php b/src/applications/policy/query/PhabricatorPolicyQuery.php index 55569356b5..9a078abf3e 100644 --- a/src/applications/policy/query/PhabricatorPolicyQuery.php +++ b/src/applications/policy/query/PhabricatorPolicyQuery.php @@ -1,217 +1,227 @@ object = $object; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public static function loadPolicies( PhabricatorUser $viewer, PhabricatorPolicyInterface $object) { $results = array(); $map = array(); foreach ($object->getCapabilities() as $capability) { $map[$capability] = $object->getPolicy($capability); } $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->withPHIDs($map) ->execute(); foreach ($map as $capability => $phid) { $results[$capability] = $policies[$phid]; } return $results; } public static function renderPolicyDescriptions( PhabricatorUser $viewer, PhabricatorPolicyInterface $object, $icon = false) { $policies = self::loadPolicies($viewer, $object); foreach ($policies as $capability => $policy) { $policies[$capability] = $policy->renderDescription($icon); } return $policies; } public function loadPage() { if ($this->object && $this->phids) { throw new Exception( "You can not issue a policy query with both setObject() and ". "setPHIDs()."); } else if ($this->object) { $phids = $this->loadObjectPolicyPHIDs(); } else { $phids = $this->phids; } $phids = array_fuse($phids); $results = array(); // First, load global policies. foreach ($this->getGlobalPolicies() as $phid => $policy) { if (isset($phids[$phid])) { $results[$phid] = $policy; unset($phids[$phid]); } } // If we still need policies, we're going to have to fetch data. Bucket // the remaining policies into rule-based policies and handle-based // policies. if ($phids) { $rule_policies = array(); $handle_policies = array(); foreach ($phids as $phid) { $phid_type = phid_get_type($phid); if ($phid_type == PhabricatorPolicyPHIDTypePolicy::TYPECONST) { $rule_policies[$phid] = $phid; } else { $handle_policies[$phid] = $phid; } } if ($handle_policies) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($handle_policies) ->execute(); foreach ($handle_policies as $phid) { $results[$phid] = PhabricatorPolicy::newFromPolicyAndHandle( $phid, $handles[$phid]); } } if ($rule_policies) { $rules = id(new PhabricatorPolicy())->loadAllWhere( 'phid IN (%Ls)', $rule_policies); $results += mpull($rules, null, 'getPHID'); } } $results = msort($results, 'getSortKey'); return $results; } public static function isGlobalPolicy($policy) { $globalPolicies = self::getGlobalPolicies(); if (isset($globalPolicies[$policy])) { return true; } return false; } public static function getGlobalPolicy($policy) { if (!self::isGlobalPolicy($policy)) { throw new Exception("Policy '{$policy}' is not a global policy!"); } return idx(self::getGlobalPolicies(), $policy); } private static function getGlobalPolicies() { static $constants = array( PhabricatorPolicies::POLICY_PUBLIC, PhabricatorPolicies::POLICY_USER, PhabricatorPolicies::POLICY_ADMIN, PhabricatorPolicies::POLICY_NOONE, ); $results = array(); foreach ($constants as $constant) { $results[$constant] = id(new PhabricatorPolicy()) ->setType(PhabricatorPolicyType::TYPE_GLOBAL) ->setPHID($constant) ->setName(self::getGlobalPolicyName($constant)) + ->setShortName(self::getGlobalPolicyShortName($constant)) ->makeEphemeral(); } return $results; } private static function getGlobalPolicyName($policy) { switch ($policy) { case PhabricatorPolicies::POLICY_PUBLIC: return pht('Public (No Login Required)'); case PhabricatorPolicies::POLICY_USER: return pht('All Users'); case PhabricatorPolicies::POLICY_ADMIN: return pht('Administrators'); case PhabricatorPolicies::POLICY_NOONE: return pht('No One'); default: return pht('Unknown Policy'); } } + private static function getGlobalPolicyShortName($policy) { + switch ($policy) { + case PhabricatorPolicies::POLICY_PUBLIC: + return pht('Public'); + default: + return null; + } + } + private function loadObjectPolicyPHIDs() { $phids = array(); $viewer = $this->getViewer(); if ($viewer->getPHID()) { $projects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withMemberPHIDs(array($viewer->getPHID())) ->execute(); foreach ($projects as $project) { $phids[] = $project->getPHID(); } } $capabilities = $this->object->getCapabilities(); foreach ($capabilities as $capability) { $policy = $this->object->getPolicy($capability); if (!$policy) { continue; } $phids[] = $policy; } // If this install doesn't have "Public" enabled, don't include it as an // option unless the object already has a "Public" policy. In this case we // retain the policy but enforce it as though it was "All Users". $show_public = PhabricatorEnv::getEnvConfig('policy.allow-public'); foreach ($this->getGlobalPolicies() as $phid => $policy) { if ($phid == PhabricatorPolicies::POLICY_PUBLIC) { if (!$show_public) { continue; } } $phids[] = $phid; } return $phids; } protected function shouldDisablePolicyFiltering() { // Policy filtering of policies is currently perilous and not required by // the application. return true; } } diff --git a/src/applications/policy/storage/PhabricatorPolicy.php b/src/applications/policy/storage/PhabricatorPolicy.php index 1ad41b4577..d124a10e5e 100644 --- a/src/applications/policy/storage/PhabricatorPolicy.php +++ b/src/applications/policy/storage/PhabricatorPolicy.php @@ -1,329 +1,342 @@ true, self::CONFIG_SERIALIZATION => array( 'rules' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorPolicyPHIDTypePolicy::TYPECONST); } public static function newFromPolicyAndHandle( $policy_identifier, PhabricatorObjectHandle $handle = null) { $is_global = PhabricatorPolicyQuery::isGlobalPolicy($policy_identifier); if ($is_global) { return PhabricatorPolicyQuery::getGlobalPolicy($policy_identifier); } if (!$handle) { throw new Exception( "Policy identifier is an object PHID ('{$policy_identifier}'), but no ". "object handle was provided. A handle must be provided for object ". "policies."); } $handle_phid = $handle->getPHID(); if ($policy_identifier != $handle_phid) { throw new Exception( "Policy identifier is an object PHID ('{$policy_identifier}'), but ". "the provided handle has a different PHID ('{$handle_phid}'). The ". "handle must correspond to the policy identifier."); } $policy = id(new PhabricatorPolicy()) ->setPHID($policy_identifier) ->setHref($handle->getURI()); $phid_type = phid_get_type($policy_identifier); switch ($phid_type) { case PhabricatorProjectPHIDTypeProject::TYPECONST: $policy->setType(PhabricatorPolicyType::TYPE_PROJECT); $policy->setName($handle->getName()); break; case PhabricatorPolicyPHIDTypePolicy::TYPECONST: // TODO: This creates a weird handle-based version of a rule policy. // It behaves correctly, but can't be applied since it doesn't have // any rules. It is used to render transactions, and might need some // cleanup. break; default: $policy->setType(PhabricatorPolicyType::TYPE_MASKED); $policy->setName($handle->getFullName()); break; } $policy->makeEphemeral(); return $policy; } public function setType($type) { $this->type = $type; return $this; } public function getType() { if (!$this->type) { return PhabricatorPolicyType::TYPE_CUSTOM; } return $this->type; } public function setName($name) { $this->name = $name; return $this; } public function getName() { if (!$this->name) { return pht('Custom Policy'); } return $this->name; } + public function setShortName($short_name) { + $this->shortName = $short_name; + return $this; + } + + public function getShortName() { + if ($this->shortName) { + return $this->shortName; + } + return $this->getName(); + } + public function setHref($href) { $this->href = $href; return $this; } public function getHref() { return $this->href; } public function getIcon() { switch ($this->getType()) { case PhabricatorPolicyType::TYPE_GLOBAL: static $map = array( PhabricatorPolicies::POLICY_PUBLIC => 'policy-public', PhabricatorPolicies::POLICY_USER => 'policy-all', PhabricatorPolicies::POLICY_ADMIN => 'policy-admin', PhabricatorPolicies::POLICY_NOONE => 'policy-noone', ); return idx($map, $this->getPHID(), 'policy-unknown'); break; case PhabricatorPolicyType::TYPE_PROJECT: return 'policy-project'; break; case PhabricatorPolicyType::TYPE_CUSTOM: case PhabricatorPolicyType::TYPE_MASKED: return 'policy-custom'; break; default: return 'policy-unknown'; break; } } public function getSortKey() { return sprintf( '%02d%s', PhabricatorPolicyType::getPolicyTypeOrder($this->getType()), $this->getSortName()); } private function getSortName() { if ($this->getType() == PhabricatorPolicyType::TYPE_GLOBAL) { static $map = array( PhabricatorPolicies::POLICY_PUBLIC => 0, PhabricatorPolicies::POLICY_USER => 1, PhabricatorPolicies::POLICY_ADMIN => 2, PhabricatorPolicies::POLICY_NOONE => 3, ); return idx($map, $this->getPHID()); } return $this->getName(); } public static function getPolicyExplanation( PhabricatorUser $viewer, $policy) { switch ($policy) { case PhabricatorPolicies::POLICY_PUBLIC: return pht('This object is public.'); case PhabricatorPolicies::POLICY_USER: return pht('Logged in users can take this action.'); case PhabricatorPolicies::POLICY_ADMIN: return pht('Administrators can take this action.'); case PhabricatorPolicies::POLICY_NOONE: return pht('By default, no one can take this action.'); default: $handle = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs(array($policy)) ->executeOne(); $type = phid_get_type($policy); if ($type == PhabricatorProjectPHIDTypeProject::TYPECONST) { return pht( 'Members of the project "%s" can take this action.', $handle->getFullName()); } else if ($type == PhabricatorPeoplePHIDTypeUser::TYPECONST) { return pht( '%s can take this action.', $handle->getFullName()); } else if ($type == PhabricatorPolicyPHIDTypePolicy::TYPECONST) { return pht( 'This object has a custom policy controlling who can take this '. 'action.'); } else { return pht( 'This object has an unknown or invalid policy setting ("%s").', $policy); } } } public function getFullName() { switch ($this->getType()) { case PhabricatorPolicyType::TYPE_PROJECT: return pht('Project: %s', $this->getName()); case PhabricatorPolicyType::TYPE_MASKED: return pht('Other: %s', $this->getName()); default: return $this->getName(); } } public function renderDescription($icon=false) { $img = null; if ($icon) { $img = id(new PHUIIconView()) ->setSpriteSheet(PHUIIconView::SPRITE_STATUS) ->setSpriteIcon($this->getIcon()); } if ($this->getHref()) { $desc = phutil_tag( 'a', array( 'href' => $this->getHref(), 'class' => 'policy-link', ), array( $img, $this->getName(), )); } else { if ($img) { $desc = array($img, $this->getName()); } else { $desc = $this->getName(); } } switch ($this->getType()) { case PhabricatorPolicyType::TYPE_PROJECT: return pht('%s (Project)', $desc); case PhabricatorPolicyType::TYPE_CUSTOM: return pht('Custom Policy'); case PhabricatorPolicyType::TYPE_MASKED: return pht( '%s (You do not have permission to view policy details.)', $desc); default: return $desc; } } /** * Return a list of custom rule classes (concrete subclasses of * @{class:PhabricatorPolicyRule}) this policy uses. * * @return list List of class names. */ public function getCustomRuleClasses() { $classes = array(); foreach ($this->getRules() as $rule) { $class = idx($rule, 'rule'); try { if (class_exists($class)) { $classes[$class] = $class; } } catch (Exception $ex) { continue; } } return array_keys($classes); } /** * Return a list of all values used by a given rule class to implement this * policy. This is used to bulk load data (like project memberships) in order * to apply policy filters efficiently. * * @param string Policy rule classname. * @return list List of values used in this policy. */ public function getCustomRuleValues($rule_class) { $values = array(); foreach ($this->getRules() as $rule) { if ($rule['rule'] == $rule_class) { $values[] = $rule['value']; } } return $values; } public function attachRuleObjects(array $objects) { $this->ruleObjects = $objects; return $this; } public function getRuleObjects() { return $this->assertAttached($this->ruleObjects); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, ); } public function getPolicy($capability) { // NOTE: We implement policies only so we can comply with the interface. // The actual query skips them, as enforcing policies on policies seems // perilous and isn't currently required by the application. return PhabricatorPolicies::POLICY_PUBLIC; } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return false; } public function describeAutomaticCapability($capability) { return null; } } diff --git a/src/view/phui/PHUIHeaderView.php b/src/view/phui/PHUIHeaderView.php index 1c960c98a7..5bfb58785a 100644 --- a/src/view/phui/PHUIHeaderView.php +++ b/src/view/phui/PHUIHeaderView.php @@ -1,231 +1,231 @@ header = $header; return $this; } public function setObjectName($object_name) { $this->objectName = $object_name; return $this; } public function setNoBackground($nada) { $this->noBackground = $nada; return $this; } public function addTag(PhabricatorTagView $tag) { $this->tags[] = $tag; return $this; } public function setImage($uri) { $this->image = $uri; return $this; } public function setSubheader($subheader) { $this->subheader = $subheader; return $this; } public function setBleedHeader($bleed) { $this->bleedHeader = $bleed; return $this; } public function setGradient($gradient) { $this->gradient = $gradient; return $this; } public function setPolicyObject(PhabricatorPolicyInterface $object) { $this->policyObject = $object; return $this; } public function addProperty($property, $value) { $this->properties[$property] = $value; return $this; } public function setStatus($icon, $color, $name) { $header_class = 'phui-header-status'; if ($color) { $icon = $icon.'-'.$color; $header_class = $header_class.'-'.$color; } $img = id(new PHUIIconView()) ->setSpriteSheet(PHUIIconView::SPRITE_STATUS) ->setSpriteIcon($icon); $tag = phutil_tag( 'span', array( 'class' => "{$header_class} plr", ), array( $img, $name, )); return $this->addProperty(self::PROPERTY_STATUS, $tag); } public function render() { require_celerity_resource('phui-header-view-css'); $classes = array(); $classes[] = 'phui-header-shell'; if ($this->noBackground) { $classes[] = 'phui-header-no-backgound'; } if ($this->bleedHeader) { $classes[] = 'phui-bleed-header'; } if ($this->gradient) { $classes[] = 'sprite-gradient'; $classes[] = 'gradient-'.$this->gradient.'-header'; } if ($this->properties || $this->policyObject || $this->subheader) { $classes[] = 'phui-header-tall'; } $image = null; if ($this->image) { $image = phutil_tag( 'span', array( 'class' => 'phui-header-image', 'style' => 'background-image: url('.$this->image.')', ), ''); $classes[] = 'phui-header-has-image'; } $header = array(); $header[] = $this->header; if ($this->objectName) { array_unshift( $header, phutil_tag( 'a', array( 'href' => '/'.$this->objectName, ), $this->objectName), ' '); } if ($this->tags) { $header[] = ' '; $header[] = phutil_tag( 'span', array( 'class' => 'phui-header-tags', ), array_interleave(' ', $this->tags)); } if ($this->subheader) { $header[] = phutil_tag( 'div', array( 'class' => 'phui-header-subheader', ), $this->subheader); } if ($this->properties || $this->policyObject) { $property_list = array(); foreach ($this->properties as $type => $property) { switch ($type) { case self::PROPERTY_STATUS: $property_list[] = $property; break; default: throw new Exception('Incorrect Property Passed'); break; } } if ($this->policyObject) { $property_list[] = $this->renderPolicyProperty($this->policyObject); } $header[] = phutil_tag( 'div', array( 'class' => 'phui-header-subheader', ), $property_list); } return phutil_tag( 'div', array( 'class' => implode(' ', $classes), ), array( $image, phutil_tag( 'h1', array( 'class' => 'phui-header-view', ), $header), )); } private function renderPolicyProperty(PhabricatorPolicyInterface $object) { $policies = PhabricatorPolicyQuery::loadPolicies( $this->getUser(), $object); $view_capability = PhabricatorPolicyCapability::CAN_VIEW; $policy = idx($policies, $view_capability); if (!$policy) { return null; } $phid = $object->getPHID(); $icon = id(new PHUIIconView()) ->setSpriteSheet(PHUIIconView::SPRITE_STATUS) ->setSpriteIcon($policy->getIcon()); $link = javelin_tag( 'a', array( 'class' => 'policy-link', 'href' => '/policy/explain/'.$phid.'/'.$view_capability.'/', 'sigil' => 'workflow', ), - $policy->getName()); + $policy->getShortName()); return array($icon, $link); } }