diff --git a/src/applications/policy/rule/PhabricatorPolicyRule.php b/src/applications/policy/rule/PhabricatorPolicyRule.php index 59fcc954c7..bd5af9a1b8 100644 --- a/src/applications/policy/rule/PhabricatorPolicyRule.php +++ b/src/applications/policy/rule/PhabricatorPolicyRule.php @@ -1,197 +1,200 @@ new AphrontTokenizerTemplateView(), 'uri' => $datasource->getDatasourceURI(), 'placeholder' => $datasource->getPlaceholderText(), 'browseURI' => $datasource->getBrowseURI(), ); } public function getRuleOrder() { return 500; } public function getValueForStorage($value) { return $value; } public function getValueForDisplay(PhabricatorUser $viewer, $value) { return $value; } public function getRequiredHandlePHIDsForSummary($value) { $phids = array(); + switch ($this->getValueControlType()) { case self::CONTROL_TYPE_TOKENIZER: $phids = $value; break; case self::CONTROL_TYPE_TEXT: case self::CONTROL_TYPE_SELECT: case self::CONTROL_TYPE_NONE: default: if (phid_get_type($value) != PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) { $phids = array($value); } else { $phids = array(); } break; } return $phids; } /** - * Return true if the given value creates a rule with a meaningful effect. + * Return `true` if the given value creates a rule with a meaningful effect. * An example of a rule with no meaningful effect is a "users" rule with no * users specified. * * @return bool True if the value creates a meaningful rule. */ public function ruleHasEffect($value) { return true; } /* -( Transaction Hints )-------------------------------------------------- */ /** * Tell policy rules about upcoming transaction effects. * * Before transaction effects are applied, we try to stop users from making * edits which will lock them out of objects. We can't do this perfectly, * since they can set a policy to "the moon is full" moments before it wanes, * but we try to prevent as many mistakes as possible. * * Some policy rules depend on complex checks against object state which * we can't set up ahead of time. For example, subscriptions require database * writes. * * In cases like this, instead of doing writes, you can pass a hint about an * object to a policy rule. The rule can then look for hints and use them in * rendering a verdict about whether the user will be able to see the object * or not after applying the policy change. * * @param PhabricatorPolicyInterface Object to pass a hint about. * @param PhabricatorPolicyRule Rule to pass hint to. * @param wild Hint. * @return void */ public static function passTransactionHintToRule( PhabricatorPolicyInterface $object, PhabricatorPolicyRule $rule, $hint) { $cache = PhabricatorCaches::getRequestCache(); $cache->setKey(self::getObjectPolicyCacheKey($object, $rule), $hint); } - protected function getTransactionHint( + final protected function getTransactionHint( PhabricatorPolicyInterface $object) { $cache = PhabricatorCaches::getRequestCache(); return $cache->getKey(self::getObjectPolicyCacheKey($object, $this)); } private static function getObjectPolicyCacheKey( PhabricatorPolicyInterface $object, PhabricatorPolicyRule $rule) { $hash = spl_object_hash($object); $rule = get_class($rule); return 'policycache.'.$hash.'.'.$rule; } /* -( Implementing Object Policies )--------------------------------------- */ /** * Return a unique string like "maniphest.author" to expose this rule as an * object policy. * * Object policy rules, like "Task Author", are more advanced than basic * policy rules (like "All Users") but not as powerful as custom rules. * * @return string Unique identifier for this rule. * @task objectpolicy */ public function getObjectPolicyKey() { return null; } - public function getObjectPolicyFullKey() { + final public function getObjectPolicyFullKey() { $key = $this->getObjectPolicyKey(); if (!$key) { throw new Exception( pht( 'This policy rule (of class "%s") does not have an associated '. 'object policy key.', get_class($this))); } return PhabricatorPolicyQuery::OBJECT_POLICY_PREFIX.$key; } public function getObjectPolicyName() { throw new PhutilMethodNotImplementedException(); } public function getObjectPolicyShortName() { return $this->getObjectPolicyName(); } public function getObjectPolicyIcon() { return 'fa-cube'; } public function getPolicyExplanation() { throw new PhutilMethodNotImplementedException(); } } diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index e45da78fc8..89880bdd1a 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -1,222 +1,223 @@ getUser(); $query = id(new PhabricatorProjectQuery()) ->setViewer($user) ->needMembers(true) ->needWatchers(true) ->needImages(true) ->needSlugs(true); $id = $request->getURIData('id'); $slug = $request->getURIData('slug'); if ($slug) { $query->withSlugs(array($slug)); } else { $query->withIDs(array($id)); } $project = $query->executeOne(); if (!$project) { return new Aphront404Response(); } if ($slug && $slug != $project->getPrimarySlug()) { return id(new AphrontRedirectResponse()) ->setURI('/tag/'.$project->getPrimarySlug().'/'); } $picture = $project->getProfileImageURI(); $header = id(new PHUIHeaderView()) ->setHeader($project->getName()) ->setUser($user) ->setPolicyObject($project) ->setImage($picture); if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ACTIVE) { $header->setStatus('fa-check', 'bluegrey', pht('Active')); } else { $header->setStatus('fa-ban', 'red', pht('Archived')); } $actions = $this->buildActionListView($project); $properties = $this->buildPropertyListView($project, $actions); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); $timeline = $this->buildTransactionTimeline( $project, new PhabricatorProjectTransactionQuery()); $timeline->setShouldTerminate(true); $nav = $this->buildIconNavView($project); $nav->selectFilter("profile/{$id}/"); $nav->appendChild($object_box); $nav->appendChild($timeline); return $this->buildApplicationPage( $nav, array( 'title' => $project->getName(), 'pageObjects' => array($project->getPHID()), )); } private function buildActionListView(PhabricatorProject $project) { $request = $this->getRequest(); $viewer = $request->getUser(); $id = $project->getID(); $view = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObject($project); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Details')) ->setIcon('fa-pencil') ->setHref($this->getApplicationURI("details/{$id}/")) - ->setDisabled(!$can_edit)); + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Picture')) ->setIcon('fa-picture-o') ->setHref($this->getApplicationURI("picture/{$id}/")) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); if ($project->isArchived()) { $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Activate Project')) ->setIcon('fa-check') ->setHref($this->getApplicationURI("archive/{$id}/")) ->setDisabled(!$can_edit) ->setWorkflow(true)); } else { $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Archive Project')) ->setIcon('fa-ban') ->setHref($this->getApplicationURI("archive/{$id}/")) ->setDisabled(!$can_edit) ->setWorkflow(true)); } $action = null; if (!$project->isUserMember($viewer->getPHID())) { $can_join = PhabricatorPolicyFilter::hasCapability( $viewer, $project, PhabricatorPolicyCapability::CAN_JOIN); $action = id(new PhabricatorActionView()) ->setUser($viewer) ->setRenderAsForm(true) ->setHref('/project/update/'.$project->getID().'/join/') ->setIcon('fa-plus') ->setDisabled(!$can_join) ->setName(pht('Join Project')); $view->addAction($action); } else { $action = id(new PhabricatorActionView()) ->setWorkflow(true) ->setHref('/project/update/'.$project->getID().'/leave/') ->setIcon('fa-times') ->setName(pht('Leave Project...')); $view->addAction($action); if (!$project->isUserWatcher($viewer->getPHID())) { $action = id(new PhabricatorActionView()) ->setWorkflow(true) ->setHref('/project/watch/'.$project->getID().'/') ->setIcon('fa-eye') ->setName(pht('Watch Project')); $view->addAction($action); } else { $action = id(new PhabricatorActionView()) ->setWorkflow(true) ->setHref('/project/unwatch/'.$project->getID().'/') ->setIcon('fa-eye-slash') ->setName(pht('Unwatch Project')); $view->addAction($action); } } return $view; } private function buildPropertyListView( PhabricatorProject $project, PhabricatorActionListView $actions) { $request = $this->getRequest(); $viewer = $request->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($project) ->setActionList($actions); $hashtags = array(); foreach ($project->getSlugs() as $slug) { $hashtags[] = id(new PHUITagView()) ->setType(PHUITagView::TYPE_OBJECT) ->setName('#'.$slug->getSlug()); } $view->addProperty(pht('Hashtags'), phutil_implode_html(' ', $hashtags)); $view->addProperty( pht('Members'), $project->getMemberPHIDs() ? $viewer ->renderHandleList($project->getMemberPHIDs()) ->setAsInline(true) : phutil_tag('em', array(), pht('None'))); $view->addProperty( pht('Watchers'), $project->getWatcherPHIDs() ? $viewer ->renderHandleList($project->getWatcherPHIDs()) ->setAsInline(true) : phutil_tag('em', array(), pht('None'))); $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( $viewer, $project); $view->addProperty( pht('Looks Like'), $viewer->renderHandle($project->getPHID())->setAsTag(true)); $view->addProperty( pht('Joinable By'), $descriptions[PhabricatorPolicyCapability::CAN_JOIN]); $field_list = PhabricatorCustomField::getObjectFields( $project, PhabricatorCustomField::ROLE_VIEW); $field_list->appendFieldsToPropertyList($project, $viewer, $view); return $view; } }