Differential D14393 Diff 34774 src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php
| <?php | <?php | ||||
| /** | |||||
| * @task web Responding to Web Requests | |||||
| * @task conduit Responding to Conduit Requests | |||||
| */ | |||||
| abstract class PhabricatorApplicationEditEngine extends Phobject { | abstract class PhabricatorApplicationEditEngine extends Phobject { | ||||
| private $viewer; | private $viewer; | ||||
| private $controller; | private $controller; | ||||
| private $isCreate; | private $isCreate; | ||||
| final public function setViewer(PhabricatorUser $viewer) { | final public function setViewer(PhabricatorUser $viewer) { | ||||
| $this->viewer = $viewer; | $this->viewer = $viewer; | ||||
| Show All 31 Lines | if ($object instanceof PhabricatorPolicyInterface) { | ||||
| $map = array( | $map = array( | ||||
| PhabricatorTransactions::TYPE_VIEW_POLICY => array( | PhabricatorTransactions::TYPE_VIEW_POLICY => array( | ||||
| 'key' => 'policy.view', | 'key' => 'policy.view', | ||||
| 'aliases' => array('view'), | 'aliases' => array('view'), | ||||
| 'capability' => PhabricatorPolicyCapability::CAN_VIEW, | 'capability' => PhabricatorPolicyCapability::CAN_VIEW, | ||||
| 'label' => pht('View Policy'), | 'label' => pht('View Policy'), | ||||
| 'description' => pht('Controls who can view the object.'), | 'description' => pht('Controls who can view the object.'), | ||||
| 'edit' => 'view', | |||||
| ), | ), | ||||
| PhabricatorTransactions::TYPE_EDIT_POLICY => array( | PhabricatorTransactions::TYPE_EDIT_POLICY => array( | ||||
| 'key' => 'policy.edit', | 'key' => 'policy.edit', | ||||
| 'aliases' => array('edit'), | 'aliases' => array('edit'), | ||||
| 'capability' => PhabricatorPolicyCapability::CAN_EDIT, | 'capability' => PhabricatorPolicyCapability::CAN_EDIT, | ||||
| 'label' => pht('Edit Policy'), | 'label' => pht('Edit Policy'), | ||||
| 'description' => pht('Controls who can edit the object.'), | 'description' => pht('Controls who can edit the object.'), | ||||
| 'edit' => 'edit', | |||||
| ), | ), | ||||
| PhabricatorTransactions::TYPE_JOIN_POLICY => array( | PhabricatorTransactions::TYPE_JOIN_POLICY => array( | ||||
| 'key' => 'policy.join', | 'key' => 'policy.join', | ||||
| 'aliases' => array('join'), | 'aliases' => array('join'), | ||||
| 'capability' => PhabricatorPolicyCapability::CAN_JOIN, | 'capability' => PhabricatorPolicyCapability::CAN_JOIN, | ||||
| 'label' => pht('Join Policy'), | 'label' => pht('Join Policy'), | ||||
| 'description' => pht('Controls who can join the object.'), | 'description' => pht('Controls who can join the object.'), | ||||
| 'edit' => 'join', | |||||
| ), | ), | ||||
| ); | ); | ||||
| foreach ($map as $type => $spec) { | foreach ($map as $type => $spec) { | ||||
| if (empty($types[$type])) { | if (empty($types[$type])) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| $capability = $spec['capability']; | $capability = $spec['capability']; | ||||
| $key = $spec['key']; | $key = $spec['key']; | ||||
| $aliases = $spec['aliases']; | $aliases = $spec['aliases']; | ||||
| $label = $spec['label']; | $label = $spec['label']; | ||||
| $description = $spec['description']; | $description = $spec['description']; | ||||
| $edit = $spec['edit']; | |||||
| $policy_field = id(new PhabricatorPolicyEditField()) | $policy_field = id(new PhabricatorPolicyEditField()) | ||||
| ->setKey($key) | ->setKey($key) | ||||
| ->setLabel($label) | ->setLabel($label) | ||||
| ->setDescription($description) | ->setDescription($description) | ||||
| ->setAliases($aliases) | ->setAliases($aliases) | ||||
| ->setCapability($capability) | ->setCapability($capability) | ||||
| ->setPolicies($policies) | ->setPolicies($policies) | ||||
| ->setTransactionType($type) | ->setTransactionType($type) | ||||
| ->setEditTypeKey($edit) | |||||
| ->setValue($object->getPolicy($capability)); | ->setValue($object->getPolicy($capability)); | ||||
| $fields[] = $policy_field; | $fields[] = $policy_field; | ||||
| if ($object instanceof PhabricatorSpacesInterface) { | if ($object instanceof PhabricatorSpacesInterface) { | ||||
| if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { | if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { | ||||
| $type_space = PhabricatorTransactions::TYPE_SPACE; | $type_space = PhabricatorTransactions::TYPE_SPACE; | ||||
| if (isset($types[$type_space])) { | if (isset($types[$type_space])) { | ||||
| $space_field = id(new PhabricatorSpaceEditField()) | $space_field = id(new PhabricatorSpaceEditField()) | ||||
| ->setKey('spacePHID') | ->setKey('spacePHID') | ||||
| ->setLabel(pht('Space')) | ->setLabel(pht('Space')) | ||||
| ->setEditTypeKey('space') | |||||
| ->setDescription( | ->setDescription( | ||||
| pht('Shifts the object in the Spaces application.')) | pht('Shifts the object in the Spaces application.')) | ||||
| ->setAliases(array('space', 'policy.space')) | ->setAliases(array('space', 'policy.space')) | ||||
| ->setTransactionType($type_space) | ->setTransactionType($type_space) | ||||
| ->setValue($object->getSpacePHID()); | ->setValue($object->getSpacePHID()); | ||||
| $fields[] = $space_field; | $fields[] = $space_field; | ||||
| $policy_field->setSpaceField($space_field); | $policy_field->setSpaceField($space_field); | ||||
| Show All 17 Lines | if ($object instanceof PhabricatorProjectInterface) { | ||||
| $project_phids = array_reverse($project_phids); | $project_phids = array_reverse($project_phids); | ||||
| } else { | } else { | ||||
| $project_phids = array(); | $project_phids = array(); | ||||
| } | } | ||||
| $edge_field = id(new PhabricatorDatasourceEditField()) | $edge_field = id(new PhabricatorDatasourceEditField()) | ||||
| ->setKey('projectPHIDs') | ->setKey('projectPHIDs') | ||||
| ->setLabel(pht('Projects')) | ->setLabel(pht('Projects')) | ||||
| ->setEditTypeKey('projects') | |||||
| ->setDescription( | ->setDescription( | ||||
| pht( | pht( | ||||
| 'Add or remove associated projects.')) | 'Add or remove associated projects.')) | ||||
| ->setDatasource(new PhabricatorProjectDatasource()) | ->setDatasource(new PhabricatorProjectDatasource()) | ||||
| ->setAliases(array('project', 'projects')) | ->setAliases(array('project', 'projects')) | ||||
| ->setTransactionType($edge_type) | ->setTransactionType($edge_type) | ||||
| ->setMetadataValue('edge:type', $project_edge_type) | ->setMetadataValue('edge:type', $project_edge_type) | ||||
| ->setValue($project_phids); | ->setValue($project_phids); | ||||
| Show All 12 Lines | if ($object instanceof PhabricatorSubscribableInterface) { | ||||
| // TODO: Allow applications to provide default subscribers; Maniphest | // TODO: Allow applications to provide default subscribers; Maniphest | ||||
| // does this at a minimum. | // does this at a minimum. | ||||
| $sub_phids = array(); | $sub_phids = array(); | ||||
| } | } | ||||
| $subscribers_field = id(new PhabricatorDatasourceEditField()) | $subscribers_field = id(new PhabricatorDatasourceEditField()) | ||||
| ->setKey('subscriberPHIDs') | ->setKey('subscriberPHIDs') | ||||
| ->setLabel(pht('Subscribers')) | ->setLabel(pht('Subscribers')) | ||||
| ->setEditTypeKey('subscribers') | |||||
| ->setDescription(pht('Manage subscribers.')) | ->setDescription(pht('Manage subscribers.')) | ||||
| ->setDatasource(new PhabricatorMetaMTAMailableDatasource()) | ->setDatasource(new PhabricatorMetaMTAMailableDatasource()) | ||||
| ->setAliases(array('subscriber', 'subscribers')) | ->setAliases(array('subscriber', 'subscribers')) | ||||
| ->setTransactionType($subscribers_type) | ->setTransactionType($subscribers_type) | ||||
| ->setValue($sub_phids); | ->setValue($sub_phids); | ||||
| $fields[] = $subscribers_field; | $fields[] = $subscribers_field; | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | if ($id) { | ||||
| array( | array( | ||||
| PhabricatorPolicyCapability::CAN_VIEW, | PhabricatorPolicyCapability::CAN_VIEW, | ||||
| PhabricatorPolicyCapability::CAN_EDIT, | PhabricatorPolicyCapability::CAN_EDIT, | ||||
| )) | )) | ||||
| ->executeOne(); | ->executeOne(); | ||||
| if (!$object) { | if (!$object) { | ||||
| return new Aphront404Response(); | return new Aphront404Response(); | ||||
| } | } | ||||
| $this->setIsCreate(false); | $this->setIsCreate(false); | ||||
| } else { | } else { | ||||
| $object = $this->newEditableObject(); | $object = $this->newEditableObject(); | ||||
| $this->setIsCreate(true); | $this->setIsCreate(true); | ||||
| } | } | ||||
| $fields = $this->buildEditFields($object); | $fields = $this->buildEditFields($object); | ||||
| foreach ($fields as $field) { | foreach ($fields as $field) { | ||||
| $field | $field | ||||
| ->setViewer($viewer) | ->setViewer($viewer) | ||||
| ▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Lines | private function buildEditFormActions($object) { | ||||
| $actions[] = id(new PhabricatorActionView()) | $actions[] = id(new PhabricatorActionView()) | ||||
| ->setName(pht('Show HTTP Parameters')) | ->setName(pht('Show HTTP Parameters')) | ||||
| ->setIcon('fa-crosshairs') | ->setIcon('fa-crosshairs') | ||||
| ->setHref($this->getEditURI($object, 'parameters/')); | ->setHref($this->getEditURI($object, 'parameters/')); | ||||
| return $actions; | return $actions; | ||||
| } | } | ||||
| /* -( Conduit )------------------------------------------------------------ */ | |||||
| /** | |||||
| * Respond to a Conduit edit request. | |||||
| * | |||||
| * This method accepts a list of transactions to apply to an object, and | |||||
| * either edits an existing object or creates a new one. | |||||
| * | |||||
| * @task conduit | |||||
| */ | |||||
| final public function buildConduitResponse(ConduitAPIRequest $request) { | |||||
| $viewer = $this->getViewer(); | |||||
| $phid = $request->getValue('objectPHID'); | |||||
| if ($phid) { | |||||
| $object = $this->newObjectQuery() | |||||
| ->setViewer($viewer) | |||||
| ->withPHIDs(array($phid)) | |||||
| ->requireCapabilities( | |||||
| array( | |||||
| PhabricatorPolicyCapability::CAN_VIEW, | |||||
| PhabricatorPolicyCapability::CAN_EDIT, | |||||
| )) | |||||
| ->executeOne(); | |||||
| if (!$object) { | |||||
| throw new Exception(pht('No such object with PHID "%s".', $phid)); | |||||
| } | |||||
| $this->setIsCreate(false); | |||||
| } else { | |||||
| $object = $this->newEditableObject(); | |||||
| $this->setIsCreate(true); | |||||
| } | |||||
| $fields = $this->buildEditFields($object); | |||||
| foreach ($fields as $field) { | |||||
| $field | |||||
| ->setViewer($viewer) | |||||
| ->setObject($object); | |||||
| } | |||||
| $types = $this->getAllEditTypesFromFields($fields); | |||||
| $template = $object->getApplicationTransactionTemplate(); | |||||
| $xactions = $this->getConduitTransactions($request, $types, $template); | |||||
| $editor = $object->getApplicationTransactionEditor() | |||||
| ->setActor($viewer) | |||||
| ->setContentSourceFromConduitRequest($request) | |||||
| ->setContinueOnNoEffect(true); | |||||
| $xactions = $editor->applyTransactions($object, $xactions); | |||||
| $xactions_struct = array(); | |||||
| foreach ($xactions as $xaction) { | |||||
| $xactions_struct[] = array( | |||||
| 'phid' => $xaction->getPHID(), | |||||
| ); | |||||
| } | |||||
| return array( | |||||
| 'object' => array( | |||||
| 'id' => $object->getID(), | |||||
| 'phid' => $object->getPHID(), | |||||
| ), | |||||
| 'transactions' => $xactions_struct, | |||||
| ); | |||||
| } | |||||
| /** | |||||
| * Generate transactions which can be applied from edit actions in a Conduit | |||||
| * request. | |||||
| * | |||||
| * @param ConduitAPIRequest The request. | |||||
| * @param list<PhabricatorEditType> Supported edit types. | |||||
| * @param PhabricatorApplicationTransaction Template transaction. | |||||
| * @return list<PhabricatorApplicationTransaction> Generated transactions. | |||||
| * @task conduit | |||||
| */ | |||||
| private function getConduitTransactions( | |||||
| ConduitAPIRequest $request, | |||||
| array $types, | |||||
| PhabricatorApplicationTransaction $template) { | |||||
| $transactions_key = 'transactions'; | |||||
| $xactions = $request->getValue($transactions_key); | |||||
| if (!is_array($xactions)) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Parameter "%s" is not a list of transactions.', | |||||
| $transactions_key)); | |||||
| } | |||||
| foreach ($xactions as $key => $xaction) { | |||||
| if (!is_array($xaction)) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Parameter "%s" must contain a list of transaction descriptions, '. | |||||
| 'but item with key "%s" is not a dictionary.', | |||||
| $transactions_key, | |||||
| $key)); | |||||
| } | |||||
| if (!array_key_exists('type', $xaction)) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Parameter "%s" must contain a list of transaction descriptions, '. | |||||
| 'but item with key "%s" is missing a "type" field. Each '. | |||||
| 'transaction must have a type field.', | |||||
| $transactions_key, | |||||
| $key)); | |||||
| } | |||||
| $type = $xaction['type']; | |||||
| if (empty($types[$type])) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Transaction with key "%s" has invalid type "%s". This type is '. | |||||
| 'not recognized. Valid types are: %s.', | |||||
| $key, | |||||
| $type, | |||||
| implode(', ', array_keys($types)))); | |||||
| } | |||||
| } | |||||
| $results = array(); | |||||
| foreach ($xactions as $xaction) { | |||||
| $type = $types[$xaction['type']]; | |||||
| $results[] = $type->generateTransaction( | |||||
| clone $template, | |||||
| $xaction); | |||||
| } | |||||
| return $results; | |||||
| } | |||||
| /** | |||||
| * @return map<string, PhabricatorEditType> | |||||
| * @task conduit | |||||
| */ | |||||
| private function getAllEditTypesFromFields(array $fields) { | |||||
| $types = array(); | |||||
| foreach ($fields as $field) { | |||||
| $field_types = $field->getEditTransactionTypes(); | |||||
| foreach ($field_types as $field_type) { | |||||
| $field_type->setField($field); | |||||
| $types[$field_type->getEditType()] = $field_type; | |||||
| } | |||||
| } | |||||
| return $types; | |||||
| } | |||||
| public function getAllEditTypes() { | |||||
| $object = $this->newEditableObject(); | |||||
| $fields = $this->buildEditFields($object); | |||||
| return $this->getAllEditTypesFromFields($fields); | |||||
| } | |||||
| } | } | ||||