@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 board relationships to dedicated storage

Summary:
Fixes T5476. Using edges to store which objects are on which board columns ends up being pretty awkward. In particular, it makes T4807 very difficult to implement.

Introduce a dedicated `BoardColumnPosition` storage.

This doesn't affect ordering rules (T4807) yet: boards are still arranged by priority. We just read which tasks are on which columns out of a new table.

Test Plan:
- Migrated data, then viewed some boards. Saw exactly the same data.
- Dragged tasks from column to column.
- Created a task directly into a column.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T5476

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

+470 -122
+10
resources/sql/autopatches/20140805.boardcol.1.sql
··· 1 + CREATE TABLE {$NAMESPACE}_project.project_columnposition ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + boardPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 4 + columnPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 5 + objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 6 + sequence INT UNSIGNED NOT NULL, 7 + UNIQUE KEY (boardPHID, columnPHID, objectPHID), 8 + KEY (objectPHID, boardPHID), 9 + KEY (boardPHID, columnPHID, sequence) 10 + ) ENGINE=InnoDB, COLLATE utf8_general_ci;
+53
resources/sql/autopatches/20140805.boardcol.2.php
··· 1 + <?php 2 + 3 + // Was PhabricatorEdgeConfig::TYPE_COLUMN_HAS_OBJECT 4 + $type_has_object = 44; 5 + 6 + $column = new PhabricatorProjectColumn(); 7 + $conn_w = $column->establishConnection('w'); 8 + 9 + $rows = queryfx_all( 10 + $conn_w, 11 + 'SELECT src, dst FROM %T WHERE type = %d', 12 + PhabricatorEdgeConfig::TABLE_NAME_EDGE, 13 + $type_has_object); 14 + 15 + $cols = array(); 16 + foreach ($rows as $row) { 17 + $cols[$row['src']][] = $row['dst']; 18 + } 19 + 20 + $sql = array(); 21 + foreach ($cols as $col_phid => $obj_phids) { 22 + echo "Migrating column '{$col_phid}'...\n"; 23 + $column = id(new PhabricatorProjectColumn())->loadOneWhere( 24 + 'phid = %s', 25 + $col_phid); 26 + if (!$column) { 27 + echo "Column '{$col_phid}' does not exist.\n"; 28 + continue; 29 + } 30 + 31 + $sequence = 0; 32 + foreach ($obj_phids as $obj_phid) { 33 + $sql[] = qsprintf( 34 + $conn_w, 35 + '(%s, %s, %s, %d)', 36 + $column->getProjectPHID(), 37 + $column->getPHID(), 38 + $obj_phid, 39 + $sequence++); 40 + } 41 + } 42 + 43 + echo "Inserting rows...\n"; 44 + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { 45 + queryfx( 46 + $conn_w, 47 + 'INSERT INTO %T (boardPHID, columnPHID, objectPHID, sequence) 48 + VALUES %Q', 49 + id(new PhabricatorProjectColumnPosition())->getTableName(), 50 + $chunk); 51 + } 52 + 53 + echo "Done.\n";
+7
src/__phutil_library_map__.php
··· 1932 1932 'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php', 1933 1933 'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php', 1934 1934 'PhabricatorProjectColumnPHIDType' => 'applications/project/phid/PhabricatorProjectColumnPHIDType.php', 1935 + 'PhabricatorProjectColumnPosition' => 'applications/project/storage/PhabricatorProjectColumnPosition.php', 1936 + 'PhabricatorProjectColumnPositionQuery' => 'applications/project/query/PhabricatorProjectColumnPositionQuery.php', 1935 1937 'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.php', 1936 1938 'PhabricatorProjectColumnTransaction' => 'applications/project/storage/PhabricatorProjectColumnTransaction.php', 1937 1939 'PhabricatorProjectColumnTransactionEditor' => 'applications/project/editor/PhabricatorProjectColumnTransactionEditor.php', ··· 4764 4766 ), 4765 4767 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController', 4766 4768 'PhabricatorProjectColumnPHIDType' => 'PhabricatorPHIDType', 4769 + 'PhabricatorProjectColumnPosition' => array( 4770 + 'PhabricatorProjectDAO', 4771 + 'PhabricatorPolicyInterface', 4772 + ), 4773 + 'PhabricatorProjectColumnPositionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4767 4774 'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4768 4775 'PhabricatorProjectColumnTransaction' => 'PhabricatorApplicationTransaction', 4769 4776 'PhabricatorProjectColumnTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
+9 -22
src/applications/maniphest/controller/ManiphestTaskDetailController.php
··· 534 534 if ($project_phids) { 535 535 require_celerity_resource('maniphest-task-summary-css'); 536 536 537 - // If we end up with real-world projects with many hundreds of columns, it 538 - // might be better to just load all the edges, then load those columns and 539 - // work backward that way, or denormalize this data more. 540 - 541 - $columns = id(new PhabricatorProjectColumnQuery()) 537 + $positions = id(new PhabricatorProjectColumnPositionQuery()) 542 538 ->setViewer($viewer) 543 - ->withProjectPHIDs($project_phids) 539 + ->withBoardPHIDs($project_phids) 540 + ->withObjectPHIDs(array($task->getPHID())) 541 + ->needColumns(true) 544 542 ->execute(); 545 - $columns = mpull($columns, null, 'getPHID'); 546 - 547 - $column_edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; 548 - $all_column_phids = array_keys($columns); 549 - 550 - $column_edge_query = id(new PhabricatorEdgeQuery()) 551 - ->withSourcePHIDs(array($task->getPHID())) 552 - ->withEdgeTypes(array($column_edge_type)) 553 - ->withDestinationPHIDs($all_column_phids); 554 - $column_edge_query->execute(); 555 - $in_column_phids = array_fuse($column_edge_query->getDestinationPHIDs()); 556 - 557 - $column_groups = mgroup($columns, 'getProjectPHID'); 543 + $positions = mpull($positions, null, 'getBoardPHID'); 558 544 559 545 $project_handles = array(); 560 546 $project_annotations = array(); ··· 562 548 $handle = $this->getHandle($project_phid); 563 549 $project_handles[] = $handle; 564 550 565 - $columns = idx($column_groups, $project_phid, array()); 566 - $column = head(array_intersect_key($columns, $in_column_phids)); 567 - if ($column) { 551 + $position = idx($positions, $project_phid); 552 + if ($position) { 553 + $column = $position->getColumn(); 554 + 568 555 $column_name = pht('(%s)', $column->getDisplayName()); 569 556 $column_link = phutil_tag( 570 557 'a',
+16 -36
src/applications/maniphest/controller/ManiphestTaskEditController.php
··· 339 339 340 340 341 341 if ($parent_task) { 342 + // TODO: This should be transactional now. 342 343 id(new PhabricatorEdgeEditor()) 343 344 ->addEdge( 344 345 $parent_task->getPHID(), ··· 364 365 ->setOwner($owner) 365 366 ->setCanEdit(true) 366 367 ->getItem(); 367 - $column_phid = $request->getStr('columnPHID'); 368 + 368 369 $column = id(new PhabricatorProjectColumnQuery()) 369 370 ->setViewer($user) 370 - ->withPHIDs(array($column_phid)) 371 + ->withPHIDs(array($request->getStr('columnPHID'))) 371 372 ->executeOne(); 372 - if ($column->isDefaultColumn()) { 373 - $column_tasks = array(); 374 - $potential_col_tasks = id(new ManiphestTaskQuery()) 375 - ->setViewer($user) 376 - ->withAllProjects(array($column->getProjectPHID())) 377 - ->execute(); 378 - $potential_col_tasks = mpull( 379 - $potential_col_tasks, 380 - null, 381 - 'getPHID'); 382 - $potential_task_phids = array_keys($potential_col_tasks); 383 - if ($potential_task_phids) { 384 - $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; 385 - $edge_query = id(new PhabricatorEdgeQuery()) 386 - ->withSourcePHIDs($potential_task_phids) 387 - ->withEdgeTypes(array($edge_type)); 388 - $edges = $edge_query->execute(); 389 - foreach ($potential_col_tasks as $task_phid => $curr_task) { 390 - $curr_column_phids = $edges[$task_phid][$edge_type]; 391 - $curr_column_phid = head_key($curr_column_phids); 392 - if (!$curr_column_phid || 393 - $curr_column_phid == $column_phid) { 394 - $column_tasks[] = $curr_task; 395 - } 396 - } 397 - } 398 - } else { 399 - $column_task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 400 - $column_phid, 401 - PhabricatorEdgeConfig::TYPE_COLUMN_HAS_OBJECT); 402 - $column_tasks = id(new ManiphestTaskQuery()) 403 - ->setViewer($user) 404 - ->withPHIDs($column_task_phids) 405 - ->execute(); 373 + if (!$column) { 374 + return new Aphront404Response(); 406 375 } 376 + 377 + $positions = id(new PhabricatorProjectColumnPositionQuery()) 378 + ->setViewer($user) 379 + ->withColumns(array($column)) 380 + ->execute(); 381 + $task_phids = mpull($positions, 'getObjectPHID'); 382 + 383 + $column_tasks = id(new ManiphestTaskQuery()) 384 + ->setViewer($user) 385 + ->withPHIDs($task_phids) 386 + ->execute(); 407 387 408 388 $sort_map = mpull( 409 389 $column_tasks,
+32 -30
src/applications/maniphest/editor/ManiphestTransactionEditor.php
··· 184 184 185 185 switch ($xaction->getTransactionType()) { 186 186 case ManiphestTransaction::TYPE_PROJECT_COLUMN: 187 - $new = $xaction->getNewValue(); 188 - $old = $xaction->getOldValue(); 189 - $src = $object->getPHID(); 190 - $dst = head($new['columnPHIDs']); 191 - $edges = $old['columnPHIDs']; 192 - $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; 193 - // NOTE: Normally, we expect only one edge to exist, but this works in 194 - // a general way so it will repair any stray edges. 195 - $remove = array(); 196 - $edge_missing = true; 197 - foreach ($edges as $phid) { 198 - if ($phid == $dst) { 199 - $edge_missing = false; 200 - } else { 201 - $remove[] = $phid; 202 - } 187 + $board_phid = idx($xaction->getNewValue(), 'projectPHID'); 188 + if (!$board_phid) { 189 + throw new Exception( 190 + pht("Expected 'projectPHID' in column transaction.")); 203 191 } 204 192 205 - $add = array(); 206 - if ($edge_missing) { 207 - $add[] = $dst; 193 + $new_phids = idx($xaction->getNewValue(), 'columnPHIDs', array()); 194 + if (count($new_phids) !== 1) { 195 + throw new Exception( 196 + pht("Expected exactly one 'columnPHIDs' in column transaction.")); 208 197 } 209 198 210 - // This should never happen because of the code in 211 - // transactionHasEffect, but keep it for maximum conservativeness 212 - if (!$add && !$remove) { 213 - return; 199 + $positions = id(new PhabricatorProjectColumnPositionQuery()) 200 + ->setViewer($this->requireActor()) 201 + ->withObjectPHIDs(array($object->getPHID())) 202 + ->withBoardPHIDs(array($board_phid)) 203 + ->execute(); 204 + 205 + // Remove all existing column positions on the board. 206 + 207 + foreach ($positions as $position) { 208 + if (!$position->getID()) { 209 + // This is an ephemeral position, so don't try to destroy it. 210 + continue; 211 + } 212 + $position->delete(); 214 213 } 215 214 216 - $editor = new PhabricatorEdgeEditor(); 217 - foreach ($add as $phid) { 218 - $editor->addEdge($src, $edge_type, $phid); 215 + // Add the new column position. 216 + 217 + foreach ($new_phids as $phid) { 218 + id(new PhabricatorProjectColumnPosition()) 219 + ->setBoardPHID($board_phid) 220 + ->setColumnPHID($phid) 221 + ->setObjectPHID($object->getPHID()) 222 + // TODO: Do real sequence stuff. 223 + ->setSequence(0) 224 + ->save(); 219 225 } 220 - foreach ($remove as $phid) { 221 - $editor->removeEdge($src, $edge_type, $phid); 222 - } 223 - $editor->save(); 224 226 break; 225 227 default: 226 228 break;
+13 -11
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 135 135 ->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY) 136 136 ->setViewer($viewer) 137 137 ->execute(); 138 - 139 138 $tasks = mpull($tasks, null, 'getPHID'); 140 - $task_phids = array_keys($tasks); 141 139 142 - if ($task_phids) { 143 - $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; 144 - $edge_query = id(new PhabricatorEdgeQuery()) 145 - ->withSourcePHIDs($task_phids) 146 - ->withEdgeTypes(array($edge_type)) 147 - ->withDestinationPHIDs(mpull($columns, 'getPHID')); 148 - $edge_query->execute(); 140 + if ($tasks) { 141 + $positions = id(new PhabricatorProjectColumnPositionQuery()) 142 + ->setViewer($viewer) 143 + ->withObjectPHIDs(mpull($tasks, 'getPHID')) 144 + ->withColumns($columns) 145 + ->execute(); 146 + $positions = mpull($positions, null, 'getObjectPHID'); 147 + } else { 148 + $positions = array(); 149 149 } 150 150 151 151 $task_map = array(); 152 152 $default_phid = $columns[0]->getPHID(); 153 153 foreach ($tasks as $task) { 154 154 $task_phid = $task->getPHID(); 155 - $column_phids = $edge_query->getDestinationPHIDs(array($task_phid)); 156 155 157 - $column_phid = head($column_phids); 156 + $column_phid = null; 157 + if (isset($positions[$task_phid])) { 158 + $column_phid = $positions[$task_phid]->getColumnPHID(); 159 + } 158 160 $column_phid = nonempty($column_phid, $default_phid); 159 161 160 162 $task_map[$column_phid][] = $task_phid;
+17 -17
src/applications/project/controller/PhabricatorProjectMoveController.php
··· 57 57 return new Aphront404Response(); 58 58 } 59 59 60 - $xactions = array(); 60 + $positions = id(new PhabricatorProjectColumnPositionQuery()) 61 + ->setViewer($viewer) 62 + ->withColumns($columns) 63 + ->withObjectPHIDs(array($object_phid)) 64 + ->execute(); 61 65 62 - $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; 63 - 64 - $query = id(new PhabricatorEdgeQuery()) 65 - ->withSourcePHIDs(array($object->getPHID())) 66 - ->withEdgeTypes(array($edge_type)) 67 - ->withDestinationPHIDs(array_keys($columns)); 68 - 69 - $query->execute(); 70 - 71 - $edge_phids = $query->getDestinationPHIDs(); 66 + $xactions = array(); 72 67 73 68 $xactions[] = id(new ManiphestTransaction()) 74 69 ->setTransactionType(ManiphestTransaction::TYPE_PROJECT_COLUMN) 75 - ->setNewValue(array( 76 - 'columnPHIDs' => array($column->getPHID()), 77 - 'projectPHID' => $column->getProjectPHID())) 78 - ->setOldValue(array( 79 - 'columnPHIDs' => $edge_phids, 80 - 'projectPHID' => $column->getProjectPHID())); 70 + ->setNewValue( 71 + array( 72 + 'columnPHIDs' => array($column->getPHID()), 73 + 'projectPHID' => $column->getProjectPHID(), 74 + )) 75 + ->setOldValue( 76 + array( 77 + 'columnPHIDs' => mpull($positions, 'getColumnPHID'), 78 + 'projectPHID' => $column->getProjectPHID(), 79 + )); 81 80 82 81 $task_phids = array(); 83 82 if ($after_phid) { ··· 86 85 if ($before_phid) { 87 86 $task_phids[] = $before_phid; 88 87 } 88 + 89 89 if ($task_phids) { 90 90 $tasks = id(new ManiphestTaskQuery()) 91 91 ->setViewer($viewer)
+258
src/applications/project/query/PhabricatorProjectColumnPositionQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectColumnPositionQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $boardPHIDs; 8 + private $objectPHIDs; 9 + private $columns; 10 + 11 + private $needColumns; 12 + 13 + public function withIDs(array $ids) { 14 + $this->ids = $ids; 15 + return $this; 16 + } 17 + 18 + public function withBoardPHIDs(array $board_phids) { 19 + $this->boardPHIDs = $board_phids; 20 + return $this; 21 + } 22 + 23 + public function withObjectPHIDs(array $object_phids) { 24 + $this->objectPHIDs = $object_phids; 25 + return $this; 26 + } 27 + 28 + /** 29 + * Find objects in specific columns. 30 + * 31 + * NOTE: Using this method activates logic which constructs virtual 32 + * column positions for objects not in any column, if you pass a default 33 + * column. Normally these results are not returned. 34 + * 35 + * @param list<PhabricatorProjectColumn> Columns to look for objects in. 36 + * @return this 37 + */ 38 + public function withColumns(array $columns) { 39 + assert_instances_of($columns, 'PhabricatorProjectColumn'); 40 + $this->columns = $columns; 41 + return $this; 42 + } 43 + 44 + public function needColumns($need_columns) { 45 + $this->needColumns = true; 46 + return $this; 47 + } 48 + 49 + // NOTE: For now, boards are always attached to projects. However, they might 50 + // not be in the future. This generalization just anticipates a future where 51 + // we let other types of objects (like users) have boards, or let boards 52 + // contain other types of objects. 53 + 54 + private function newPositionObject() { 55 + return new PhabricatorProjectColumnPosition(); 56 + } 57 + 58 + private function newColumnQuery() { 59 + return new PhabricatorProjectColumnQuery(); 60 + } 61 + 62 + private function getBoardMembershipEdgeTypes() { 63 + return array( 64 + PhabricatorProjectProjectHasObjectEdgeType::EDGECONST, 65 + ); 66 + } 67 + 68 + private function getBoardMembershipPHIDTypes() { 69 + return array( 70 + ManiphestTaskPHIDType::TYPECONST, 71 + ); 72 + } 73 + 74 + protected function loadPage() { 75 + $table = $this->newPositionObject(); 76 + $conn_r = $table->establishConnection('r'); 77 + 78 + // We're going to find results by combining two queries: one query finds 79 + // objects on a board column, while the other query finds objects not on 80 + // any board column and virtually puts them on the default column. 81 + 82 + $unions = array(); 83 + 84 + // First, find all the stuff that's actually on a column. 85 + 86 + $unions[] = qsprintf( 87 + $conn_r, 88 + 'SELECT * FROM %T %Q', 89 + $table->getTableName(), 90 + $this->buildWhereClause($conn_r)); 91 + 92 + // If we have a default column, find all the stuff that's not in any 93 + // column and put it in the default column. 94 + 95 + $must_type_filter = false; 96 + if ($this->columns) { 97 + $default_map = array(); 98 + foreach ($this->columns as $column) { 99 + if ($column->isDefaultColumn()) { 100 + $default_map[$column->getProjectPHID()] = $column->getPHID(); 101 + } 102 + } 103 + 104 + if ($default_map) { 105 + $where = array(); 106 + 107 + // Find the edges attached to the boards we have default columns for. 108 + 109 + $where[] = qsprintf( 110 + $conn_r, 111 + 'e.src IN (%Ls)', 112 + array_keys($default_map)); 113 + 114 + // Find only edges which describe a board relationship. 115 + 116 + $where[] = qsprintf( 117 + $conn_r, 118 + 'e.type IN (%Ld)', 119 + $this->getBoardMembershipEdgeTypes()); 120 + 121 + if ($this->boardPHIDs !== null) { 122 + // This should normally be redundant, but construct it anyway if 123 + // the caller has told us to. 124 + $where[] = qsprintf( 125 + $conn_r, 126 + 'e.src IN (%Ls)', 127 + $this->boardPHIDs); 128 + } 129 + 130 + if ($this->objectPHIDs !== null) { 131 + $where[] = qsprintf( 132 + $conn_r, 133 + 'e.dst IN (%Ls)', 134 + $this->objectPHIDs); 135 + } 136 + 137 + $where[] = qsprintf( 138 + $conn_r, 139 + 'p.id IS NULL'); 140 + 141 + $where = $this->formatWhereClause($where); 142 + 143 + $unions[] = qsprintf( 144 + $conn_r, 145 + 'SELECT NULL id, e.src boardPHID, NULL columnPHID, e.dst objectPHID, 146 + 0 sequence 147 + FROM %T e LEFT JOIN %T p 148 + ON e.src = p.boardPHID AND e.dst = p.objectPHID 149 + %Q', 150 + PhabricatorEdgeConfig::TABLE_NAME_EDGE, 151 + $table->getTableName(), 152 + $where); 153 + 154 + $must_type_filter = true; 155 + } 156 + } 157 + 158 + $data = queryfx_all( 159 + $conn_r, 160 + '%Q %Q %Q', 161 + implode(' UNION ALL ', $unions), 162 + $this->buildOrderClause($conn_r), 163 + $this->buildLimitClause($conn_r)); 164 + 165 + // If we've picked up objects not in any column, we need to filter out any 166 + // matched objects which have the wrong edge type. 167 + if ($must_type_filter) { 168 + $allowed_types = array_fuse($this->getBoardMembershipPHIDTypes()); 169 + foreach ($data as $id => $row) { 170 + if ($row['columnPHID'] === null) { 171 + $object_phid = $row['objectPHID']; 172 + if (empty($allowed_types[phid_get_type($object_phid)])) { 173 + unset($data[$id]); 174 + } 175 + } 176 + } 177 + } 178 + 179 + $positions = $table->loadAllFromArray($data); 180 + 181 + foreach ($positions as $position) { 182 + if ($position->getColumnPHID() === null) { 183 + $position->makeEphemeral(); 184 + $column_phid = idx($default_map, $position->getBoardPHID()); 185 + $position->setColumnPHID($column_phid); 186 + } 187 + } 188 + 189 + return $positions; 190 + } 191 + 192 + protected function willFilterPage(array $page) { 193 + 194 + if ($this->needColumns) { 195 + $column_phids = mpull($page, 'getColumnPHID'); 196 + $columns = $this->newColumnQuery() 197 + ->setParentQuery($this) 198 + ->setViewer($this->getViewer()) 199 + ->withPHIDs($column_phids) 200 + ->execute(); 201 + $columns = mpull($columns, null, 'getPHID'); 202 + 203 + foreach ($page as $key => $position) { 204 + $column = idx($columns, $position->getColumnPHID()); 205 + if (!$column) { 206 + unset($page[$key]); 207 + continue; 208 + } 209 + 210 + $position->attachColumn($column); 211 + } 212 + } 213 + 214 + return $page; 215 + } 216 + 217 + private function buildWhereClause($conn_r) { 218 + $where = array(); 219 + 220 + if ($this->ids !== null) { 221 + $where[] = qsprintf( 222 + $conn_r, 223 + 'id IN (%Ld)', 224 + $this->ids); 225 + } 226 + 227 + if ($this->boardPHIDs !== null) { 228 + $where[] = qsprintf( 229 + $conn_r, 230 + 'boardPHID IN (%Ls)', 231 + $this->boardPHIDs); 232 + } 233 + 234 + if ($this->objectPHIDs !== null) { 235 + $where[] = qsprintf( 236 + $conn_r, 237 + 'objectPHID IN (%Ls)', 238 + $this->objectPHIDs); 239 + } 240 + 241 + if ($this->columns !== null) { 242 + $where[] = qsprintf( 243 + $conn_r, 244 + 'columnPHID IN (%Ls)', 245 + mpull($this->columns, 'getPHID')); 246 + } 247 + 248 + // NOTE: Explicitly not building the paging clause here, since it won't 249 + // work with the UNION. 250 + 251 + return $this->formatWhereClause($where); 252 + } 253 + 254 + public function getQueryApplicationClass() { 255 + return 'PhabricatorProjectApplication'; 256 + } 257 + 258 + }
+51
src/applications/project/storage/PhabricatorProjectColumnPosition.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO 4 + implements PhabricatorPolicyInterface { 5 + 6 + protected $boardPHID; 7 + protected $columnPHID; 8 + protected $objectPHID; 9 + protected $sequence; 10 + 11 + private $column = self::ATTACHABLE; 12 + 13 + public function getConfiguration() { 14 + return array( 15 + self::CONFIG_TIMESTAMPS => false, 16 + ) + parent::getConfiguration(); 17 + } 18 + 19 + public function getColumn() { 20 + return $this->assertAttached($this->column); 21 + } 22 + 23 + public function attachColumn(PhabricatorProjectColumn $column) { 24 + $this->column = $column; 25 + return $this; 26 + } 27 + 28 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 29 + 30 + public function getCapabilities() { 31 + return array( 32 + PhabricatorPolicyCapability::CAN_VIEW, 33 + ); 34 + } 35 + 36 + public function getPolicy($capability) { 37 + switch ($capability) { 38 + case PhabricatorPolicyCapability::CAN_VIEW: 39 + return PhabricatorPolicies::getMostOpenPolicy(); 40 + } 41 + } 42 + 43 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 44 + return false; 45 + } 46 + 47 + public function describeAutomaticCapability($capability) { 48 + return null; 49 + } 50 + 51 + }
+4 -6
src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
··· 57 57 const TYPE_OBJECT_USES_CREDENTIAL = 39; 58 58 const TYPE_CREDENTIAL_USED_BY_OBJECT = 40; 59 59 60 - const TYPE_OBJECT_HAS_COLUMN = 43; 61 - const TYPE_COLUMN_HAS_OBJECT = 44; 62 - 63 60 const TYPE_DASHBOARD_HAS_PANEL = 45; 64 61 const TYPE_PANEL_HAS_DASHBOARD = 46; 65 62 ··· 105 102 range(1, 50), 106 103 array(9000), 107 104 range(80000, 80005)); 105 + 106 + $exclude[] = 43; // Was TYPE_OBJECT_HAS_COLUMN 107 + $exclude[] = 44; // Was TYPE_COLUMN_HAS_OBJECT 108 + 108 109 $consts = array_diff($consts, $exclude); 109 110 110 111 $map = array(); ··· 189 190 190 191 self::TYPE_OBJECT_USES_CREDENTIAL => self::TYPE_CREDENTIAL_USED_BY_OBJECT, 191 192 self::TYPE_CREDENTIAL_USED_BY_OBJECT => self::TYPE_OBJECT_USES_CREDENTIAL, 192 - 193 - self::TYPE_OBJECT_HAS_COLUMN => self::TYPE_COLUMN_HAS_OBJECT, 194 - self::TYPE_COLUMN_HAS_OBJECT => self::TYPE_OBJECT_HAS_COLUMN, 195 193 196 194 self::TYPE_PANEL_HAS_DASHBOARD => self::TYPE_DASHBOARD_HAS_PANEL, 197 195 self::TYPE_DASHBOARD_HAS_PANEL => self::TYPE_PANEL_HAS_DASHBOARD,