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

Make Subscribers automatically provide working SearchFields

Summary:
Ref T8441. Ref T7715. For modern Query classes, automatically make subscriber queries and SearchField integrations work.

In particular, we can just drive this query with EdgeLogic and don't need to do anything specific on these Query classes beyond making sure they're implemented in a way that picks up all of the EdgeLogic clauses.

Test Plan:
- Searched for subscribers in Pholio, Files, Paste, and Projects.
- Searched for all other fields in Projects to check that Query changes are OK.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7715, T8441

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

+103 -54
+2
src/__phutil_library_map__.php
··· 2529 2529 'PhabricatorSearchSelectField' => 'applications/search/field/PhabricatorSearchSelectField.php', 2530 2530 'PhabricatorSearchSpacesField' => 'applications/search/field/PhabricatorSearchSpacesField.php', 2531 2531 'PhabricatorSearchStringListField' => 'applications/search/field/PhabricatorSearchStringListField.php', 2532 + 'PhabricatorSearchSubscribersField' => 'applications/search/field/PhabricatorSearchSubscribersField.php', 2532 2533 'PhabricatorSearchTextField' => 'applications/search/field/PhabricatorSearchTextField.php', 2533 2534 'PhabricatorSearchThreeStateField' => 'applications/search/field/PhabricatorSearchThreeStateField.php', 2534 2535 'PhabricatorSearchTokenizerField' => 'applications/search/field/PhabricatorSearchTokenizerField.php', ··· 6031 6032 'PhabricatorSearchSelectField' => 'PhabricatorSearchField', 6032 6033 'PhabricatorSearchSpacesField' => 'PhabricatorSearchTokenizerField', 6033 6034 'PhabricatorSearchStringListField' => 'PhabricatorSearchField', 6035 + 'PhabricatorSearchSubscribersField' => 'PhabricatorSearchTokenizerField', 6034 6036 'PhabricatorSearchTextField' => 'PhabricatorSearchField', 6035 6037 'PhabricatorSearchThreeStateField' => 'PhabricatorSearchField', 6036 6038 'PhabricatorSearchTokenizerField' => 'PhabricatorSearchField',
+8
src/applications/files/query/PhabricatorFileSearchEngine.php
··· 37 37 ); 38 38 } 39 39 40 + protected function getDefaultFieldOrder() { 41 + return array( 42 + '...', 43 + 'createdStart', 44 + 'createdEnd', 45 + ); 46 + } 47 + 40 48 public function buildQueryFromParameters(array $map) { 41 49 $query = id(new PhabricatorFileQuery()); 42 50
+8
src/applications/paste/query/PhabricatorPasteSearchEngine.php
··· 56 56 ); 57 57 } 58 58 59 + protected function getDefaultFieldOrder() { 60 + return array( 61 + '...', 62 + 'createdStart', 63 + 'createdEnd', 64 + ); 65 + } 66 + 59 67 protected function getURI($path) { 60 68 return '/paste/'.$path; 61 69 }
+37 -50
src/applications/project/query/PhabricatorProjectQuery.php
··· 120 120 121 121 protected function loadPage() { 122 122 $table = new PhabricatorProject(); 123 - $conn_r = $table->establishConnection('r'); 124 - 125 - // NOTE: Because visibility checks for projects depend on whether or not 126 - // the user is a project member, we always load their membership. If we're 127 - // loading all members anyway we can piggyback on that; otherwise we 128 - // do an explicit join. 129 - 130 - $select_clause = ''; 131 - if (!$this->needMembers) { 132 - $select_clause = ', vm.dst viewerIsMember'; 133 - } 134 - 135 - $data = queryfx_all( 136 - $conn_r, 137 - 'SELECT p.* %Q FROM %T p %Q %Q %Q %Q %Q', 138 - $select_clause, 139 - $table->getTableName(), 140 - $this->buildJoinClause($conn_r), 141 - $this->buildWhereClause($conn_r), 142 - $this->buildGroupClause($conn_r), 143 - $this->buildOrderClause($conn_r), 144 - $this->buildLimitClause($conn_r)); 145 - 123 + $data = $this->loadStandardPageRows($table); 146 124 $projects = $table->loadAllFromArray($data); 147 125 148 126 if ($projects) { ··· 240 218 return $projects; 241 219 } 242 220 243 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 244 - $where = array(); 221 + protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) { 222 + $select = parent::buildSelectClauseParts($conn); 223 + 224 + // NOTE: Because visibility checks for projects depend on whether or not 225 + // the user is a project member, we always load their membership. If we're 226 + // loading all members anyway we can piggyback on that; otherwise we 227 + // do an explicit join. 228 + if (!$this->needMembers) { 229 + $select[] = 'vm.dst viewerIsMember'; 230 + } 231 + 232 + return $select; 233 + } 234 + 235 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 236 + $where = parent::buildWhereClauseParts($conn); 245 237 246 238 if ($this->status != self::STATUS_ANY) { 247 239 switch ($this->status) { ··· 264 256 $this->status)); 265 257 } 266 258 $where[] = qsprintf( 267 - $conn_r, 259 + $conn, 268 260 'status IN (%Ld)', 269 261 $filter); 270 262 } 271 263 272 264 if ($this->ids !== null) { 273 265 $where[] = qsprintf( 274 - $conn_r, 266 + $conn, 275 267 'id IN (%Ld)', 276 268 $this->ids); 277 269 } 278 270 279 271 if ($this->phids !== null) { 280 272 $where[] = qsprintf( 281 - $conn_r, 273 + $conn, 282 274 'phid IN (%Ls)', 283 275 $this->phids); 284 276 } 285 277 286 278 if ($this->memberPHIDs !== null) { 287 279 $where[] = qsprintf( 288 - $conn_r, 280 + $conn, 289 281 'e.dst IN (%Ls)', 290 282 $this->memberPHIDs); 291 283 } 292 284 293 285 if ($this->slugs !== null) { 294 286 $where[] = qsprintf( 295 - $conn_r, 287 + $conn, 296 288 'slug.slug IN (%Ls)', 297 289 $this->slugs); 298 290 } 299 291 300 292 if ($this->phrictionSlugs !== null) { 301 293 $where[] = qsprintf( 302 - $conn_r, 294 + $conn, 303 295 'phrictionSlug IN (%Ls)', 304 296 $this->phrictionSlugs); 305 297 } 306 298 307 299 if ($this->names !== null) { 308 300 $where[] = qsprintf( 309 - $conn_r, 301 + $conn, 310 302 'name IN (%Ls)', 311 303 $this->names); 312 304 } 313 305 314 306 if ($this->icons !== null) { 315 307 $where[] = qsprintf( 316 - $conn_r, 308 + $conn, 317 309 'icon IN (%Ls)', 318 310 $this->icons); 319 311 } 320 312 321 313 if ($this->colors !== null) { 322 314 $where[] = qsprintf( 323 - $conn_r, 315 + $conn, 324 316 'color IN (%Ls)', 325 317 $this->colors); 326 318 } 327 319 328 - $where[] = $this->buildPagingClause($conn_r); 329 - 330 - return $this->formatWhereClause($where); 320 + return $where; 331 321 } 332 322 333 - protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 323 + protected function shouldGroupQueryResultRows() { 334 324 if ($this->memberPHIDs || $this->nameTokens) { 335 - return 'GROUP BY p.id'; 336 - } else { 337 - return $this->buildApplicationSearchGroupClause($conn_r); 325 + return true; 338 326 } 327 + return parent::shouldGroupQueryResultRows(); 339 328 } 340 329 341 - protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { 342 - $joins = array(); 330 + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 331 + $joins = parent::buildJoinClauseParts($conn); 343 332 344 333 if (!$this->needMembers !== null) { 345 334 $joins[] = qsprintf( 346 - $conn_r, 335 + $conn, 347 336 'LEFT JOIN %T vm ON vm.src = p.phid AND vm.type = %d AND vm.dst = %s', 348 337 PhabricatorEdgeConfig::TABLE_NAME_EDGE, 349 338 PhabricatorProjectProjectHasMemberEdgeType::EDGECONST, ··· 352 341 353 342 if ($this->memberPHIDs !== null) { 354 343 $joins[] = qsprintf( 355 - $conn_r, 344 + $conn, 356 345 'JOIN %T e ON e.src = p.phid AND e.type = %d', 357 346 PhabricatorEdgeConfig::TABLE_NAME_EDGE, 358 347 PhabricatorProjectProjectHasMemberEdgeType::EDGECONST); ··· 360 349 361 350 if ($this->slugs !== null) { 362 351 $joins[] = qsprintf( 363 - $conn_r, 352 + $conn, 364 353 'JOIN %T slug on slug.projectPHID = p.phid', 365 354 id(new PhabricatorProjectSlug())->getTableName()); 366 355 } ··· 369 358 foreach ($this->nameTokens as $key => $token) { 370 359 $token_table = 'token_'.$key; 371 360 $joins[] = qsprintf( 372 - $conn_r, 361 + $conn, 373 362 'JOIN %T %T ON %T.projectID = p.id AND %T.token LIKE %>', 374 363 PhabricatorProject::TABLE_DATASOURCE_TOKEN, 375 364 $token_table, ··· 379 368 } 380 369 } 381 370 382 - $joins[] = $this->buildApplicationSearchJoinClause($conn_r); 383 - 384 - return implode(' ', $joins); 371 + return $joins; 385 372 } 386 373 387 374 public function getQueryApplicationClass() {
+16
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 113 113 return $query; 114 114 } 115 115 116 + if ($object instanceof PhabricatorSubscribableInterface) { 117 + if (!empty($parameters['subscriberPHIDs'])) { 118 + $query->withEdgeLogicPHIDs( 119 + PhabricatorObjectHasSubscriberEdgeType::EDGECONST, 120 + PhabricatorQueryConstraint::OPERATOR_OR, 121 + $parameters['subscriberPHIDs']); 122 + } 123 + } 124 + 116 125 if ($object instanceof PhabricatorProjectInterface) { 117 126 if (!empty($parameters['projectPHIDs'])) { 118 127 $query->withEdgeLogicConstraints( ··· 178 187 $object = $this->newResultObject(); 179 188 if (!$object) { 180 189 return $fields; 190 + } 191 + 192 + if ($object instanceof PhabricatorSubscribableInterface) { 193 + $fields[] = id(new PhabricatorSearchSubscribersField()) 194 + ->setLabel(pht('Subscribers')) 195 + ->setKey('subscriberPHIDs') 196 + ->setAliases(array('subscriber', 'subscribers')); 181 197 } 182 198 183 199 if ($object instanceof PhabricatorProjectInterface) {
+21
src/applications/search/field/PhabricatorSearchSubscribersField.php
··· 1 + <?php 2 + 3 + final class PhabricatorSearchSubscribersField 4 + extends PhabricatorSearchTokenizerField { 5 + 6 + protected function getDefaultValue() { 7 + return array(); 8 + } 9 + 10 + protected function getValueFromRequest(AphrontRequest $request, $key) { 11 + $allow_types = array( 12 + PhabricatorProjectProjectPHIDType::TYPECONST, 13 + ); 14 + return $this->getUsersFromRequest($request, $key, $allow_types); 15 + } 16 + 17 + protected function newDatasource() { 18 + return new PhabricatorMetaMTAMailableFunctionDatasource(); 19 + } 20 + 21 + }
+4 -2
src/applications/search/field/PhabricatorSearchTokenizerField.php
··· 26 26 abstract protected function newDatasource(); 27 27 28 28 29 - protected function getUsersFromRequest(AphrontRequest $request, $key) { 29 + protected function getUsersFromRequest( 30 + AphrontRequest $request, 31 + $key, 32 + array $allow_types = array()) { 30 33 $list = $this->getListFromRequest($request, $key); 31 - $allow_types = array(); 32 34 33 35 $phids = array(); 34 36 $names = array();
+7 -2
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
··· 76 76 return $this->beforeID; 77 77 } 78 78 79 - public function loadStandardPage(PhabricatorLiskDAO $table) { 79 + protected function loadStandardPage(PhabricatorLiskDAO $table) { 80 + $rows = $this->loadStandardPageRows($table); 81 + return $table->loadAllFromArray($rows); 82 + } 83 + 84 + protected function loadStandardPageRows(PhabricatorLiskDAO $table) { 80 85 $conn = $table->establishConnection('r'); 81 86 82 87 $rows = queryfx_all( ··· 92 97 $this->buildOrderClause($conn), 93 98 $this->buildLimitClause($conn)); 94 99 95 - return $table->loadAllFromArray($rows); 100 + return $rows; 96 101 } 97 102 98 103 /**