diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php --- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php +++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php @@ -155,6 +155,10 @@ $unlanded = $this->loadUnlandedDependencies($revisions); + $custom_field_lists = $this->loadCustomFields( + $revisions, + PhabricatorCustomField::ROLE_LIST); + $views = array(); if ($bucket) { $bucket->setViewer($viewer); @@ -190,6 +194,7 @@ foreach ($views as $view) { $view->setHandles($handles); $view->setUnlandedDependencies($unlanded); + $view->setCustomFieldLists($custom_field_lists); } if (count($views) == 1) { diff --git a/src/applications/differential/view/DifferentialRevisionListView.php b/src/applications/differential/view/DifferentialRevisionListView.php --- a/src/applications/differential/view/DifferentialRevisionListView.php +++ b/src/applications/differential/view/DifferentialRevisionListView.php @@ -12,6 +12,7 @@ private $noBox; private $background = null; private $unlandedDependencies = array(); + private $customFieldLists = array(); public function setUnlandedDependencies(array $unlanded_dependencies) { $this->unlandedDependencies = $unlanded_dependencies; @@ -66,6 +67,11 @@ return $this; } + public function setCustomFieldLists(array $lists) { + $this->customFieldLists = $lists; + return $this; + } + public function render() { $viewer = $this->getViewer(); @@ -153,6 +159,12 @@ $revision->getStatusIcon(), $revision->getStatusDisplayName()); + $field_list = idx($this->customFieldLists, $revision->getPHID()); + if ($field_list) { + $field_list + ->appendFieldsToListItem($revision, $this->getViewer(), $item); + } + $list->addItem($item); } diff --git a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php --- a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php +++ b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php @@ -297,14 +297,18 @@ EOTEXT )); + $custom_fields_href = PhabricatorEnv::getDoclink( + 'Configuring Custom Fields'); + return array( $this->newOption('maniphest.custom-field-definitions', 'wild', array()) ->setSummary(pht('Custom Maniphest fields.')) ->setDescription( pht( 'Array of custom fields for Maniphest tasks. For details on '. - 'adding custom fields to Maniphest, see "Configuring Custom '. - 'Fields" in the documentation.')) + 'adding custom fields to Maniphest, see **[[ %s | Configuring '. + 'Custom Fields ]]** in the documentation.', + $custom_fields_href)) ->addExample($fields_json, pht('Valid setting')), $this->newOption('maniphest.fields', $custom_field_type, $default_fields) ->setCustomData(id(new ManiphestTask())->getCustomFieldBaseClass()) diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php --- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php +++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php @@ -342,13 +342,18 @@ ManiphestBulkEditCapability::CAPABILITY); } + $custom_field_lists = $this->loadCustomFields( + $tasks, + PhabricatorCustomField::ROLE_LIST); + $list = id(new ManiphestTaskResultListView()) ->setUser($viewer) ->setTasks($tasks) ->setSavedQuery($saved) ->setCanEditPriority($can_edit_priority) ->setCanBatchEdit($can_bulk_edit) - ->setShowBatchControls($this->showBatchControls); + ->setShowBatchControls($this->showBatchControls) + ->setCustomFieldLists($custom_field_lists); $result = new PhabricatorApplicationSearchResultView(); $result->setContent($list); diff --git a/src/applications/maniphest/view/ManiphestTaskListView.php b/src/applications/maniphest/view/ManiphestTaskListView.php --- a/src/applications/maniphest/view/ManiphestTaskListView.php +++ b/src/applications/maniphest/view/ManiphestTaskListView.php @@ -7,6 +7,7 @@ private $showBatchControls; private $showSubpriorityControls; private $noDataString; + private $customFieldLists = array(); public function setTasks(array $tasks) { assert_instances_of($tasks, 'ManiphestTask'); @@ -35,6 +36,11 @@ return $this; } + public function setCustomFieldLists(array $lists) { + $this->customFieldLists = $lists; + return $this; + } + public function render() { $handles = $this->handles; @@ -64,6 +70,12 @@ ->setHeader($task->getTitle()) ->setHref('/T'.$task->getID()); + $field_list = idx($this->customFieldLists, $task->getPHID()); + if ($field_list) { + $field_list + ->appendFieldsToListItem($task, $this->getViewer(), $item); + } + if ($task->getOwnerPHID()) { $owner = $handles[$task->getOwnerPHID()]; $item->addByline(pht('Assigned: %s', $owner->renderLink())); diff --git a/src/applications/maniphest/view/ManiphestTaskResultListView.php b/src/applications/maniphest/view/ManiphestTaskResultListView.php --- a/src/applications/maniphest/view/ManiphestTaskResultListView.php +++ b/src/applications/maniphest/view/ManiphestTaskResultListView.php @@ -7,6 +7,7 @@ private $canEditPriority; private $canBatchEdit; private $showBatchControls; + private $customFieldLists = array(); public function setSavedQuery(PhabricatorSavedQuery $query) { $this->savedQuery = $query; @@ -33,6 +34,11 @@ return $this; } + public function setCustomFieldLists(array $lists) { + $this->customFieldLists = $lists; + return $this; + } + public function render() { $viewer = $this->getUser(); $tasks = $this->tasks; @@ -79,6 +85,8 @@ $task_list->setUser($viewer); $task_list->setTasks($list); $task_list->setHandles($handles); + $task_list->setCustomFieldLists($this->customFieldLists); + $header = id(new PHUIHeaderView()) ->addSigil('task-group') @@ -88,7 +96,6 @@ $lists[] = id(new PHUIObjectBoxView()) ->setHeader($header) ->setObjectList($task_list); - } if ($can_drag) { diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -13,6 +13,7 @@ * @task read Reading Utilities * @task exec Paging and Executing Queries * @task render Rendering Results + * @task custom Custom Fields */ abstract class PhabricatorApplicationSearchEngine extends Phobject { @@ -1400,4 +1401,24 @@ return array(); } + /** + * @return mapPhabricatorCustomFieldList> of loaded fields. + * @task custom + */ + protected function loadCustomFields(array $objects, $role) { + assert_instances_of($objects, 'PhabricatorCustomFieldInterface'); + $custom_fields_query = new PhabricatorCustomFieldStorageQuery(); + $custom_field_lists = array(); + + foreach ($objects as $object) { + $field_list = PhabricatorCustomField::getObjectFields($object, $role); + $field_list->readFieldsFromObject($object); + $custom_field_lists[$object->getPHID()] = $field_list; + $custom_fields_query->addFields($field_list->getFields()); + } + // This update the field_list objects. + $custom_fields_query->execute(); + + return $custom_field_lists; + } } diff --git a/src/docs/user/configuration/custom_fields.diviner b/src/docs/user/configuration/custom_fields.diviner --- a/src/docs/user/configuration/custom_fields.diviner +++ b/src/docs/user/configuration/custom_fields.diviner @@ -119,6 +119,10 @@ above the control when rendered on the edit view. - **placeholder**: A placeholder text that appears on text boxes. Only supported in text, int and remarkup fields (optional). + - **list**: If set to `icon`, `attribute` or `byline`, the value of the field + will be shown in list-view. + - **list.icon**: If `list` is set to `icon`, use this icon. + - **list.label**: When rendering value in a list, add this label. - **copy**: If true, this field's value will be copied when an object is created using another object as a template. 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 @@ -1305,6 +1305,15 @@ throw new PhabricatorCustomFieldImplementationIncompleteException($this); } + /** + * @task list + */ + public function getRequiredHandlePHIDsForListView() { + if ($this->proxy) { + return $this->proxy->getRequiredHandlePHIDsForListView(); + } + return array(); + } /* -( Global Search )------------------------------------------------------ */ 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 @@ -200,6 +200,17 @@ } } + public function appendFieldsToListItem( + PhabricatorCustomFieldInterface $object, + PhabricatorUser $viewer, + PHUIObjectItemView $view) { + + foreach ($this->fields as $field) { + $field->setViewer($viewer); + $field->renderOnListItem($view); + } + } + public function buildFieldTransactionsFromRequest( PhabricatorApplicationTransaction $template, AphrontRequest $request) { 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 @@ -286,13 +286,86 @@ return $this->getFieldConfigValue('view', true); } - public function renderPropertyViewValue(array $handles) { + public function shouldAppearInListView() { + return $this->getFieldConfigValue('list', false); + } + + protected function renderValue() { if (!strlen($this->getFieldValue())) { return null; } return $this->getFieldValue(); } + public function renderPropertyViewValue(array $handles) { + return $this->renderValue($handles); + } + + public function getStyleForListItemView() { + return $this->getFieldConfigValue('list'); + } + + public function renderListItemValue() { + return $this->renderValue(); + } + + private function isValue($something) { + // Sometimes renderValue() retuns a PHUISomething, strlen() of which is NULL + // but it has some value. + if (is_object($something)) { + return true; + } + return strlen($something); + } + + public function getValueForListItem() { + $style = $this->getStyleForListItemView(); + $value = $this->renderListItemValue(); + if (!$this->isValue($value) || !$style) { + return null; + } + switch ($style) { + case 'icon': + // TODO maybe expose 'list.icon.alt' for hover stuff. + return 'fa-'.$this->getFieldConfigValue('list.icon'); + case 'attribute': + case 'byline': + $label = $this->getFieldConfigValue('list.label'); + if (strlen($label)) { + return pht('%s: %s', $label, $value); + } + return $value; + default: + throw new Exception( + pht( + "Unknown field list-item view style '%s'; valid styles are ". + "'%s', '%s'and '%s'.", + $style, + 'icon', + 'attribute', + 'byline')); + } + } + + public function renderOnListItem(PHUIObjectItemView $view) { + $value = $this->getValueForListItem(); + if (!$this->isValue($value)) { + return; + } + + switch ($this->getStyleForListItemView()) { + case 'icon': + $view->addIcon($value); + break; + case 'attribute': + $view->addAttribute($value); + break; + case 'byline': + $view->addByline($value); + break; + } + } + public function shouldAppearInApplicationSearch() { return $this->getFieldConfigValue('search', false); } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php @@ -24,7 +24,7 @@ $new); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!$value) { return phutil_tag('em', array(), pht('No authorized blueprints.')); 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 @@ -90,7 +90,7 @@ (bool)$this->getFieldValue()); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if ($value) { return $this->getString('view.yes', pht('Yes')); diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php @@ -53,10 +53,10 @@ return array(); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if ($value) { - return $handles[$value]->renderLink(); + return $this->getViewer()->renderHandle($value); } return null; } 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 @@ -52,7 +52,7 @@ $this->setFieldValue($value); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!$value) { return null; 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 @@ -26,7 +26,7 @@ return 'header'; } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { return $this->getFieldName(); } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php @@ -18,7 +18,7 @@ return $indexes; } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!strlen($value)) { 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 @@ -77,15 +77,14 @@ return array(); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!$value) { return null; } - $handles = mpull($handles, 'renderHovercardLink'); - $handles = phutil_implode_html(', ', $handles); - return $handles; + return $this->getViewer()->renderHandleList($value) + ->setAsInline(true); } public function getRequiredHandlePHIDsForEdit() { 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 @@ -27,7 +27,7 @@ ); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!strlen($value)) { 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 @@ -72,7 +72,7 @@ ->setOptions($this->getOptions()); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { if (!strlen($this->getFieldValue())) { return null; }