diff --git a/src/applications/drydock/logtype/DrydockLogType.php b/src/applications/drydock/logtype/DrydockLogType.php index aa1f4fc4e0..7faab42dfe 100644 --- a/src/applications/drydock/logtype/DrydockLogType.php +++ b/src/applications/drydock/logtype/DrydockLogType.php @@ -1,69 +1,41 @@ <?php abstract class DrydockLogType extends Phobject { private $viewer; private $log; abstract public function getLogTypeName(); abstract public function getLogTypeIcon(array $data); abstract public function renderLog(array $data); public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; return $this; } public function getViewer() { return $this->viewer; } final public function setLog(DrydockLog $log) { $this->log = $log; return $this; } final public function getLog() { return $this->log; } final public function getLogTypeConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('LOGCONST'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'LOGCONST')); - } - - $limit = self::getLogTypeConstantByteLimit(); - if (!is_string($const) || (strlen($const) > $limit)) { - throw new Exception( - pht( - '"%s" class "%s" has an invalid "%s" property. Field constants '. - 'must be strings and no more than %s bytes in length.', - __CLASS__, - get_class($this), - 'LOGCONST', - new PhutilNumber($limit))); - } - - return $const; - } - - final private static function getLogTypeConstantByteLimit() { - return 64; + return $this->getPhobjectClassConstant('LOGCONST', 64); } final public static function getAllLogTypes() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getLogTypeConstant') ->execute(); } } diff --git a/src/applications/harbormaster/artifact/HarbormasterArtifact.php b/src/applications/harbormaster/artifact/HarbormasterArtifact.php index a0d3a86101..8d3d8dd169 100644 --- a/src/applications/harbormaster/artifact/HarbormasterArtifact.php +++ b/src/applications/harbormaster/artifact/HarbormasterArtifact.php @@ -1,88 +1,60 @@ <?php abstract class HarbormasterArtifact extends Phobject { private $buildArtifact; abstract public function getArtifactTypeName(); public function getArtifactTypeSummary() { return $this->getArtifactTypeDescription(); } abstract public function getArtifactTypeDescription(); abstract public function getArtifactParameterSpecification(); abstract public function getArtifactParameterDescriptions(); abstract public function willCreateArtifact(PhabricatorUser $actor); public function validateArtifactData(array $artifact_data) { $artifact_spec = $this->getArtifactParameterSpecification(); PhutilTypeSpec::checkMap($artifact_data, $artifact_spec); } public function renderArtifactSummary(PhabricatorUser $viewer) { return null; } public function releaseArtifact(PhabricatorUser $actor) { return; } public function getArtifactDataExample() { return null; } public function setBuildArtifact(HarbormasterBuildArtifact $build_artifact) { $this->buildArtifact = $build_artifact; return $this; } public function getBuildArtifact() { return $this->buildArtifact; } final public function getArtifactConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('ARTIFACTCONST'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'ARTIFACTCONST')); - } - - $limit = self::getArtifactConstantByteLimit(); - if (!is_string($const) || (strlen($const) > $limit)) { - throw new Exception( - pht( - '"%s" class "%s" has an invalid "%s" property. Action constants '. - 'must be strings and no more than %s bytes in length.', - __CLASS__, - get_class($this), - 'ARTIFACTCONST', - new PhutilNumber($limit))); - } - - return $const; - } - - final public static function getArtifactConstantByteLimit() { - return 32; + return $this->getPhobjectClassConstant('ARTIFACTCONST', 32); } final public static function getAllArtifactTypes() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getArtifactConstant') ->execute(); } final public static function getArtifactType($type) { return idx(self::getAllArtifactTypes(), $type); } } diff --git a/src/applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php b/src/applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php index bb439b75b4..5806861836 100644 --- a/src/applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php +++ b/src/applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php @@ -1,52 +1,40 @@ <?php abstract class HarbormasterBuildStepGroup extends Phobject { abstract public function getGroupName(); abstract public function getGroupOrder(); public function isEnabled() { return true; } public function shouldShowIfEmpty() { return true; } final public function getGroupKey() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('GROUPKEY'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'GROUPKEY')); - } - - return $const; + return $this->getPhobjectClassConstant('GROUPKEY'); } final public static function getAllGroups() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getGroupKey') ->setSortMethod('getGroupOrder') ->execute(); } final public static function getAllEnabledGroups() { $groups = self::getAllGroups(); foreach ($groups as $key => $group) { if (!$group->isEnabled()) { unset($groups[$key]); } } return $groups; } } diff --git a/src/applications/herald/action/HeraldAction.php b/src/applications/herald/action/HeraldAction.php index 02a9dfa60a..f4217cd4db 100644 --- a/src/applications/herald/action/HeraldAction.php +++ b/src/applications/herald/action/HeraldAction.php @@ -1,407 +1,379 @@ <?php abstract class HeraldAction extends Phobject { private $adapter; private $viewer; private $applyLog = array(); const STANDARD_NONE = 'standard.none'; const STANDARD_PHID_LIST = 'standard.phid.list'; const STANDARD_TEXT = 'standard.text'; const DO_STANDARD_EMPTY = 'do.standard.empty'; const DO_STANDARD_NO_EFFECT = 'do.standard.no-effect'; const DO_STANDARD_INVALID = 'do.standard.invalid'; const DO_STANDARD_UNLOADABLE = 'do.standard.unloadable'; const DO_STANDARD_PERMISSION = 'do.standard.permission'; const DO_STANDARD_INVALID_ACTION = 'do.standard.invalid-action'; const DO_STANDARD_WRONG_RULE_TYPE = 'do.standard.wrong-rule-type'; abstract public function getHeraldActionName(); abstract public function supportsObject($object); abstract public function supportsRuleType($rule_type); abstract public function applyEffect($object, HeraldEffect $effect); abstract public function renderActionDescription($value); protected function renderActionEffectDescription($type, $data) { return null; } public function getActionGroupKey() { return null; } public function getActionsForObject($object) { return array($this->getActionConstant() => $this); } protected function getDatasource() { throw new PhutilMethodNotImplementedException(); } protected function getDatasourceValueMap() { return null; } public function getHeraldActionStandardType() { throw new PhutilMethodNotImplementedException(); } public function getHeraldActionValueType() { switch ($this->getHeraldActionStandardType()) { case self::STANDARD_NONE: return new HeraldEmptyFieldValue(); case self::STANDARD_TEXT: return new HeraldTextFieldValue(); case self::STANDARD_PHID_LIST: $tokenizer = id(new HeraldTokenizerFieldValue()) ->setKey($this->getHeraldActionName()) ->setDatasource($this->getDatasource()); $value_map = $this->getDatasourceValueMap(); if ($value_map !== null) { $tokenizer->setValueMap($value_map); } return $tokenizer; } throw new PhutilMethodNotImplementedException(); } public function willSaveActionValue($value) { try { $type = $this->getHeraldActionStandardType(); } catch (PhutilMethodNotImplementedException $ex) { return $value; } switch ($type) { case self::STANDARD_PHID_LIST: return array_keys($value); } return $value; } public function getEditorValue(PhabricatorUser $viewer, $target) { try { $type = $this->getHeraldActionStandardType(); } catch (PhutilMethodNotImplementedException $ex) { return $target; } switch ($type) { case self::STANDARD_PHID_LIST: $handles = $viewer->loadHandles($target); $handles = iterator_to_array($handles); return mpull($handles, 'getName', 'getPHID'); } return $target; } final public function setAdapter(HeraldAdapter $adapter) { $this->adapter = $adapter; return $this; } final public function getAdapter() { return $this->adapter; } final public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; return $this; } final public function getViewer() { return $this->viewer; } final public function getActionConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('ACTIONCONST'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'ACTIONCONST')); - } - - $limit = self::getActionConstantByteLimit(); - if (!is_string($const) || (strlen($const) > $limit)) { - throw new Exception( - pht( - '"%s" class "%s" has an invalid "%s" property. Action constants '. - 'must be strings and no more than %s bytes in length.', - __CLASS__, - get_class($this), - 'ACTIONCONST', - new PhutilNumber($limit))); - } - - return $const; - } - - final public static function getActionConstantByteLimit() { - return 64; + return $this->getPhobjectClassConstant('ACTIONCONST', 64); } final public static function getAllActions() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getActionConstant') ->execute(); } protected function logEffect($type, $data = null) { if (!is_string($type)) { throw new Exception( pht( 'Effect type passed to "%s" must be a scalar string.', 'logEffect()')); } $this->applyLog[] = array( 'type' => $type, 'data' => $data, ); return $this; } final public function getApplyTranscript(HeraldEffect $effect) { $context = $this->applyLog; $this->applyLog = array(); return new HeraldApplyTranscript($effect, true, $context); } protected function getActionEffectMap() { throw new PhutilMethodNotImplementedException(); } private function getActionEffectSpec($type) { $map = $this->getActionEffectMap() + $this->getStandardEffectMap(); return idx($map, $type, array()); } final public function renderActionEffectIcon($type, $data) { $map = $this->getActionEffectSpec($type); return idx($map, 'icon'); } final public function renderActionEffectColor($type, $data) { $map = $this->getActionEffectSpec($type); return idx($map, 'color'); } final public function renderActionEffectName($type, $data) { $map = $this->getActionEffectSpec($type); return idx($map, 'name'); } protected function renderHandleList($phids) { if (!is_array($phids)) { return pht('(Invalid List)'); } return $this->getViewer() ->renderHandleList($phids) ->setAsInline(true) ->render(); } protected function loadStandardTargets( array $phids, array $allowed_types, array $current_value) { $phids = array_fuse($phids); if (!$phids) { $this->logEffect(self::DO_STANDARD_EMPTY); } $current_value = array_fuse($current_value); $no_effect = array(); foreach ($phids as $phid) { if (isset($current_value[$phid])) { $no_effect[] = $phid; unset($phids[$phid]); } } if ($no_effect) { $this->logEffect(self::DO_STANDARD_NO_EFFECT, $no_effect); } if (!$phids) { return; } $allowed_types = array_fuse($allowed_types); $invalid = array(); foreach ($phids as $phid) { $type = phid_get_type($phid); if ($type == PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) { $invalid[] = $phid; unset($phids[$phid]); continue; } if ($allowed_types && empty($allowed_types[$type])) { $invalid[] = $phid; unset($phids[$phid]); continue; } } if ($invalid) { $this->logEffect(self::DO_STANDARD_INVALID, $invalid); } if (!$phids) { return; } $targets = id(new PhabricatorObjectQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs($phids) ->execute(); $targets = mpull($targets, null, 'getPHID'); $unloadable = array(); foreach ($phids as $phid) { if (empty($targets[$phid])) { $unloadable[] = $phid; unset($phids[$phid]); } } if ($unloadable) { $this->logEffect(self::DO_STANDARD_UNLOADABLE, $unloadable); } if (!$phids) { return; } $adapter = $this->getAdapter(); $object = $adapter->getObject(); if ($object instanceof PhabricatorPolicyInterface) { $no_permission = array(); foreach ($targets as $phid => $target) { if (!($target instanceof PhabricatorUser)) { continue; } $can_view = PhabricatorPolicyFilter::hasCapability( $target, $object, PhabricatorPolicyCapability::CAN_VIEW); if ($can_view) { continue; } $no_permission[] = $phid; unset($targets[$phid]); } } if ($no_permission) { $this->logEffect(self::DO_STANDARD_PERMISSION, $no_permission); } return $targets; } protected function getStandardEffectMap() { return array( self::DO_STANDARD_EMPTY => array( 'icon' => 'fa-ban', 'color' => 'grey', 'name' => pht('No Targets'), ), self::DO_STANDARD_NO_EFFECT => array( 'icon' => 'fa-circle-o', 'color' => 'grey', 'name' => pht('No Effect'), ), self::DO_STANDARD_INVALID => array( 'icon' => 'fa-ban', 'color' => 'red', 'name' => pht('Invalid Targets'), ), self::DO_STANDARD_UNLOADABLE => array( 'icon' => 'fa-ban', 'color' => 'red', 'name' => pht('Unloadable Targets'), ), self::DO_STANDARD_PERMISSION => array( 'icon' => 'fa-lock', 'color' => 'red', 'name' => pht('No Permission'), ), self::DO_STANDARD_INVALID_ACTION => array( 'icon' => 'fa-ban', 'color' => 'red', 'name' => pht('Invalid Action'), ), self::DO_STANDARD_WRONG_RULE_TYPE => array( 'icon' => 'fa-ban', 'color' => 'red', 'name' => pht('Wrong Rule Type'), ), ); } final public function renderEffectDescription($type, $data) { $result = $this->renderActionEffectDescription($type, $data); if ($result !== null) { return $result; } switch ($type) { case self::DO_STANDARD_EMPTY: return pht( 'This action specifies no targets.'); case self::DO_STANDARD_NO_EFFECT: return pht( 'This action has no effect on %s target(s): %s.', new PhutilNumber(count($data)), $this->renderHandleList($data)); case self::DO_STANDARD_INVALID: return pht( '%s target(s) are invalid or of the wrong type: %s.', new PhutilNumber(count($data)), $this->renderHandleList($data)); case self::DO_STANDARD_UNLOADABLE: return pht( '%s target(s) could not be loaded: %s.', new PhutilNumber(count($data)), $this->renderHandleList($data)); case self::DO_STANDARD_PERMISSION: return pht( '%s target(s) do not have permission to see this object: %s.', new PhutilNumber(count($data)), $this->renderHandleList($data)); case self::DO_STANDARD_INVALID_ACTION: return pht( 'No implementation is available for rule "%s".', $data); case self::DO_STANDARD_WRONG_RULE_TYPE: return pht( 'This action does not support rules of type "%s".', $data); } return null; } } diff --git a/src/applications/herald/action/HeraldActionGroup.php b/src/applications/herald/action/HeraldActionGroup.php index a087909609..ad4fecd6b6 100644 --- a/src/applications/herald/action/HeraldActionGroup.php +++ b/src/applications/herald/action/HeraldActionGroup.php @@ -1,28 +1,16 @@ <?php abstract class HeraldActionGroup extends HeraldGroup { final public function getGroupKey() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('ACTIONGROUPKEY'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'ACTIONGROUPKEY')); - } - - return $const; + return $this->getPhobjectClassConstant('ACTIONGROUPKEY'); } final public static function getAllActionGroups() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getGroupKey') ->setSortMethod('getSortKey') ->execute(); } } diff --git a/src/applications/herald/field/HeraldField.php b/src/applications/herald/field/HeraldField.php index a6d2e580c2..2aba443077 100644 --- a/src/applications/herald/field/HeraldField.php +++ b/src/applications/herald/field/HeraldField.php @@ -1,210 +1,188 @@ <?php abstract class HeraldField extends Phobject { private $adapter; const STANDARD_BOOL = 'standard.bool'; const STANDARD_TEXT = 'standard.text'; const STANDARD_TEXT_LIST = 'standard.text.list'; const STANDARD_TEXT_MAP = 'standard.text.map'; const STANDARD_PHID = 'standard.phid'; const STANDARD_PHID_LIST = 'standard.phid.list'; const STANDARD_PHID_BOOL = 'standard.phid.bool'; const STANDARD_PHID_NULLABLE = 'standard.phid.nullable'; abstract public function getHeraldFieldName(); abstract public function getHeraldFieldValue($object); public function getFieldGroupKey() { return null; } protected function getHeraldFieldStandardType() { throw new PhutilMethodNotImplementedException(); } protected function getDatasource() { throw new PhutilMethodNotImplementedException(); } protected function getDatasourceValueMap() { return null; } public function getHeraldFieldConditions() { $standard_type = $this->getHeraldFieldStandardType(); switch ($standard_type) { case self::STANDARD_BOOL: return array( HeraldAdapter::CONDITION_IS_TRUE, HeraldAdapter::CONDITION_IS_FALSE, ); case self::STANDARD_TEXT: return array( HeraldAdapter::CONDITION_CONTAINS, HeraldAdapter::CONDITION_NOT_CONTAINS, HeraldAdapter::CONDITION_IS, HeraldAdapter::CONDITION_IS_NOT, HeraldAdapter::CONDITION_REGEXP, ); case self::STANDARD_PHID: return array( HeraldAdapter::CONDITION_IS_ANY, HeraldAdapter::CONDITION_IS_NOT_ANY, ); case self::STANDARD_PHID_LIST: return array( HeraldAdapter::CONDITION_INCLUDE_ALL, HeraldAdapter::CONDITION_INCLUDE_ANY, HeraldAdapter::CONDITION_INCLUDE_NONE, HeraldAdapter::CONDITION_EXISTS, HeraldAdapter::CONDITION_NOT_EXISTS, ); case self::STANDARD_PHID_BOOL: return array( HeraldAdapter::CONDITION_EXISTS, HeraldAdapter::CONDITION_NOT_EXISTS, ); case self::STANDARD_PHID_NULLABLE: return array( HeraldAdapter::CONDITION_IS_ANY, HeraldAdapter::CONDITION_IS_NOT_ANY, HeraldAdapter::CONDITION_EXISTS, HeraldAdapter::CONDITION_NOT_EXISTS, ); case self::STANDARD_TEXT_LIST: return array( HeraldAdapter::CONDITION_CONTAINS, HeraldAdapter::CONDITION_REGEXP, ); case self::STANDARD_TEXT_MAP: return array( HeraldAdapter::CONDITION_CONTAINS, HeraldAdapter::CONDITION_REGEXP, HeraldAdapter::CONDITION_REGEXP_PAIR, ); } throw new Exception( pht( 'Herald field "%s" has unknown standard type "%s".', get_class($this), $standard_type)); } public function getHeraldFieldValueType($condition) { $standard_type = $this->getHeraldFieldStandardType(); switch ($standard_type) { case self::STANDARD_BOOL: case self::STANDARD_PHID_BOOL: return new HeraldEmptyFieldValue(); case self::STANDARD_TEXT: case self::STANDARD_TEXT_LIST: case self::STANDARD_TEXT_MAP: return new HeraldTextFieldValue(); case self::STANDARD_PHID: case self::STANDARD_PHID_NULLABLE: case self::STANDARD_PHID_LIST: switch ($condition) { case HeraldAdapter::CONDITION_EXISTS: case HeraldAdapter::CONDITION_NOT_EXISTS: return new HeraldEmptyFieldValue(); default: $tokenizer = id(new HeraldTokenizerFieldValue()) ->setKey($this->getHeraldFieldName()) ->setDatasource($this->getDatasource()); $value_map = $this->getDatasourceValueMap(); if ($value_map !== null) { $tokenizer->setValueMap($value_map); } return $tokenizer; } break; } throw new Exception( pht( 'Herald field "%s" has unknown standard type "%s".', get_class($this), $standard_type)); } abstract public function supportsObject($object); public function getFieldsForObject($object) { return array($this->getFieldConstant() => $this); } public function renderConditionValue( PhabricatorUser $viewer, $condition, $value) { $value_type = $this->getHeraldFieldValueType($condition); $value_type->setViewer($viewer); return $value_type->renderFieldValue($value); } public function getEditorValue( PhabricatorUser $viewer, $condition, $value) { $value_type = $this->getHeraldFieldValueType($condition); $value_type->setViewer($viewer); return $value_type->renderEditorValue($value); } final public function setAdapter(HeraldAdapter $adapter) { $this->adapter = $adapter; return $this; } final public function getAdapter() { return $this->adapter; } final public function getFieldConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('FIELDCONST'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'FIELDCONST')); - } - - $limit = self::getFieldConstantByteLimit(); - if (!is_string($const) || (strlen($const) > $limit)) { - throw new Exception( - pht( - '"%s" class "%s" has an invalid "%s" property. Field constants '. - 'must be strings and no more than %s bytes in length.', - __CLASS__, - get_class($this), - 'FIELDCONST', - new PhutilNumber($limit))); - } - - return $const; + return $this->getPhobjectClassConstant( + 'FIELDCONST', + self::getFieldConstantByteLimit()); } final public static function getFieldConstantByteLimit() { return 64; } final public static function getAllFields() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getFieldConstant') ->execute(); } } diff --git a/src/applications/herald/field/HeraldFieldGroup.php b/src/applications/herald/field/HeraldFieldGroup.php index adb7fbe372..9e13f17cb7 100644 --- a/src/applications/herald/field/HeraldFieldGroup.php +++ b/src/applications/herald/field/HeraldFieldGroup.php @@ -1,28 +1,16 @@ <?php abstract class HeraldFieldGroup extends HeraldGroup { final public function getGroupKey() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('FIELDGROUPKEY'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'FIELDGROUPKEY')); - } - - return $const; + return $this->getPhobjectClassConstant('FIELDGROUPKEY'); } final public static function getAllFieldGroups() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getGroupKey') ->setSortMethod('getSortKey') ->execute(); } } diff --git a/src/applications/phid/type/PhabricatorPHIDType.php b/src/applications/phid/type/PhabricatorPHIDType.php index c69075bdad..a8502b12f7 100644 --- a/src/applications/phid/type/PhabricatorPHIDType.php +++ b/src/applications/phid/type/PhabricatorPHIDType.php @@ -1,212 +1,202 @@ <?php abstract class PhabricatorPHIDType extends Phobject { final public function getTypeConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('TYPECONST'); - if ($const === false) { - throw new Exception( - pht( - '%s class "%s" must define a %s property.', - __CLASS__, - get_class($this), - 'TYPECONST')); - } + $const = $this->getPhobjectClassConstant('TYPECONST'); if (!is_string($const) || !preg_match('/^[A-Z]{4}$/', $const)) { throw new Exception( pht( '%s class "%s" has an invalid %s property. PHID '. 'constants must be a four character uppercase string.', __CLASS__, get_class($this), 'TYPECONST')); } return $const; } abstract public function getTypeName(); public function newObject() { return null; } public function getTypeIcon() { // Default to the application icon if the type doesn't specify one. $application_class = $this->getPHIDTypeApplicationClass(); if ($application_class) { $application = newv($application_class, array()); return $application->getFontIcon(); } return null; } /** * Get the class name for the application this type belongs to. * * @return string|null Class name of the corresponding application, or null * if the type is not bound to an application. */ public function getPHIDTypeApplicationClass() { // TODO: Some day this should probably be abstract, but for now it only // affects global search and there's no real burning need to go classify // every PHID type. return null; } /** * Build a @{class:PhabricatorPolicyAwareQuery} to load objects of this type * by PHID. * * If you can not build a single query which satisfies this requirement, you * can provide a dummy implementation for this method and overload * @{method:loadObjects} instead. * * @param PhabricatorObjectQuery Query being executed. * @param list<phid> PHIDs to load. * @return PhabricatorPolicyAwareQuery Query object which loads the * specified PHIDs when executed. */ abstract protected function buildQueryForObjects( PhabricatorObjectQuery $query, array $phids); /** * Load objects of this type, by PHID. For most PHID types, it is only * necessary to implement @{method:buildQueryForObjects} to get object * loading to work. * * @param PhabricatorObjectQuery Query being executed. * @param list<phid> PHIDs to load. * @return list<wild> Corresponding objects. */ public function loadObjects( PhabricatorObjectQuery $query, array $phids) { $object_query = $this->buildQueryForObjects($query, $phids) ->setViewer($query->getViewer()) ->setParentQuery($query); // If the user doesn't have permission to use the application at all, // just mark all the PHIDs as filtered. This primarily makes these // objects show up as "Restricted" instead of "Unknown" when loaded as // handles, which is technically true. if (!$object_query->canViewerUseQueryApplication()) { $object_query->addPolicyFilteredPHIDs(array_fuse($phids)); return array(); } return $object_query->execute(); } /** * Populate provided handles with application-specific data, like titles and * URIs. * * NOTE: The `$handles` and `$objects` lists are guaranteed to be nonempty * and have the same keys: subclasses are expected to load information only * for handles with visible objects. * * Because of this guarantee, a safe implementation will typically look like* * * foreach ($handles as $phid => $handle) { * $object = $objects[$phid]; * * $handle->setStuff($object->getStuff()); * // ... * } * * In general, an implementation should call `setName()` and `setURI()` on * each handle at a minimum. See @{class:PhabricatorObjectHandle} for other * handle properties. * * @param PhabricatorHandleQuery Issuing query object. * @param list<PhabricatorObjectHandle> Handles to populate with data. * @param list<Object> Objects for these PHIDs loaded by * @{method:buildQueryForObjects()}. * @return void */ abstract public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects); public function canLoadNamedObject($name) { return false; } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { throw new PhutilMethodNotImplementedException(); } /** * Get all known PHID types. * * To get PHID types a given user has access to, see * @{method:getAllInstalledTypes}. * * @return dict<string, PhabricatorPHIDType> Map of type constants to types. */ final public static function getAllTypes() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getTypeConstant') ->execute(); } /** * Get all PHID types of applications installed for a given viewer. * * @param PhabricatorUser Viewing user. * @return dict<string, PhabricatorPHIDType> Map of constants to installed * types. */ public static function getAllInstalledTypes(PhabricatorUser $viewer) { $all_types = self::getAllTypes(); $installed_types = array(); $app_classes = array(); foreach ($all_types as $key => $type) { $app_class = $type->getPHIDTypeApplicationClass(); if ($app_class === null) { // If the PHID type isn't bound to an application, include it as // installed. $installed_types[$key] = $type; continue; } // Otherwise, we need to check if this application is installed before // including the PHID type. $app_classes[$app_class][$key] = $type; } if ($app_classes) { $apps = id(new PhabricatorApplicationQuery()) ->setViewer($viewer) ->withInstalled(true) ->withClasses(array_keys($app_classes)) ->execute(); foreach ($apps as $app_class => $app) { $installed_types += $app_classes[$app_class]; } } return $installed_types; } } diff --git a/src/applications/policy/capability/PhabricatorPolicyCapability.php b/src/applications/policy/capability/PhabricatorPolicyCapability.php index b7ff6a060b..36b8ea87c0 100644 --- a/src/applications/policy/capability/PhabricatorPolicyCapability.php +++ b/src/applications/policy/capability/PhabricatorPolicyCapability.php @@ -1,88 +1,66 @@ <?php abstract class PhabricatorPolicyCapability extends Phobject { const CAN_VIEW = 'view'; const CAN_EDIT = 'edit'; const CAN_JOIN = 'join'; /** * Get the unique key identifying this capability. This key must be globally * unique. Application capabilities should be namespaced. For example: * * application.create * * @return string Globally unique capability key. */ final public function getCapabilityKey() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('CAPABILITY'); - if ($const === false) { - throw new Exception( - pht( - '%s class "%s" must define a %s property.', - __CLASS__, - get_class($this), - 'CAPABILITY')); - } - - if (!is_string($const)) { - throw new Exception( - pht( - '%s class "%s" has an invalid %s property. '. - 'Capability constants must be a string.', - __CLASS__, - get_class($this), - 'CAPABILITY')); - } - - return $const; + return $this->getPhobjectClassConstant('CAPABILITY'); } /** * Return a human-readable descriptive name for this capability, like * "Can View". * * @return string Human-readable name describing the capability. */ abstract public function getCapabilityName(); /** * Return a human-readable string describing what not having this capability * prevents the user from doing. For example: * * - You do not have permission to edit this object. * - You do not have permission to create new tasks. * * @return string Human-readable name describing what failing a check for this * capability prevents the user from doing. */ public function describeCapabilityRejection() { return null; } /** * Can this capability be set to "public"? Broadly, this is only appropriate * for view and view-related policies. * * @return bool True to allow the "public" policy. Returns false by default. */ public function shouldAllowPublicPolicySetting() { return false; } final public static function getCapabilityByKey($key) { return idx(self::getCapabilityMap(), $key); } final public static function getCapabilityMap() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getCapabilityKey') ->execute(); } } diff --git a/src/infrastructure/edges/type/PhabricatorEdgeType.php b/src/infrastructure/edges/type/PhabricatorEdgeType.php index c86af5c027..1729256050 100644 --- a/src/infrastructure/edges/type/PhabricatorEdgeType.php +++ b/src/infrastructure/edges/type/PhabricatorEdgeType.php @@ -1,219 +1,209 @@ <?php /** * Defines an edge type. * * Edges are typed, directed connections between two objects. They are used to * represent most simple relationships, like when a user is subscribed to an * object or an object is a member of a project. * * @task load Loading Types */ abstract class PhabricatorEdgeType extends Phobject { final public function getEdgeConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('EDGECONST'); - if ($const === false) { - throw new Exception( - pht( - '%s class "%s" must define an %s property.', - __CLASS__, - get_class($this), - 'EDGECONST')); - } + $const = $this->getPhobjectClassConstant('EDGECONST'); if (!is_int($const) || ($const <= 0)) { throw new Exception( pht( '%s class "%s" has an invalid %s property. '. 'Edge constants must be positive integers.', __CLASS__, get_class($this), 'EDGECONST')); } return $const; } public function getInverseEdgeConstant() { return null; } public function shouldPreventCycles() { return false; } public function shouldWriteInverseTransactions() { return false; } public function getTransactionPreviewString($actor) { return pht( '%s edited edge metadata.', $actor); } public function getTransactionAddString( $actor, $add_count, $add_edges) { return pht( '%s added %s edge(s): %s.', $actor, $add_count, $add_edges); } public function getTransactionRemoveString( $actor, $rem_count, $rem_edges) { return pht( '%s removed %s edge(s): %s.', $actor, $rem_count, $rem_edges); } public function getTransactionEditString( $actor, $total_count, $add_count, $add_edges, $rem_count, $rem_edges) { return pht( '%s edited %s edge(s), added %s: %s; removed %s: %s.', $actor, $total_count, $add_count, $add_edges, $rem_count, $rem_edges); } public function getFeedAddString( $actor, $object, $add_count, $add_edges) { return pht( '%s added %s edge(s) to %s: %s.', $actor, $add_count, $object, $add_edges); } public function getFeedRemoveString( $actor, $object, $rem_count, $rem_edges) { return pht( '%s removed %s edge(s) from %s: %s.', $actor, $rem_count, $object, $rem_edges); } public function getFeedEditString( $actor, $object, $total_count, $add_count, $add_edges, $rem_count, $rem_edges) { return pht( '%s edited %s edge(s) for %s, added %s: %s; removed %s: %s.', $actor, $total_count, $object, $add_count, $add_edges, $rem_count, $rem_edges); } /* -( Loading Types )------------------------------------------------------ */ /** * @task load */ public static function getAllTypes() { static $type_map; if ($type_map === null) { $types = id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getEdgeConstant') ->execute(); // Check that all the inverse edge definitions actually make sense. If // edge type A says B is its inverse, B must exist and say that A is its // inverse. foreach ($types as $const => $type) { $inverse = $type->getInverseEdgeConstant(); if ($inverse === null) { continue; } if (empty($types[$inverse])) { throw new Exception( pht( 'Edge type "%s" ("%d") defines an inverse type ("%d") which '. 'does not exist.', get_class($type), $const, $inverse)); } $inverse_inverse = $types[$inverse]->getInverseEdgeConstant(); if ($inverse_inverse !== $const) { throw new Exception( pht( 'Edge type "%s" ("%d") defines an inverse type ("%d"), but that '. 'inverse type defines a different type ("%d") as its '. 'inverse.', get_class($type), $const, $inverse, $inverse_inverse)); } } $type_map = $types; } return $type_map; } /** * @task load */ public static function getByConstant($const) { $type = idx(self::getAllTypes(), $const); if (!$type) { throw new Exception( pht('Unknown edge constant "%s"!', $const)); } return $type; } }