@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.

Modularize SearchEngine extensions

Summary:
Ref T9964. ApplicationSearch currently has a bunch of hard-coded `if ($object instanceof thing)` stuff.

Pull that out so it can live in extensions.

Test Plan:
- Searched by spaces, subscribers, projects.

{F1023921}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9964

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

+246 -149
+6 -2
src/__phutil_library_map__.php
··· 2836 2836 'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php', 2837 2837 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 2838 2838 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 2839 - 'PhabricatorProjectsEditEngineExtension' => 'applications/project/editor/PhabricatorProjectsEditEngineExtension.php', 2839 + 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 2840 2840 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', 2841 2841 'PhabricatorProjectsPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsPolicyRule.php', 2842 + 'PhabricatorProjectsSearchEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineExtension.php', 2842 2843 'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php', 2843 2844 'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php', 2844 2845 'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php', ··· 3132 3133 'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsAddSubscribersHeraldAction.php', 3133 3134 'PhabricatorSubscriptionsApplication' => 'applications/subscriptions/application/PhabricatorSubscriptionsApplication.php', 3134 3135 'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php', 3135 - 'PhabricatorSubscriptionsEditEngineExtension' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditEngineExtension.php', 3136 + 'PhabricatorSubscriptionsEditEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php', 3136 3137 'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php', 3137 3138 'PhabricatorSubscriptionsHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php', 3138 3139 'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php', 3139 3140 'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSelfHeraldAction.php', 3140 3141 'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSubscribersHeraldAction.php', 3142 + 'PhabricatorSubscriptionsSearchEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsSearchEngineExtension.php', 3141 3143 'PhabricatorSubscriptionsSubscribeEmailCommand' => 'applications/subscriptions/command/PhabricatorSubscriptionsSubscribeEmailCommand.php', 3142 3144 'PhabricatorSubscriptionsSubscribersPolicyRule' => 'applications/subscriptions/policyrule/PhabricatorSubscriptionsSubscribersPolicyRule.php', 3143 3145 'PhabricatorSubscriptionsTransactionController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsTransactionController.php', ··· 7104 7106 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 7105 7107 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', 7106 7108 'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule', 7109 + 'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension', 7107 7110 'PhabricatorProtocolAdapter' => 'Phobject', 7108 7111 'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck', 7109 7112 'PhabricatorQuery' => 'Phobject', ··· 7459 7462 'PhabricatorSubscriptionsListController' => 'PhabricatorController', 7460 7463 'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction', 7461 7464 'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction', 7465 + 'PhabricatorSubscriptionsSearchEngineExtension' => 'PhabricatorSearchEngineExtension', 7462 7466 'PhabricatorSubscriptionsSubscribeEmailCommand' => 'MetaMTAEmailTransactionCommand', 7463 7467 'PhabricatorSubscriptionsSubscribersPolicyRule' => 'PhabricatorPolicyRule', 7464 7468 'PhabricatorSubscriptionsTransactionController' => 'PhabricatorController',
src/applications/project/editor/PhabricatorProjectsEditEngineExtension.php src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php
+51
src/applications/project/engineextension/PhabricatorProjectsSearchEngineExtension.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectsSearchEngineExtension 4 + extends PhabricatorSearchEngineExtension { 5 + 6 + const EXTENSIONKEY = 'projects'; 7 + 8 + public function isExtensionEnabled() { 9 + return PhabricatorApplication::isClassInstalled( 10 + 'PhabricatorProjectApplication'); 11 + } 12 + 13 + public function getExtensionName() { 14 + return pht('Support for Projects'); 15 + } 16 + 17 + public function getExtensionOrder() { 18 + return 2000; 19 + } 20 + 21 + public function supportsObject($object) { 22 + return ($object instanceof PhabricatorProjectInterface); 23 + } 24 + 25 + public function applyConstraintsToQuery( 26 + $object, 27 + $query, 28 + PhabricatorSavedQuery $saved, 29 + array $map) { 30 + 31 + if (!empty($map['projectPHIDs'])) { 32 + $query->withEdgeLogicConstraints( 33 + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 34 + $map['projectPHIDs']); 35 + } 36 + } 37 + 38 + public function getSearchFields($object) { 39 + $fields = array(); 40 + 41 + $fields[] = id(new PhabricatorProjectSearchField()) 42 + ->setKey('projectPHIDs') 43 + ->setConduitKey('projects') 44 + ->setAliases(array('project', 'projects')) 45 + ->setLabel(pht('Projects')); 46 + 47 + return $fields; 48 + } 49 + 50 + 51 + }
+29 -145
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 19 19 private $application; 20 20 private $viewer; 21 21 private $errors = array(); 22 - private $customFields = false; 23 22 private $request; 24 23 private $context; 25 24 private $controller; ··· 164 163 return $query; 165 164 } 166 165 167 - if ($object instanceof PhabricatorSubscribableInterface) { 168 - if (!empty($map['subscriberPHIDs'])) { 169 - $query->withEdgeLogicPHIDs( 170 - PhabricatorObjectHasSubscriberEdgeType::EDGECONST, 171 - PhabricatorQueryConstraint::OPERATOR_OR, 172 - $map['subscriberPHIDs']); 173 - } 174 - } 175 - 176 - if ($object instanceof PhabricatorProjectInterface) { 177 - if (!empty($map['projectPHIDs'])) { 178 - $query->withEdgeLogicConstraints( 179 - PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 180 - $map['projectPHIDs']); 181 - } 182 - } 183 - 184 - if ($object instanceof PhabricatorSpacesInterface) { 185 - if (!empty($map['spacePHIDs'])) { 186 - $query->withSpacePHIDs($map['spacePHIDs']); 187 - } else { 188 - // If the user doesn't search for objects in specific spaces, we 189 - // default to "all active spaces you have permission to view". 190 - $query->withSpaceIsArchived(false); 191 - } 192 - } 193 - 194 - if ($object instanceof PhabricatorCustomFieldInterface) { 195 - $this->applyCustomFieldsToQuery($query, $saved); 166 + $extensions = $this->getEngineExtensions(); 167 + foreach ($extensions as $extension) { 168 + $extension->applyConstraintsToQuery($object, $query, $saved, $map); 196 169 } 197 170 198 171 $order = $saved->getParameter('order'); ··· 272 245 273 246 $object = $this->newResultObject(); 274 247 if ($object) { 275 - if ($object instanceof PhabricatorSubscribableInterface) { 276 - $fields[] = id(new PhabricatorSearchSubscribersField()) 277 - ->setLabel(pht('Subscribers')) 278 - ->setKey('subscriberPHIDs') 279 - ->setAliases(array('subscriber', 'subscribers')); 280 - } 281 - 282 - if ($object instanceof PhabricatorProjectInterface) { 283 - $fields[] = id(new PhabricatorProjectSearchField()) 284 - ->setKey('projectPHIDs') 285 - ->setAliases(array('project', 'projects')) 286 - ->setLabel(pht('Projects')); 287 - } 288 - 289 - if ($object instanceof PhabricatorSpacesInterface) { 290 - if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) { 291 - $fields[] = id(new PhabricatorSpacesSearchField()) 292 - ->setKey('spacePHIDs') 293 - ->setAliases(array('space', 'spaces')) 294 - ->setLabel(pht('Spaces')); 248 + $extensions = $this->getEngineExtensions(); 249 + foreach ($extensions as $extension) { 250 + $extension_fields = $extension->getSearchFields($object); 251 + foreach ($extension_fields as $extension_field) { 252 + $fields[] = $extension_field; 295 253 } 296 254 } 297 - } 298 - 299 - foreach ($this->buildCustomFieldSearchFields() as $custom_field) { 300 - $fields[] = $custom_field; 301 255 } 302 256 303 257 $query = $this->newQuery(); ··· 1089 1043 /* -( Application Search )------------------------------------------------- */ 1090 1044 1091 1045 1092 - /** 1093 - * Retrieve an object to use to define custom fields for this search. 1094 - * 1095 - * To integrate with custom fields, subclasses should override this method 1096 - * and return an instance of the application object which implements 1097 - * @{interface:PhabricatorCustomFieldInterface}. 1098 - * 1099 - * @return PhabricatorCustomFieldInterface|null Object with custom fields. 1100 - * @task appsearch 1101 - */ 1102 - public function getCustomFieldObject() { 1103 - $object = $this->newResultObject(); 1104 - if ($object instanceof PhabricatorCustomFieldInterface) { 1105 - return $object; 1106 - } 1107 - return null; 1108 - } 1109 - 1110 - 1111 - /** 1112 - * Get the custom fields for this search. 1113 - * 1114 - * @return PhabricatorCustomFieldList|null Custom fields, if this search 1115 - * supports custom fields. 1116 - * @task appsearch 1117 - */ 1118 - public function getCustomFieldList() { 1119 - if ($this->customFields === false) { 1120 - $object = $this->getCustomFieldObject(); 1121 - if ($object) { 1122 - $fields = PhabricatorCustomField::getObjectFields( 1123 - $object, 1124 - PhabricatorCustomField::ROLE_APPLICATIONSEARCH); 1125 - $fields->setViewer($this->requireViewer()); 1126 - } else { 1127 - $fields = null; 1128 - } 1129 - $this->customFields = $fields; 1130 - } 1131 - return $this->customFields; 1132 - } 1133 - 1134 - 1135 - /** 1136 - * Applies data from a saved query to an executable query. 1137 - * 1138 - * @param PhabricatorCursorPagedPolicyAwareQuery Query to constrain. 1139 - * @param PhabricatorSavedQuery Saved query to read. 1140 - * @return void 1141 - */ 1142 - protected function applyCustomFieldsToQuery( 1143 - PhabricatorCursorPagedPolicyAwareQuery $query, 1144 - PhabricatorSavedQuery $saved) { 1145 - 1146 - $list = $this->getCustomFieldList(); 1147 - if (!$list) { 1148 - return; 1149 - } 1150 - 1151 - foreach ($list->getFields() as $field) { 1152 - $value = $field->applyApplicationSearchConstraintToQuery( 1153 - $this, 1154 - $query, 1155 - $saved->getParameter('custom:'.$field->getFieldIndex())); 1156 - } 1157 - } 1158 - 1159 - private function buildCustomFieldSearchFields() { 1160 - $list = $this->getCustomFieldList(); 1161 - if (!$list) { 1162 - return array(); 1163 - } 1164 - 1165 - $fields = array(); 1166 - foreach ($list->getFields() as $field) { 1167 - $fields[] = id(new PhabricatorSearchCustomFieldProxyField()) 1168 - ->setSearchEngine($this) 1169 - ->setCustomField($field); 1170 - } 1171 - 1172 - return $fields; 1173 - } 1174 - 1175 1046 public function getSearchFieldsForConduit() { 1176 1047 $standard_fields = $this->buildSearchFields(); 1177 1048 ··· 1302 1173 return $specifications; 1303 1174 } 1304 1175 1305 - private function getConduitFieldExtensions() { 1176 + private function getEngineExtensions() { 1306 1177 $extensions = PhabricatorSearchEngineExtension::getAllEnabledExtensions(); 1307 - $object = $this->newQuery()->newResultObject(); 1308 1178 1309 - $field_extensions = array(); 1310 1179 foreach ($extensions as $key => $extension) { 1311 - $extension->setViewer($this->requireViewer()); 1180 + $extension 1181 + ->setViewer($this->requireViewer()) 1182 + ->setSearchEngine($this); 1183 + } 1312 1184 1185 + $object = $this->newResultObject(); 1186 + foreach ($extensions as $key => $extension) { 1313 1187 if (!$extension->supportsObject($object)) { 1314 - continue; 1188 + unset($extensions[$key]); 1315 1189 } 1190 + } 1316 1191 1317 - if ($extension->getFieldSpecificationsForConduit($object)) { 1318 - $field_extensions[$key] = $extension; 1192 + return $extensions; 1193 + } 1194 + 1195 + 1196 + private function getConduitFieldExtensions() { 1197 + $extensions = $this->getEngineExtensions(); 1198 + $object = $this->newResultObject(); 1199 + 1200 + foreach ($extensions as $key => $extension) { 1201 + if (!$extension->getFieldSpecificationsForConduit($object)) { 1202 + unset($extensions[$key]); 1319 1203 } 1320 1204 } 1321 1205 1322 - return $field_extensions; 1206 + return $extensions; 1323 1207 } 1324 1208 1325 1209 private function setAutomaticConstraintsForConduit(
+28
src/applications/search/engineextension/PhabricatorSearchEngineExtension.php
··· 3 3 abstract class PhabricatorSearchEngineExtension extends Phobject { 4 4 5 5 private $viewer; 6 + private $searchEngine; 6 7 7 8 final public function getExtensionKey() { 8 9 return $this->getPhobjectClassConstant('EXTENSIONKEY'); ··· 17 18 return $this->viewer; 18 19 } 19 20 21 + final public function setSearchEngine( 22 + PhabricatorApplicationSearchEngine $engine) { 23 + $this->searchEngine = $engine; 24 + return $this; 25 + } 26 + 27 + final public function getSearchEngine() { 28 + return $this->searchEngine; 29 + } 30 + 20 31 abstract public function isExtensionEnabled(); 21 32 abstract public function getExtensionName(); 22 33 abstract public function supportsObject($object); 23 34 35 + public function getExtensionOrder() { 36 + return 5000; 37 + } 38 + 39 + public function getSearchFields($object) { 40 + return array(); 41 + } 42 + 43 + public function applyConstraintsToQuery( 44 + $object, 45 + $query, 46 + PhabricatorSavedQuery $saved, 47 + array $map) { 48 + return; 49 + } 50 + 24 51 public function getFieldSpecificationsForConduit($object) { 25 52 return array(); 26 53 } ··· 33 60 return id(new PhutilClassMapQuery()) 34 61 ->setAncestorClass(__CLASS__) 35 62 ->setUniqueMethod('getExtensionKey') 63 + ->setSortMethod('getExtensionOrder') 36 64 ->execute(); 37 65 } 38 66
+3
src/applications/search/engineextension/PhabricatorSearchEngineExtensionModule.php
··· 19 19 $rows = array(); 20 20 foreach ($extensions as $extension) { 21 21 $rows[] = array( 22 + $extension->getExtensionOrder(), 22 23 $extension->getExtensionKey(), 23 24 get_class($extension), 24 25 $extension->getExtensionName(), ··· 31 32 $table = id(new AphrontTableView($rows)) 32 33 ->setHeaders( 33 34 array( 35 + pht('Order'), 34 36 pht('Key'), 35 37 pht('Class'), 36 38 pht('Name'), ··· 38 40 )) 39 41 ->setColumnClasses( 40 42 array( 43 + null, 41 44 null, 42 45 null, 43 46 'wide pri',
+33
src/applications/spaces/engineextension/PhabricatorSpacesSearchEngineExtension.php
··· 14 14 return pht('Support for Spaces'); 15 15 } 16 16 17 + public function getExtensionOrder() { 18 + return 3000; 19 + } 20 + 17 21 public function supportsObject($object) { 18 22 return ($object instanceof PhabricatorSpacesInterface); 23 + } 24 + 25 + public function getSearchFields($object) { 26 + $fields = array(); 27 + 28 + if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) { 29 + $fields[] = id(new PhabricatorSpacesSearchField()) 30 + ->setKey('spacePHIDs') 31 + ->setConduitKey('spaces') 32 + ->setAliases(array('space', 'spaces')) 33 + ->setLabel(pht('Spaces')); 34 + } 35 + 36 + return $fields; 37 + } 38 + 39 + public function applyConstraintsToQuery( 40 + $object, 41 + $query, 42 + PhabricatorSavedQuery $saved, 43 + array $map) { 44 + 45 + if (!empty($map['spacePHIDs'])) { 46 + $query->withSpacePHIDs($map['spacePHIDs']); 47 + } else { 48 + // If the user doesn't search for objects in specific spaces, we 49 + // default to "all active spaces you have permission to view". 50 + $query->withSpaceIsArchived(false); 51 + } 19 52 } 20 53 21 54 public function getFieldSpecificationsForConduit($object) {
-2
src/applications/subscriptions/editor/PhabricatorSubscriptionsEditEngineExtension.php src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php
··· 34 34 $sub_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID( 35 35 $object_phid); 36 36 } else { 37 - // TODO: Allow applications to provide default subscribers? Maniphest 38 - // does this at a minimum. 39 37 $sub_phids = array(); 40 38 } 41 39
+52
src/applications/subscriptions/engineextension/PhabricatorSubscriptionsSearchEngineExtension.php
··· 1 + <?php 2 + 3 + final class PhabricatorSubscriptionsSearchEngineExtension 4 + extends PhabricatorSearchEngineExtension { 5 + 6 + const EXTENSIONKEY = 'subscriptions'; 7 + 8 + public function isExtensionEnabled() { 9 + return PhabricatorApplication::isClassInstalled( 10 + 'PhabricatorSubscriptionsApplication'); 11 + } 12 + 13 + public function getExtensionName() { 14 + return pht('Support for Subscriptions'); 15 + } 16 + 17 + public function getExtensionOrder() { 18 + return 1000; 19 + } 20 + 21 + public function supportsObject($object) { 22 + return ($object instanceof PhabricatorSubscribableInterface); 23 + } 24 + 25 + public function applyConstraintsToQuery( 26 + $object, 27 + $query, 28 + PhabricatorSavedQuery $saved, 29 + array $map) { 30 + 31 + if (!empty($map['subscriberPHIDs'])) { 32 + $query->withEdgeLogicPHIDs( 33 + PhabricatorObjectHasSubscriberEdgeType::EDGECONST, 34 + PhabricatorQueryConstraint::OPERATOR_OR, 35 + $map['subscriberPHIDs']); 36 + } 37 + } 38 + 39 + public function getSearchFields($object) { 40 + $fields = array(); 41 + 42 + $fields[] = id(new PhabricatorSearchSubscribersField()) 43 + ->setLabel(pht('Subscribers')) 44 + ->setKey('subscriberPHIDs') 45 + ->setConduitKey('subscribers') 46 + ->setAliases(array('subscriber', 'subscribers')); 47 + 48 + return $fields; 49 + } 50 + 51 + 52 + }
+44
src/infrastructure/customfield/engineextension/PhabricatorCustomFieldSearchEngineExtension.php
··· 17 17 return ($object instanceof PhabricatorCustomFieldInterface); 18 18 } 19 19 20 + public function getExtensionOrder() { 21 + return 9000; 22 + } 23 + 24 + public function getSearchFields($object) { 25 + $engine = $this->getSearchEngine(); 26 + $custom_fields = $this->getCustomFields($object); 27 + 28 + $fields = array(); 29 + foreach ($custom_fields as $field) { 30 + $fields[] = id(new PhabricatorSearchCustomFieldProxyField()) 31 + ->setSearchEngine($engine) 32 + ->setCustomField($field); 33 + } 34 + 35 + return $fields; 36 + } 37 + 38 + public function applyConstraintsToQuery( 39 + $object, 40 + $query, 41 + PhabricatorSavedQuery $saved, 42 + array $map) { 43 + 44 + $engine = $this->getSearchEngine(); 45 + $fields = $this->getCustomFields($object); 46 + 47 + foreach ($fields as $field) { 48 + $field->applyApplicationSearchConstraintToQuery( 49 + $engine, 50 + $query, 51 + $saved->getParameter('custom:'.$field->getFieldIndex())); 52 + } 53 + } 54 + 55 + private function getCustomFields($object) { 56 + $fields = PhabricatorCustomField::getObjectFields( 57 + $object, 58 + PhabricatorCustomField::ROLE_APPLICATIONSEARCH); 59 + $fields->setViewer($this->getViewer()); 60 + 61 + return $fields->getFields(); 62 + } 63 + 20 64 public function getFieldSpecificationsForConduit($object) { 21 65 $fields = PhabricatorCustomField::getObjectFields( 22 66 $object,