diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -431,6 +431,7 @@ 'DifferentialReleephRequestFieldSpecification' => 'applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php', 'DifferentialRemarkupRule' => 'applications/differential/remarkup/DifferentialRemarkupRule.php', 'DifferentialReplyHandler' => 'applications/differential/mail/DifferentialReplyHandler.php', + 'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', 'DifferentialRepositoryFieldSpecification' => 'applications/differential/field/specification/DifferentialRepositoryFieldSpecification.php', 'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php', 'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php', @@ -2967,6 +2968,7 @@ 'DifferentialReleephRequestFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialRemarkupRule' => 'PhabricatorRemarkupRuleObject', 'DifferentialReplyHandler' => 'PhabricatorMailReplyHandler', + 'DifferentialRepositoryField' => 'DifferentialCoreCustomField', 'DifferentialRepositoryFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialRepositoryLookup' => 'Phobject', 'DifferentialResultsTableView' => 'AphrontView', diff --git a/src/applications/differential/customfield/DifferentialRepositoryField.php b/src/applications/differential/customfield/DifferentialRepositoryField.php new file mode 100644 --- /dev/null +++ b/src/applications/differential/customfield/DifferentialRepositoryField.php @@ -0,0 +1,128 @@ +getRepositoryPHID(); + } + + protected function writeValueToRevision( + DifferentialRevision $revision, + $value) { + $revision->setRepositoryPHID($value); + } + + public function readValueFromRequest(AphrontRequest $request) { + $phids = $request->getArr($this->getFieldKey()); + $first = head($phids); + $this->setValue(coalesce($first, null)); + } + + public function getRequiredHandlePHIDsForEdit() { + $phids = array(); + if ($this->getValue()) { + $phids[] = $this->getValue(); + } + return $phids; + } + + public function renderEditControl(array $handles) { + + if ($this->getValue()) { + $control_value = array_select_keys($handles, array($this->getValue())); + } else { + $control_value = array(); + } + + return id(new AphrontFormTokenizerControl()) + ->setName($this->getFieldKey()) + ->setDatasource('/typeahead/common/repositories/') + ->setValue($control_value) + ->setError($this->getFieldError()) + ->setLabel($this->getFieldName()) + ->setLimit(1); + } + + public function getApplicationTransactionRequiredHandlePHIDs( + PhabricatorApplicationTransaction $xaction) { + + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + $phids = array(); + if ($old) { + $phids[] = $old; + } + if ($new) { + $phids[] = $new; + } + + return $phids; + } + + public function getApplicationTransactionTitle( + PhabricatorApplicationTransaction $xaction) { + $author_phid = $xaction->getAuthorPHID(); + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + if ($old && $new) { + return pht( + '%s changed the repository for this revision from %s to %s.', + $xaction->renderHandleLink($author_phid), + $xaction->renderHandleLink($old), + $xaction->renderHandleLink($new)); + } else if ($new) { + return pht( + '%s set the repository for this revision to %s.', + $xaction->renderHandleLink($author_phid), + $xaction->renderHandleLink($new)); + } else { + return pht( + '%s removed %s as the repository for this revision.', + $xaction->renderHandleLink($author_phid), + $xaction->renderHandleLink($old)); + } + + } + + public function getApplicationTransactionTitleForFeed( + PhabricatorApplicationTransaction $xaction, + PhabricatorFeedStory $story) { + + $object_phid = $xaction->getObjectPHID(); + $author_phid = $xaction->getAuthorPHID(); + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + if ($old) { + return pht( + '%s updated the repository for %s from %s to %s.', + $xaction->renderHandleLink($author_phid), + $xaction->renderHandleLink($object_phid), + $xaction->renderHandleLink($old), + $xaction->renderHandleLink($new)); + } else { + return pht( + '%s set the repository for %s to %s.', + $xaction->renderHandleLink($author_phid), + $xaction->renderHandleLink($object_phid), + $xaction->renderHandleLink($new)); + } + } + +} diff --git a/src/applications/differential/customfield/DifferentialSummaryField.php b/src/applications/differential/customfield/DifferentialSummaryField.php --- a/src/applications/differential/customfield/DifferentialSummaryField.php +++ b/src/applications/differential/customfield/DifferentialSummaryField.php @@ -30,7 +30,7 @@ $this->setValue($request->getStr($this->getFieldKey())); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new PhabricatorRemarkupControl()) ->setName($this->getFieldKey()) ->setValue($this->getValue()) diff --git a/src/applications/differential/customfield/DifferentialTestPlanField.php b/src/applications/differential/customfield/DifferentialTestPlanField.php --- a/src/applications/differential/customfield/DifferentialTestPlanField.php +++ b/src/applications/differential/customfield/DifferentialTestPlanField.php @@ -44,7 +44,7 @@ $this->setValue($request->getStr($this->getFieldKey())); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new PhabricatorRemarkupControl()) ->setName($this->getFieldKey()) ->setValue($this->getValue()) diff --git a/src/applications/differential/customfield/DifferentialTitleField.php b/src/applications/differential/customfield/DifferentialTitleField.php --- a/src/applications/differential/customfield/DifferentialTitleField.php +++ b/src/applications/differential/customfield/DifferentialTitleField.php @@ -38,7 +38,7 @@ return true; } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new AphrontFormTextAreaControl()) ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT) ->setName($this->getFieldKey()) diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php --- a/src/applications/differential/storage/DifferentialRevision.php +++ b/src/applications/differential/storage/DifferentialRevision.php @@ -468,6 +468,8 @@ $fields = array( new DifferentialTitleField(), new DifferentialSummaryField(), + new DifferentialTestPlanField(), + new DifferentialRepositoryField(), ); return array_fill_keys( diff --git a/src/applications/maniphest/controller/ManiphestTaskEditController.php b/src/applications/maniphest/controller/ManiphestTaskEditController.php --- a/src/applications/maniphest/controller/ManiphestTaskEditController.php +++ b/src/applications/maniphest/controller/ManiphestTaskEditController.php @@ -157,10 +157,10 @@ $field_list = PhabricatorCustomField::getObjectFields( $task, PhabricatorCustomField::ROLE_EDIT); + $field_list->setViewer($user); foreach ($field_list->getFields() as $field) { $field->setObject($task); - $field->setViewer($user); } $field_list->readFieldsFromStorage($task); @@ -577,10 +577,7 @@ ->setDatasource('/typeahead/common/projects/')); } - foreach ($aux_fields as $aux_field) { - $aux_control = $aux_field->renderEditControl(); - $form->appendChild($aux_control); - } + $field_list->appendFieldsToForm($form); require_celerity_resource('aphront-error-view-css'); diff --git a/src/applications/people/customfield/PhabricatorUserBlurbField.php b/src/applications/people/customfield/PhabricatorUserBlurbField.php --- a/src/applications/people/customfield/PhabricatorUserBlurbField.php +++ b/src/applications/people/customfield/PhabricatorUserBlurbField.php @@ -50,7 +50,7 @@ $this->value = $request->getStr($this->getFieldKey()); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new PhabricatorRemarkupControl()) ->setName($this->getFieldKey()) ->setValue($this->value) diff --git a/src/applications/people/customfield/PhabricatorUserRealNameField.php b/src/applications/people/customfield/PhabricatorUserRealNameField.php --- a/src/applications/people/customfield/PhabricatorUserRealNameField.php +++ b/src/applications/people/customfield/PhabricatorUserRealNameField.php @@ -53,7 +53,7 @@ $this->value = $request->getStr($this->getFieldKey()); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new AphrontFormTextControl()) ->setName($this->getFieldKey()) ->setValue($this->value) diff --git a/src/applications/people/customfield/PhabricatorUserTitleField.php b/src/applications/people/customfield/PhabricatorUserTitleField.php --- a/src/applications/people/customfield/PhabricatorUserTitleField.php +++ b/src/applications/people/customfield/PhabricatorUserTitleField.php @@ -50,7 +50,7 @@ $this->value = $request->getStr($this->getFieldKey()); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new AphrontFormTextControl()) ->setName($this->getFieldKey()) ->setValue($this->value) diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php --- a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php +++ b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php @@ -96,27 +96,6 @@ } } - if ($this->needHandles) { - $phids = array(); - foreach ($xactions as $xaction) { - $phids[$xaction->getPHID()] = $xaction->getRequiredHandlePHIDs(); - } - $handles = array(); - $merged = array_mergev($phids); - if ($merged) { - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($merged) - ->execute(); - } - foreach ($xactions as $xaction) { - $xaction->setHandles( - array_select_keys( - $handles, - $phids[$xaction->getPHID()])); - } - } - return $xactions; } @@ -138,6 +117,31 @@ $xaction->attachObject($objects[$object_phid]); } + // NOTE: We have to do this after loading objects, because the objects + // may help determine which handles are required (for example, in the case + // of custom fields. + + if ($this->needHandles) { + $phids = array(); + foreach ($xactions as $xaction) { + $phids[$xaction->getPHID()] = $xaction->getRequiredHandlePHIDs(); + } + $handles = array(); + $merged = array_mergev($phids); + if ($merged) { + $handles = id(new PhabricatorHandleQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs($merged) + ->execute(); + } + foreach ($xactions as $xaction) { + $xaction->setHandles( + array_select_keys( + $handles, + $phids[$xaction->getPHID()])); + } + } + return $xactions; } diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -148,6 +148,13 @@ $phids[] = array($this->getAuthorPHID()); switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_CUSTOMFIELD: + $field = $this->getTransactionCustomField(); + if ($field) { + $phids[] = $field->getApplicationTransactionRequiredHandlePHIDs( + $this); + } + break; case PhabricatorTransactions::TYPE_SUBSCRIBERS: $phids[] = $old; $phids[] = $new; diff --git a/src/infrastructure/customfield/field/PhabricatorCustomField.php b/src/infrastructure/customfield/field/PhabricatorCustomField.php --- a/src/infrastructure/customfield/field/PhabricatorCustomField.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomField.php @@ -924,6 +924,15 @@ return null; } + public function getApplicationTransactionRequiredHandlePHIDs( + PhabricatorApplicationTransaction $xaction) { + if ($this->proxy) { + return $this->proxy->getApplicationTransactionRequiredHandlePHIDs( + $xaction); + } + return array(); + } + /* -( Edit View )---------------------------------------------------------- */ @@ -953,9 +962,20 @@ /** * @task edit */ - public function renderEditControl() { + public function getRequiredHandlePHIDsForEdit() { + if ($this->proxy) { + return $this->proxy->getRequiredHandlePHIDsForEdit(); + } + return array(); + } + + + /** + * @task edit + */ + public function renderEditControl(array $handles) { if ($this->proxy) { - return $this->proxy->renderEditControl(); + return $this->proxy->renderEditControl($handles); } throw new PhabricatorCustomFieldImplementationIncompleteException($this); } diff --git a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php --- a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php @@ -10,6 +10,7 @@ final class PhabricatorCustomFieldList extends Phobject { private $fields; + private $viewer; public function __construct(array $fields) { assert_instances_of($fields, 'PhabricatorCustomField'); @@ -21,6 +22,7 @@ } public function setViewer(PhabricatorUser $viewer) { + $this->viewer = $viewer; foreach ($this->getFields() as $field) { $field->setViewer($viewer); } @@ -75,11 +77,32 @@ } public function appendFieldsToForm(AphrontFormView $form) { + $enabled = array(); foreach ($this->fields as $field) { if ($field->shouldEnableForRole(PhabricatorCustomField::ROLE_EDIT)) { - $form->appendChild($field->renderEditControl()); + $enabled[] = $field; } } + + $phids = array(); + foreach ($enabled as $field_key => $field) { + $phids[$field_key] = $field->getRequiredHandlePHIDsForEdit(); + } + + $all_phids = array_mergev($phids); + if ($all_phids) { + $handles = id(new PhabricatorHandleQuery()) + ->setViewer($this->viewer) + ->withPHIDs($all_phids) + ->execute(); + } else { + $handles = array(); + } + + foreach ($enabled as $field_key => $field) { + $field_handles = array_select_keys($handles, $phids[$field_key]); + $form->appendChild($field->renderEditControl($field_handles)); + } } public function appendFieldsToPropertyList( diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -200,7 +200,7 @@ $this->setFieldValue($value); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new AphrontFormTextControl()) ->setName($this->getFieldKey()) ->setCaption($this->getCaption()) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php @@ -72,7 +72,7 @@ ))); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new AphrontFormCheckboxControl()) ->setLabel($this->getFieldName()) ->setCaption($this->getCaption()) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php @@ -36,7 +36,7 @@ return $this->setFieldValue($value); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return $this->newDateControl(); } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php @@ -7,7 +7,7 @@ return 'header'; } - public function renderEditControl() { + public function renderEditControl(array $handles) { $header = phutil_tag( 'div', array( diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php @@ -86,4 +86,75 @@ return $handles; } + public function getRequiredHandlePHIDsForEdit() { + $value = $this->getFieldValue(); + if ($value) { + return $value; + } else { + return array(); + } + } + + public function getApplicationTransactionRequiredHandlePHIDs( + PhabricatorApplicationTransaction $xaction) { + + $old = json_decode($xaction->getOldValue()); + if (!is_array($old)) { + $old = array(); + } + + $new = json_decode($xaction->getNewValue()); + if (!is_array($new)) { + $new = array(); + } + + $add = array_diff($new, $old); + $rem = array_diff($old, $new); + + return array_merge($add, $rem); + } + + public function getApplicationTransactionTitle( + PhabricatorApplicationTransaction $xaction) { + $author_phid = $xaction->getAuthorPHID(); + + $old = json_decode($xaction->getOldValue()); + if (!is_array($old)) { + $old = array(); + } + + $new = json_decode($xaction->getNewValue()); + if (!is_array($new)) { + $new = array(); + } + + $add = array_diff($new, $old); + $rem = array_diff($old, $new); + + if ($add && !$rem) { + return pht( + '%s updated %s, added %d: %s.', + $xaction->renderHandleLink($author_phid), + $this->getFieldName(), + new PhutilNumber(count($add)), + $xaction->renderHandleList($add)); + } else if ($rem && !$add) { + return pht( + '%s updated %s, removed %d: %s.', + $xaction->renderHandleLink($author_phid), + $this->getFieldName(), + new PhutilNumber(count($rem)), + $xaction->renderHandleList($rem)); + } else { + return pht( + '%s updated %s, added %d: %s; removed %d: %s.', + $xaction->renderHandleLink($author_phid), + $this->getFieldName(), + new PhutilNumber(count($add)), + $xaction->renderHandleList($add), + new PhutilNumber(count($rem)), + $xaction->renderHandleList($rem)); + } + } + } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php @@ -7,7 +7,7 @@ return 'remarkup'; } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new PhabricatorRemarkupControl()) ->setLabel($this->getFieldName()) ->setName($this->getFieldKey()) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php @@ -64,7 +64,7 @@ return $this->getFieldConfigValue('options', array()); } - public function renderEditControl() { + public function renderEditControl(array $handles) { return id(new AphrontFormSelectControl()) ->setLabel($this->getFieldName()) ->setCaption($this->getCaption()) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php @@ -7,17 +7,12 @@ return 'users'; } - public function renderEditControl() { - $handles = array(); + public function renderEditControl(array $handles) { $value = $this->getFieldValue(); if ($value) { - - // TODO: Surface and batch. - - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($value) - ->execute(); + $control_value = array_select_keys($handles, $value); + } else { + $control_value = array(); } $control = id(new AphrontFormTokenizerControl()) @@ -25,7 +20,7 @@ ->setName($this->getFieldKey()) ->setDatasource('/typeahead/common/accounts/') ->setCaption($this->getCaption()) - ->setValue($handles); + ->setValue($control_value); $limit = $this->getFieldConfigValue('limit'); if ($limit) { @@ -49,18 +44,4 @@ $form->appendChild($control); } - - - public function getApplicationTransactionTitle( - PhabricatorApplicationTransaction $xaction) { - $author_phid = $xaction->getAuthorPHID(); - - // TODO: Show added/removed and render handles. We don't have handle - // surfacing or batching yet so this is a bit awkward right now. - - return pht( - '%s updated %s.', - $xaction->renderHandleLink($author_phid), - $this->getFieldName()); - } }