@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Move Projects to SearchFields

Summary:
Ref T8441. Ref T7715. This is mostly about getting SearchFields + CustomFields working.

(This includes a couple of SearchFields which aren't used quite yet.)

Test Plan:
- Used all search controls.
- Defined custom fields and searched for them.
- Created an old saved search which searches on custom fields on master, switched to this patch, search worked exaclty as written.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7715, T8441

Differential Revision: https://secure.phabricator.com/D13189

+290 -160
+10
src/__phutil_library_map__.php
··· 2498 2498 'PhabricatorSearchCheckboxesField' => 'applications/search/field/PhabricatorSearchCheckboxesField.php', 2499 2499 'PhabricatorSearchConfigOptions' => 'applications/search/config/PhabricatorSearchConfigOptions.php', 2500 2500 'PhabricatorSearchController' => 'applications/search/controller/PhabricatorSearchController.php', 2501 + 'PhabricatorSearchCustomFieldProxyField' => 'applications/search/field/PhabricatorSearchCustomFieldProxyField.php', 2501 2502 'PhabricatorSearchDAO' => 'applications/search/storage/PhabricatorSearchDAO.php', 2502 2503 'PhabricatorSearchDatasource' => 'applications/search/typeahead/PhabricatorSearchDatasource.php', 2504 + 'PhabricatorSearchDatasourceField' => 'applications/search/field/PhabricatorSearchDatasourceField.php', 2503 2505 'PhabricatorSearchDateField' => 'applications/search/field/PhabricatorSearchDateField.php', 2504 2506 'PhabricatorSearchDeleteController' => 'applications/search/controller/PhabricatorSearchDeleteController.php', 2505 2507 'PhabricatorSearchDocument' => 'applications/search/storage/document/PhabricatorSearchDocument.php', ··· 2518 2520 'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php', 2519 2521 'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php', 2520 2522 'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php', 2523 + 'PhabricatorSearchOwnersField' => 'applications/search/field/PhabricatorSearchOwnersField.php', 2521 2524 'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php', 2522 2525 'PhabricatorSearchProjectsField' => 'applications/search/field/PhabricatorSearchProjectsField.php', 2523 2526 'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php', 2524 2527 'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php', 2525 2528 'PhabricatorSearchSelectController' => 'applications/search/controller/PhabricatorSearchSelectController.php', 2529 + 'PhabricatorSearchSelectField' => 'applications/search/field/PhabricatorSearchSelectField.php', 2526 2530 'PhabricatorSearchSpacesField' => 'applications/search/field/PhabricatorSearchSpacesField.php', 2527 2531 'PhabricatorSearchStringListField' => 'applications/search/field/PhabricatorSearchStringListField.php', 2532 + 'PhabricatorSearchTextField' => 'applications/search/field/PhabricatorSearchTextField.php', 2528 2533 'PhabricatorSearchThreeStateField' => 'applications/search/field/PhabricatorSearchThreeStateField.php', 2529 2534 'PhabricatorSearchTokenizerField' => 'applications/search/field/PhabricatorSearchTokenizerField.php', 2530 2535 'PhabricatorSearchUsersField' => 'applications/search/field/PhabricatorSearchUsersField.php', ··· 5998 6003 'PhabricatorSearchCheckboxesField' => 'PhabricatorSearchField', 5999 6004 'PhabricatorSearchConfigOptions' => 'PhabricatorApplicationConfigOptions', 6000 6005 'PhabricatorSearchController' => 'PhabricatorSearchBaseController', 6006 + 'PhabricatorSearchCustomFieldProxyField' => 'PhabricatorSearchField', 6001 6007 'PhabricatorSearchDAO' => 'PhabricatorLiskDAO', 6002 6008 'PhabricatorSearchDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 6009 + 'PhabricatorSearchDatasourceField' => 'PhabricatorSearchTokenizerField', 6003 6010 'PhabricatorSearchDateField' => 'PhabricatorSearchField', 6004 6011 'PhabricatorSearchDeleteController' => 'PhabricatorSearchBaseController', 6005 6012 'PhabricatorSearchDocument' => 'PhabricatorSearchDAO', ··· 6016 6023 'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow', 6017 6024 'PhabricatorSearchManagementWorkflow' => 'PhabricatorManagementWorkflow', 6018 6025 'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController', 6026 + 'PhabricatorSearchOwnersField' => 'PhabricatorSearchTokenizerField', 6019 6027 'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 6020 6028 'PhabricatorSearchProjectsField' => 'PhabricatorSearchTokenizerField', 6021 6029 'PhabricatorSearchResultView' => 'AphrontView', 6022 6030 'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController', 6031 + 'PhabricatorSearchSelectField' => 'PhabricatorSearchField', 6023 6032 'PhabricatorSearchSpacesField' => 'PhabricatorSearchTokenizerField', 6024 6033 'PhabricatorSearchStringListField' => 'PhabricatorSearchField', 6034 + 'PhabricatorSearchTextField' => 'PhabricatorSearchField', 6025 6035 'PhabricatorSearchThreeStateField' => 'PhabricatorSearchField', 6026 6036 'PhabricatorSearchTokenizerField' => 'PhabricatorSearchField', 6027 6037 'PhabricatorSearchUsersField' => 'PhabricatorSearchTokenizerField',
+68 -112
src/applications/project/query/PhabricatorProjectSearchEngine.php
··· 11 11 return 'PhabricatorProjectApplication'; 12 12 } 13 13 14 - public function getCustomFieldObject() { 14 + public function newResultObject() { 15 15 return new PhabricatorProject(); 16 16 } 17 17 18 - public function buildSavedQueryFromRequest(AphrontRequest $request) { 19 - $saved = new PhabricatorSavedQuery(); 20 - 21 - $saved->setParameter( 22 - 'memberPHIDs', 23 - $this->readUsersFromRequest($request, 'members')); 24 - 25 - $saved->setParameter('status', $request->getStr('status')); 26 - $saved->setParameter('name', $request->getStr('name')); 27 - 28 - $saved->setParameter( 29 - 'icons', 30 - $this->readListFromRequest($request, 'icons')); 31 - 32 - $saved->setParameter( 33 - 'colors', 34 - $this->readListFromRequest($request, 'colors')); 35 - 36 - $this->readCustomFieldsFromRequest($request, $saved); 37 - 38 - return $saved; 18 + protected function buildCustomSearchFields() { 19 + return array( 20 + id(new PhabricatorSearchTextField()) 21 + ->setLabel(pht('Name')) 22 + ->setKey('name'), 23 + id(new PhabricatorSearchUsersField()) 24 + ->setLabel(pht('Members')) 25 + ->setKey('memberPHIDs') 26 + ->setAliases(array('member', 'members')), 27 + id(new PhabricatorSearchSelectField()) 28 + ->setLabel(pht('Status')) 29 + ->setKey('status') 30 + ->setOptions($this->getStatusOptions()), 31 + id(new PhabricatorSearchCheckboxesField()) 32 + ->setLabel(pht('Icons')) 33 + ->setKey('icons') 34 + ->setOptions($this->getIconOptions()), 35 + id(new PhabricatorSearchCheckboxesField()) 36 + ->setLabel(pht('Colors')) 37 + ->setKey('colors') 38 + ->setOptions($this->getColorOptions()), 39 + ); 39 40 } 40 41 41 - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 42 + 43 + public function buildQueryFromParameters(array $map) { 42 44 $query = id(new PhabricatorProjectQuery()) 43 45 ->needImages(true); 44 46 45 - $member_phids = $saved->getParameter('memberPHIDs', array()); 46 - if ($member_phids && is_array($member_phids)) { 47 - $query->withMemberPHIDs($member_phids); 48 - } 49 - 50 - $status = $saved->getParameter('status'); 51 - $status = idx($this->getStatusValues(), $status); 52 - if ($status) { 53 - $query->withStatus($status); 54 - } 55 - 56 - $name = $saved->getParameter('name'); 57 - if (strlen($name)) { 58 - $tokens = PhabricatorTypeaheadDatasource::tokenizeString($name); 47 + if (strlen($map['name'])) { 48 + $tokens = PhabricatorTypeaheadDatasource::tokenizeString($map['name']); 59 49 $query->withNameTokens($tokens); 60 50 } 61 51 62 - $icons = $saved->getParameter('icons'); 63 - if ($icons) { 64 - $query->withIcons($icons); 52 + if ($map['memberPHIDs']) { 53 + $query->withMemberPHIDs($map['memberPHIDs']); 65 54 } 66 55 67 - $colors = $saved->getParameter('colors'); 68 - if ($colors) { 69 - $query->withColors($colors); 56 + if ($map['status']) { 57 + $status = idx($this->getStatusValues(), $map['status']); 58 + if ($status) { 59 + $query->withStatus($status); 60 + } 70 61 } 71 62 72 - $this->applyCustomFieldsToQuery($query, $saved); 73 - 74 - return $query; 75 - } 76 - 77 - public function buildSearchForm( 78 - AphrontFormView $form, 79 - PhabricatorSavedQuery $saved) { 80 - 81 - $member_phids = $saved->getParameter('memberPHIDs', array()); 82 - 83 - $status = $saved->getParameter('status'); 84 - $name_match = $saved->getParameter('name'); 85 - 86 - $icons = array_fuse($saved->getParameter('icons', array())); 87 - $colors = array_fuse($saved->getParameter('colors', array())); 88 - 89 - $icon_control = id(new AphrontFormCheckboxControl()) 90 - ->setLabel(pht('Icons')); 91 - foreach (PhabricatorProjectIcon::getIconMap() as $icon => $name) { 92 - $image = id(new PHUIIconView()) 93 - ->setIconFont($icon); 94 - 95 - $icon_control->addCheckbox( 96 - 'icons[]', 97 - $icon, 98 - array($image, ' ', $name), 99 - isset($icons[$icon])); 63 + if ($map['icons']) { 64 + $query->withIcons($map['icons']); 100 65 } 101 66 102 - $color_control = id(new AphrontFormCheckboxControl()) 103 - ->setLabel(pht('Colors')); 104 - foreach (PhabricatorProjectIcon::getColorMap() as $color => $name) { 105 - $tag = id(new PHUITagView()) 106 - ->setType(PHUITagView::TYPE_SHADE) 107 - ->setShade($color) 108 - ->setName($name); 109 - 110 - $color_control->addCheckbox( 111 - 'colors[]', 112 - $color, 113 - $tag, 114 - isset($colors[$color])); 67 + if ($map['colors']) { 68 + $query->withColors($map['colors']); 115 69 } 116 70 117 - $form 118 - ->appendChild( 119 - id(new AphrontFormTextControl()) 120 - ->setName('name') 121 - ->setLabel(pht('Name')) 122 - ->setValue($name_match)) 123 - ->appendControl( 124 - id(new AphrontFormTokenizerControl()) 125 - ->setDatasource(new PhabricatorPeopleDatasource()) 126 - ->setName('members') 127 - ->setLabel(pht('Members')) 128 - ->setValue($member_phids)) 129 - ->appendChild( 130 - id(new AphrontFormSelectControl()) 131 - ->setLabel(pht('Status')) 132 - ->setName('status') 133 - ->setOptions($this->getStatusOptions()) 134 - ->setValue($status)) 135 - ->appendChild($icon_control) 136 - ->appendChild($color_control); 137 - 138 - $this->appendCustomFieldsToForm($form, $saved); 71 + return $query; 139 72 } 140 73 141 74 protected function getURI($path) { ··· 192 125 ); 193 126 } 194 127 195 - private function getColorValues() {} 128 + private function getIconOptions() { 129 + $options = array(); 130 + 131 + foreach (PhabricatorProjectIcon::getIconMap() as $icon => $name) { 132 + $options[$icon] = array( 133 + id(new PHUIIconView()) 134 + ->setIconFont($icon), 135 + ' ', 136 + $name, 137 + ); 138 + } 139 + 140 + return $options; 141 + } 142 + 143 + private function getColorOptions() { 144 + $options = array(); 196 145 197 - private function getIconValues() {} 146 + foreach (PhabricatorProjectIcon::getColorMap() as $color => $name) { 147 + $options[$color] = array( 148 + id(new PHUITagView()) 149 + ->setType(PHUITagView::TYPE_SHADE) 150 + ->setShade($color) 151 + ->setName($name), 152 + ' ', 153 + $name, 154 + ); 155 + } 198 156 199 - protected function getRequiredHandlePHIDsForResultList( 200 - array $projects, 201 - PhabricatorSavedQuery $query) { 202 - return mpull($projects, 'getPHID'); 157 + return $options; 203 158 } 204 159 205 160 protected function renderResultList( ··· 208 163 array $handles) { 209 164 assert_instances_of($projects, 'PhabricatorProject'); 210 165 $viewer = $this->requireViewer(); 166 + $handles = $viewer->loadHandles(mpull($projects, 'getPHID')); 211 167 212 168 $list = new PHUIObjectItemListView(); 213 169 $list->setUser($viewer);
+24 -8
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 127 127 } 128 128 } 129 129 130 + if ($object instanceof PhabricatorCustomFieldInterface) { 131 + $this->applyCustomFieldsToQuery($query, $saved); 132 + } 133 + 130 134 return $query; 131 135 } 132 136 ··· 190 194 ->setAliases(array('space', 'spaces')) 191 195 ->setLabel(pht('Spaces')); 192 196 } 197 + } 198 + 199 + foreach ($this->buildCustomFieldSearchFields() as $custom_field) { 200 + $fields[] = $custom_field; 193 201 } 194 202 195 203 return $fields; ··· 1094 1102 } 1095 1103 1096 1104 1097 - /** 1098 - * Add inputs to an application search form so the user can query on custom 1099 - * fields. 1100 - * 1101 - * @param AphrontFormView Form to update. 1102 - * @param PhabricatorSavedQuery Values to prefill. 1103 - * @return void 1104 - */ 1105 + protected function buildCustomFieldSearchFields() { 1106 + $list = $this->getCustomFieldList(); 1107 + if (!$list) { 1108 + return array(); 1109 + } 1110 + 1111 + $fields = array(); 1112 + foreach ($list->getFields() as $field) { 1113 + $fields[] = id(new PhabricatorSearchCustomFieldProxyField()) 1114 + ->setSearchEngine($this) 1115 + ->setCustomField($field); 1116 + } 1117 + return $fields; 1118 + } 1119 + 1120 + // TODO: Remove. 1105 1121 protected function appendCustomFieldsToForm( 1106 1122 AphrontFormView $form, 1107 1123 PhabricatorSavedQuery $saved) {
+58
src/applications/search/field/PhabricatorSearchCustomFieldProxyField.php
··· 1 + <?php 2 + 3 + final class PhabricatorSearchCustomFieldProxyField 4 + extends PhabricatorSearchField { 5 + 6 + private $searchEngine; 7 + private $customField; 8 + 9 + public function setSearchEngine(PhabricatorApplicationSearchEngine $engine) { 10 + $this->searchEngine = $engine; 11 + return $this; 12 + } 13 + 14 + public function getSearchEngine() { 15 + return $this->searchEngine; 16 + } 17 + 18 + public function setCustomField(PhabricatorCustomField $field) { 19 + $this->customField = $field; 20 + $this->setKey('custom:'.$field->getFieldIndex()); 21 + 22 + $aliases = array(); 23 + $aliases[] = $field->getFieldKey(); 24 + $this->setAliases($aliases); 25 + 26 + return $this; 27 + } 28 + 29 + public function getCustomField() { 30 + return $this->customField; 31 + } 32 + 33 + protected function getDefaultValue() { 34 + return null; 35 + } 36 + 37 + protected function getValueExistsInRequest(AphrontRequest $request, $key) { 38 + // TODO: For historical reasons, the keys we look for don't line up with 39 + // the keys that CustomFields use. Just skip the check for existence and 40 + // always read the value. It would be vaguely nice to make rendering more 41 + // consistent instead. 42 + return true; 43 + } 44 + 45 + protected function getValueFromRequest(AphrontRequest $request, $key) { 46 + return $this->getCustomField()->readApplicationSearchValueFromRequest( 47 + $this->getSearchEngine(), 48 + $request); 49 + } 50 + 51 + public function appendToForm(AphrontFormView $form) { 52 + return $this->getCustomField()->appendToApplicationSearchForm( 53 + $this->getSearchEngine(), 54 + $form, 55 + $this->getValue()); 56 + } 57 + 58 + }
+17
src/applications/search/field/PhabricatorSearchDatasourceField.php
··· 1 + <?php 2 + 3 + final class PhabricatorSearchDatasourceField 4 + extends PhabricatorSearchTokenizerField { 5 + 6 + private $datasource; 7 + 8 + protected function newDatasource() { 9 + return id(clone $this->datasource); 10 + } 11 + 12 + public function setDatasource(PhabricatorTypeaheadDatasource $datasource) { 13 + $this->datasource = $datasource; 14 + return $this; 15 + } 16 + 17 + }
+3 -1
src/applications/search/field/PhabricatorSearchField.php
··· 209 209 /* -( Rendering Controls )------------------------------------------------- */ 210 210 211 211 212 - abstract protected function newControl(); 212 + protected function newControl() { 213 + throw new PhutilMethodNotImplementedException(); 214 + } 213 215 214 216 215 217 protected function renderControl() {
+18
src/applications/search/field/PhabricatorSearchOwnersField.php
··· 1 + <?php 2 + 3 + final class PhabricatorSearchOwnersField 4 + extends PhabricatorSearchTokenizerField { 5 + 6 + protected function getDefaultValue() { 7 + return array(); 8 + } 9 + 10 + protected function getValueFromRequest(AphrontRequest $request, $key) { 11 + return $this->getUsersFromRequest($request, $key); 12 + } 13 + 14 + protected function newDatasource() { 15 + return new PhabricatorPeopleOwnerDatasource(); 16 + } 17 + 18 + }
+30
src/applications/search/field/PhabricatorSearchSelectField.php
··· 1 + <?php 2 + 3 + final class PhabricatorSearchSelectField 4 + extends PhabricatorSearchField { 5 + 6 + private $options; 7 + 8 + public function setOptions(array $options) { 9 + $this->options = $options; 10 + return $this; 11 + } 12 + 13 + public function getOptions() { 14 + return $this->options; 15 + } 16 + 17 + protected function getDefaultValue() { 18 + return null; 19 + } 20 + 21 + protected function getValueFromRequest(AphrontRequest $request, $key) { 22 + return $request->getStr($key); 23 + } 24 + 25 + protected function newControl() { 26 + return id(new AphrontFormSelectControl()) 27 + ->setOptions($this->getOptions()); 28 + } 29 + 30 + }
+18
src/applications/search/field/PhabricatorSearchTextField.php
··· 1 + <?php 2 + 3 + final class PhabricatorSearchTextField 4 + extends PhabricatorSearchField { 5 + 6 + protected function getDefaultValue() { 7 + return ''; 8 + } 9 + 10 + protected function getValueFromRequest(AphrontRequest $request, $key) { 11 + return $request->getStr($key); 12 + } 13 + 14 + protected function newControl() { 15 + return new AphrontFormTextControl(); 16 + } 17 + 18 + }
+40
src/applications/search/field/PhabricatorSearchTokenizerField.php
··· 25 25 26 26 abstract protected function newDatasource(); 27 27 28 + 29 + protected function getUsersFromRequest(AphrontRequest $request, $key) { 30 + $list = $this->getListFromRequest($request, $key); 31 + $allow_types = array(); 32 + 33 + $phids = array(); 34 + $names = array(); 35 + $allow_types = array_fuse($allow_types); 36 + $user_type = PhabricatorPeopleUserPHIDType::TYPECONST; 37 + foreach ($list as $item) { 38 + $type = phid_get_type($item); 39 + if ($type == $user_type) { 40 + $phids[] = $item; 41 + } else if (isset($allow_types[$type])) { 42 + $phids[] = $item; 43 + } else { 44 + if (PhabricatorTypeaheadDatasource::isFunctionToken($item)) { 45 + // If this is a function, pass it through unchanged; we'll evaluate 46 + // it later. 47 + $phids[] = $item; 48 + } else { 49 + $names[] = $item; 50 + } 51 + } 52 + } 53 + 54 + if ($names) { 55 + $users = id(new PhabricatorPeopleQuery()) 56 + ->setViewer($this->getViewer()) 57 + ->withUsernames($names) 58 + ->execute(); 59 + foreach ($users as $user) { 60 + $phids[] = $user->getPHID(); 61 + } 62 + $phids = array_unique($phids); 63 + } 64 + 65 + return $phids; 66 + } 67 + 28 68 }
+4 -39
src/applications/search/field/PhabricatorSearchUsersField.php
··· 7 7 return array(); 8 8 } 9 9 10 - protected function newDatasource() { 11 - return new PhabricatorPeopleUserFunctionDatasource(); 10 + protected function getValueFromRequest(AphrontRequest $request, $key) { 11 + return $this->getUsersFromRequest($request, $key); 12 12 } 13 13 14 - protected function getValueFromRequest(AphrontRequest $request, $key) { 15 - $list = $this->getListFromRequest($request, $key); 16 - $allow_types = array(); 17 - 18 - $phids = array(); 19 - $names = array(); 20 - $allow_types = array_fuse($allow_types); 21 - $user_type = PhabricatorPeopleUserPHIDType::TYPECONST; 22 - foreach ($list as $item) { 23 - $type = phid_get_type($item); 24 - if ($type == $user_type) { 25 - $phids[] = $item; 26 - } else if (isset($allow_types[$type])) { 27 - $phids[] = $item; 28 - } else { 29 - if (PhabricatorTypeaheadDatasource::isFunctionToken($item)) { 30 - // If this is a function, pass it through unchanged; we'll evaluate 31 - // it later. 32 - $phids[] = $item; 33 - } else { 34 - $names[] = $item; 35 - } 36 - } 37 - } 38 - 39 - if ($names) { 40 - $users = id(new PhabricatorPeopleQuery()) 41 - ->setViewer($this->getViewer()) 42 - ->withUsernames($names) 43 - ->execute(); 44 - foreach ($users as $user) { 45 - $phids[] = $user->getPHID(); 46 - } 47 - $phids = array_unique($phids); 48 - } 49 - 50 - return $phids; 14 + protected function newDatasource() { 15 + return new PhabricatorPeopleUserFunctionDatasource(); 51 16 } 52 17 53 18 }