Page MenuHomePhabricator

D15171.id36627.diff
No OneTemporary

D15171.id36627.diff

diff --git a/resources/builtin/image-200x200.png b/resources/builtin/image-200x200.png
new file mode 100644
index 0000000000000000000000000000000000000000..53bc1e785c395c4c5882c1aec3bfc9eac13d4159
GIT binary patch
literal 1261
zc%17D@N?(olHy`uVBq!ia0vp^CqS5k2}mkgS)K$^k|nMYCBgY=CFO}lsSJ)O`AMk?
zp1FzXsX?iUDV2pMQ*9U+So%F(978H@y}9l0EnO*b;NvP`ZAXzFZmwIQH=1vJekW)1
zev>-?KEC&_E>7OppL)Hy>Z@!AyLgUw=I#s5N9`4Qm6|R}{eR<J_hy3El|?T#=krS#
zJyvOdcsspr@{Mi1_5W(w&Q3de-e1|?LeS*masT<Z=d$i&PgK-tY2Dxu!NHx_xIsYd
z5EDp%^^g-#04OdX1{7D+0g5|CD9~Qv$Ga(hj$5Pt{$6jdJ@wSpt5+}IU%Pm5a!SgR
z?f3tm*yIzTt2@cc)^_gf+1X(c+}zyHpFh8Q_wJ#rkDaB3wY9MtB4y>}w}If+ExGS@
z=H})f?G`Uy{PD-r?hPwUC$^pa^{eXewo8oPyXUE?sa-mFzi7{fsAq1=(kd!`>{2@_
zIC1G;{(Uv+>CdD2gu}GkrcU3oe}8>RiOB5Q+FEC4=b!oSC$-JlP;;jsJwj0|T3uZ|
z*7T!_Tj1XpyS#lY5;<2FMhLpIgy>CA-aECZQ@y>toj+{qB%x^=-tC^V^lJIOOJ0jt
z+<Sd}{q<|t@~%$X&wMDWK=aI|H6dD|tFMZL*>s+*X=n{!AGf#ivszff#c7?YYufD#
zJvM6{l9HBw{qm*d+D`S2lGee|A9w8BIrFNFjLebHPuHYQv-7^=lJ;NYp}sET@#Dva
z>n@$!W|e<`UoZ2euZCvHvGxWA57q@l@UPz(6!Eol$={DVcJ11=cdzXBuAkHGrKP2-
ztE+>9gMS+OE%r`)y!+6fG=u5yEC2til?wm4%g(Fn`@2}HW5;>b!X6iv{(q`d7p)<p
zr>A!*r2F`cXrOC8Pk)l%RG%2HdoS4J>-UDQX=!P{e*aE(t&bD-PcklZ^U*ZzE*D<?
z@yM$;Z+x=Xv>g}eKKk<I%O7vwyvfM{8g%cE{-mz!vmy><?Ku_|74_@G!^5$`>bkFz
zKh86oK7D#}-qY^2KydP;=kmY^-a}p&?-aXs{{_mtoZL|{iO>7^@oe3DtyeA9NZ4G>
z`ugwiqUF=|<Bv_=ym@m-#9y0Ysn)9%3ZYt4*{^%PZ1}bES^w{r(s;d%X<E}y_eV6i
zhklIGE>FCBPRVxNnl(00^Y2~Oy7xlpTJG2EWyjCjJP(N2-dyqP`0Ra#BG<w%J%4sK
zOR3&GaqF&i>(>4J`SVZxGn>?;qc^mtdbLg4Fi(_guKoW{uRne)3>Lc^IqmD$sq^gL
z3x%!L7VFlJ`(t5YabwNXsF11kZ<W`C&5LNgcQ5Yw!ksm&1E1#*3XhggK6)NlF3jba
e_1d11jp1+WwB`DOCszW?F9uInKbLh*2~7ZxuS3ZI
literal 0
Hc$@<O00001
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -413,7 +413,7 @@
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
'rsrc/js/application/policy/behavior-policy-control.js' => 'ae45872f',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
- 'rsrc/js/application/projects/behavior-project-boards.js' => 'c05fb42a',
+ 'rsrc/js/application/projects/behavior-project-boards.js' => '48470f95',
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
@@ -653,7 +653,7 @@
'javelin-behavior-phui-profile-menu' => '12884df9',
'javelin-behavior-policy-control' => 'ae45872f',
'javelin-behavior-policy-rule-editor' => '5e9f347c',
- 'javelin-behavior-project-boards' => 'c05fb42a',
+ 'javelin-behavior-project-boards' => '48470f95',
'javelin-behavior-project-create' => '065227cc',
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
'javelin-behavior-recurring-edit' => '5f1c4d5f',
@@ -1144,6 +1144,15 @@
'javelin-dom',
'javelin-workflow',
),
+ '48470f95' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-vector',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'phabricator-draggable-list',
+ ),
'49b73b36' => array(
'javelin-behavior',
'javelin-dom',
@@ -1772,15 +1781,6 @@
'javelin-install',
'javelin-dom',
),
- 'c05fb42a' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-vector',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'phabricator-draggable-list',
- ),
'c1700f6f' => array(
'javelin-install',
'javelin-util',
diff --git a/resources/sql/autopatches/20160202.board.1.proxy.sql b/resources/sql/autopatches/20160202.board.1.proxy.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160202.board.1.proxy.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_project.project_column
+ ADD proxyPHID VARBINARY(64);
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1885,6 +1885,7 @@
'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php',
'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php',
'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php',
+ 'PhabricatorColumnProxyInterface' => 'applications/project/interface/PhabricatorColumnProxyInterface.php',
'PhabricatorCommentEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php',
'PhabricatorCommentEditField' => 'applications/transactions/editfield/PhabricatorCommentEditField.php',
'PhabricatorCommentEditType' => 'applications/transactions/edittype/PhabricatorCommentEditType.php',
@@ -7260,6 +7261,7 @@
'PhabricatorDestructibleInterface',
'PhabricatorFulltextInterface',
'PhabricatorConduitResultInterface',
+ 'PhabricatorColumnProxyInterface',
),
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
'PhabricatorProjectApplication' => 'PhabricatorApplication',
diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php
--- a/src/applications/maniphest/editor/ManiphestEditEngine.php
+++ b/src/applications/maniphest/editor/ManiphestEditEngine.php
@@ -280,10 +280,23 @@
return new Aphront404Response();
}
- // If the workboard's project has been removed from the card's project
- // list, we are going to remove it from the board completely.
+ // If the workboard's project and all descendant projects have been removed
+ // from the card's project list, we are going to remove it from the board
+ // completely.
+
+ // TODO: If the user did something sneaky and changed a subproject, we'll
+ // currently leave the card where it was but should really move it to the
+ // proper new column.
+
+ $descendant_projects = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withAncestorProjectPHIDs(array($column->getProjectPHID()))
+ ->execute();
+ $board_phids = mpull($descendant_projects, 'getPHID', 'getPHID');
+ $board_phids[$column->getProjectPHID()] = $column->getProjectPHID();
+
$project_map = array_fuse($task->getProjectPHIDs());
- $remove_card = empty($project_map[$column->getProjectPHID()]);
+ $remove_card = !array_intersect_key($board_phids, $project_map);
$positions = id(new PhabricatorProjectColumnPositionQuery())
->setViewer($viewer)
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -130,57 +130,28 @@
$task_query = $engine->buildQueryFromSavedQuery($saved);
+ $select_phids = array($project->getPHID());
+ if ($project->getHasSubprojects() || $project->getHasMilestones()) {
+ $descendants = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withAncestorProjectPHIDs($select_phids)
+ ->execute();
+ foreach ($descendants as $descendant) {
+ $select_phids[] = $descendant->getPHID();
+ }
+ }
+
$tasks = $task_query
->withEdgeLogicPHIDs(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
- PhabricatorQueryConstraint::OPERATOR_AND,
- array($project->getPHID()))
+ PhabricatorQueryConstraint::OPERATOR_ANCESTOR,
+ array($select_phids))
->setOrder(ManiphestTaskQuery::ORDER_PRIORITY)
->setViewer($viewer)
->execute();
$tasks = mpull($tasks, null, 'getPHID');
- if ($tasks) {
- $positions = id(new PhabricatorProjectColumnPositionQuery())
- ->setViewer($viewer)
- ->withObjectPHIDs(mpull($tasks, 'getPHID'))
- ->withColumns($columns)
- ->execute();
- $positions = mpull($positions, null, 'getObjectPHID');
- } else {
- $positions = array();
- }
-
- $task_map = array();
- foreach ($tasks as $task) {
- $task_phid = $task->getPHID();
- if (empty($positions[$task_phid])) {
- // This shouldn't normally be possible because we create positions on
- // demand, but we might have raced as an object was removed from the
- // board. Just drop the task if we don't have a position for it.
- continue;
- }
-
- $position = $positions[$task_phid];
- $task_map[$position->getColumnPHID()][] = $task_phid;
- }
-
- // If we're showing the board in "natural" order, sort columns by their
- // column positions.
- if ($this->sortKey == PhabricatorProjectColumn::ORDER_NATURAL) {
- foreach ($task_map as $column_phid => $task_phids) {
- $order = array();
- foreach ($task_phids as $task_phid) {
- if (isset($positions[$task_phid])) {
- $order[$task_phid] = $positions[$task_phid]->getOrderingKey();
- } else {
- $order[$task_phid] = 0;
- }
- }
- asort($order);
- $task_map[$column_phid] = array_keys($order);
- }
- }
+ $task_map = $this->loadColumnMap($project, $tasks, $columns);
$task_can_edit_map = id(new PhabricatorPolicyFilter())
->setViewer($viewer)
@@ -251,6 +222,10 @@
$this->handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks);
foreach ($columns as $column) {
+ if (!$this->showHidden && $column->isHidden()) {
+ continue;
+ }
+
$task_phids = idx($task_map, $column->getPHID(), array());
$column_tasks = array_select_keys($tasks, $task_phids);
@@ -264,6 +239,11 @@
$panel->setHeaderIcon($header_icon);
}
+ $display_class = $column->getDisplayClass();
+ if ($display_class) {
+ $panel->addClass($display_class);
+ }
+
if ($column->isHidden()) {
$panel->addClass('project-panel-hidden');
}
@@ -390,14 +370,54 @@
->setViewer($viewer)
->withProjectPHIDs(array($project->getPHID()));
- if (!$this->showHidden) {
- $column_query->withStatuses(
- array(PhabricatorProjectColumn::STATUS_ACTIVE));
+ $columns = $column_query->execute();
+ $columns = msort($columns, 'getSequence');
+
+ // If this project has no real columns, consider the workboard empty and
+ // return nothing. We don't want to create proxy columns if there's no
+ // board at all yet.
+ if (!$columns) {
+ return array();
}
- $columns = $column_query->execute();
- $columns = mpull($columns, null, 'getSequence');
- ksort($columns);
+ // If this project has subprojects or milestones, remove all columns
+ // except the backlog, then create any missing columns.
+ if ($project->getHasSubprojects() || $project->getHasMilestones()) {
+ foreach ($columns as $key => $column) {
+ if (!$column->getProxyPHID() && !$column->isDefaultColumn()) {
+ unset($columns[$key]);
+ }
+ }
+
+ $child_projects = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withParentProjectPHIDs(array($project->getPHID()))
+ ->withIsMilestone(true)
+ ->execute();
+ $child_projects = mpull($child_projects, null, 'getPHID');
+
+ $next_sequence = last($columns)->getSequence() + 1;
+
+ $proxy_columns = mpull($columns, null, 'getProxyPHID');
+ foreach ($child_projects as $phid => $child) {
+ if (isset($proxy_columns[$phid])) {
+ continue;
+ }
+
+ $new_column = PhabricatorProjectColumn::initializeNewColumn($viewer)
+ ->attachProject($project)
+ ->attachProxy($child)
+ ->setSequence($next_sequence++)
+ ->setProjectPHID($project->getPHID())
+ ->setProxyPHID($phid);
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ $new_column->save();
+ unset($unguarded);
+
+ $columns[] = $new_column;
+ }
+ }
return $columns;
}
@@ -618,6 +638,12 @@
$column_items = array();
+ if ($column->getProxyPHID()) {
+ $default_phid = $column->getProxyPHID();
+ } else {
+ $default_phid = $column->getProjectPHID();
+ }
+
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-plus')
->setName(pht('Create Task...'))
@@ -626,6 +652,7 @@
->setMetadata(
array(
'columnPHID' => $column->getPHID(),
+ 'projectPHID' => $default_phid,
));
$batch_edit_uri = $request->getRequestURI();
@@ -774,6 +801,10 @@
}
}
+ // TODO: Tailor this UI if the project is already a parent project. We
+ // should not offer options for creating a parent project workboard, since
+ // they can't have their own columns.
+
$new_selector = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Columns'))
->setName('initialize-type')
@@ -830,4 +861,188 @@
->addCancelButton($profile_uri);
}
+ private function loadColumnMap(
+ PhabricatorProject $board_project,
+ array $tasks,
+ array $columns) {
+
+ if (!$tasks) {
+ return array();
+ }
+
+ $viewer = $this->getViewer();
+
+ $positions = id(new PhabricatorProjectColumnPositionQuery())
+ ->setViewer($viewer)
+ ->withObjectPHIDs(mpull($tasks, 'getPHID'))
+ ->withColumns($columns)
+ ->execute();
+ $positions = mpull($positions, null, 'getObjectPHID');
+
+ // First, put the tasks in the correct global order so we don't have to
+ // worry about it later.
+ if ($this->sortKey == PhabricatorProjectColumn::ORDER_NATURAL) {
+ $sort_map = array();
+ foreach ($tasks as $phid => $task) {
+ $position = idx($positions, $phid);
+ if ($position) {
+ $sort_map[$phid] = $position->getOrderingKey();
+ } else {
+ $sort_map[$phid] = 0;
+ }
+ }
+ asort($sort_map);
+ $tasks = array_select_keys($tasks, array_keys($sort_map));
+ }
+
+ // Find all the columns which are proxies for other objects.
+ $proxy_map = array();
+ foreach ($columns as $column) {
+ $proxy_phid = $column->getProxyPHID();
+ if ($proxy_phid) {
+ $proxy_map[$proxy_phid] = $column->getPHID();
+ }
+ }
+
+ // If we have proxies, we need to force cards into the correct proxy
+ // columns.
+ if ($proxy_map) {
+ $all_projects = array();
+ foreach ($tasks as $task) {
+ foreach ($task->getProjectPHIDs() as $project_phid) {
+ $all_projects[$project_phid] = $project_phid;
+ }
+ }
+
+ if ($all_projects) {
+ $projects = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($all_projects)
+ ->execute();
+ $projects = mpull($projects, null, 'getPHID');
+ } else {
+ $projects = array();
+ }
+
+ // Build a map from every project that any task is tagged with to the
+ // ancestor project which has a column on this board, if one exists.
+ $ancestor_map = array();
+ foreach ($projects as $phid => $project) {
+ if (isset($proxy_map[$phid])) {
+ $ancestor_map[$phid] = $proxy_map[$phid];
+ } else {
+ $seen = array($phid);
+ foreach ($project->getAncestorProjects() as $ancestor) {
+ $ancestor_phid = $ancestor->getPHID();
+ $seen[] = $ancestor_phid;
+ if (isset($proxy_map[$ancestor_phid])) {
+ foreach ($seen as $project_phid) {
+ $ancestor_map[$project_phid] = $proxy_map[$ancestor_phid];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ $column_map = mpull($columns, null, 'getPHID');
+
+ foreach ($columns as $column) {
+ if ($column->isDefaultColumn()) {
+ $default_phid = $column->getPHID();
+ break;
+ }
+ }
+
+ $rem_positions = array();
+ $add_positions = array();
+
+ $task_map = array();
+ foreach ($tasks as $task_phid => $task) {
+ // First, check for a tags that have corresponding proxy columns,
+ if ($proxy_map) {
+ $proxy_hits = array();
+ foreach ($task->getProjectPHIDs() as $project_phid) {
+ if (isset($ancestor_map[$project_phid])) {
+ $task_map[$ancestor_map[$project_phid]][] = $task_phid;
+ $proxy_hits[] = $ancestor_map[$project_phid];
+
+ // NOTE: For now, only permit tasks to appear in one column. We
+ // may change this soon.
+ break;
+ }
+ }
+ if ($proxy_hits) {
+ $proxy_hits = array_fuse($proxy_hits);
+
+ $task_positions = array();
+
+ $position = idx($positions, $task_phid);
+ if ($position) {
+ $task_positions[] = $position;
+ }
+
+ foreach ($task_positions as $task_position) {
+ $column_phid = $task_position->getColumnPHID();
+
+ if (isset($proxy_hits[$column_phid])) {
+ unset($proxy_hits[$column_phid]);
+ continue;
+ }
+
+ $rem_positions[] = $task_position;
+ }
+
+ foreach ($proxy_hits as $proxy_column_phid) {
+ $add_positions[] = id(new PhabricatorProjectColumnPosition())
+ ->setBoardPHID($board_project->getPHID())
+ ->setColumnPHID($proxy_column_phid)
+ ->setObjectPHID($task_phid)
+ ->setSequence(0);
+ }
+
+ continue;
+ }
+ }
+
+ // Next, check for existing positions on the board.
+ $position = idx($positions, $task_phid);
+ if ($position) {
+ $column_phid = $position->getColumnPHID();
+
+ // We only use an exiting position if the column is valid. For example,
+ // if a task was in a column but the project was converted into a
+ // parent project, the task gets kicked back to the backlog.
+ if (isset($column_map[$column_phid])) {
+ $task_map[$column_phid][] = $task_phid;
+ continue;
+ } else {
+ $rem_positions[] = $position;
+ }
+ }
+
+ // If we made it here, we didn't find a valid position, so we're putting
+ // it in the backlog and writing a new position row.
+
+ $task_map[$default_phid][] = $task_phid;
+ $add_positions[] = id(new PhabricatorProjectColumnPosition())
+ ->setBoardPHID($board_project->getPHID())
+ ->setColumnPHID($default_phid)
+ ->setObjectPHID($task_phid)
+ ->setSequence(0);
+ }
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ foreach ($rem_positions as $position) {
+ $position->delete();
+ }
+ foreach ($add_positions as $position) {
+ $position->save();
+ }
+ unset($unguarded);
+
+ return $task_map;
+ }
+
+
}
diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php
--- a/src/applications/project/controller/PhabricatorProjectMoveController.php
+++ b/src/applications/project/controller/PhabricatorProjectMoveController.php
@@ -134,7 +134,33 @@
->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
->setNewValue($sub);
}
- }
+ }
+
+ $proxy = $column->getProxy();
+ if ($proxy) {
+ // We're moving the task into a subproject or milestone column, so add
+ // the subproject or milestone.
+ $add_projects = array($proxy->getPHID());
+ } else if ($project->getHasSubprojects() || $project->getHasMilestones()) {
+ // We're moving the task into the "Backlog" column on the parent project,
+ // so add the parent explicitly. This gets rid of any subproject or
+ // milestone tags.
+ $add_projects = array($project->getPHID());
+ } else {
+ $add_projects = array();
+ }
+
+ if ($add_projects) {
+ $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
+
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+ ->setMetadataValue('edge:type', $project_type)
+ ->setNewValue(
+ array(
+ '+' => array_fuse($add_projects),
+ ));
+ }
$editor = id(new ManiphestTransactionEditor())
->setActor($viewer)
@@ -152,6 +178,18 @@
->executeOne();
}
+ // Reload the object so it reflects edits which have been applied.
+ $object = id(new ManiphestTaskQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($object_phid))
+ ->needProjectPHIDs(true)
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+
$card = id(new ProjectBoardTaskCard())
->setViewer($viewer)
->setTask($object)
@@ -164,6 +202,6 @@
return id(new AphrontAjaxResponse())->setContent(
array('task' => $card));
- }
+ }
}
diff --git a/src/applications/project/interface/PhabricatorColumnProxyInterface.php b/src/applications/project/interface/PhabricatorColumnProxyInterface.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/interface/PhabricatorColumnProxyInterface.php
@@ -0,0 +1,7 @@
+<?php
+
+interface PhabricatorColumnProxyInterface {
+
+ public function getProxyColumnName();
+
+}
diff --git a/src/applications/project/query/PhabricatorProjectColumnQuery.php b/src/applications/project/query/PhabricatorProjectColumnQuery.php
--- a/src/applications/project/query/PhabricatorProjectColumnQuery.php
+++ b/src/applications/project/query/PhabricatorProjectColumnQuery.php
@@ -60,6 +60,55 @@
$column->attachProject($project);
}
+ $proxy_phids = array_filter(mpull($page, 'getProjectPHID'));
+
+ return $page;
+ }
+
+ protected function didFilterPage(array $page) {
+ $proxy_phids = array();
+ foreach ($page as $column) {
+ $proxy_phid = $column->getProxyPHID();
+ if ($proxy_phid !== null) {
+ $proxy_phids[$proxy_phid] = $proxy_phid;
+ }
+ }
+
+ if ($proxy_phids) {
+ $proxies = id(new PhabricatorObjectQuery())
+ ->setParentQuery($this)
+ ->setViewer($this->getViewer())
+ ->withPHIDs($proxy_phids)
+ ->execute();
+ $proxies = mpull($proxies, null, 'getPHID');
+ } else {
+ $proxies = array();
+ }
+
+ foreach ($page as $key => $column) {
+ $proxy_phid = $column->getProxyPHID();
+
+ if ($proxy_phid !== null) {
+ $proxy = idx($proxies, $proxy_phid);
+
+ // Only attach valid proxies, so we don't end up getting surprsied if
+ // an install somehow gets junk into their database.
+ if (!($proxy instanceof PhabricatorColumnProxyInterface)) {
+ $proxy = null;
+ }
+
+ if (!$proxy) {
+ $this->didRejectResult($column);
+ unset($page[$key]);
+ continue;
+ }
+ } else {
+ $proxy = null;
+ }
+
+ $column->attachProxy($proxy);
+ }
+
return $page;
}
diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php
--- a/src/applications/project/storage/PhabricatorProject.php
+++ b/src/applications/project/storage/PhabricatorProject.php
@@ -9,7 +9,8 @@
PhabricatorCustomFieldInterface,
PhabricatorDestructibleInterface,
PhabricatorFulltextInterface,
- PhabricatorConduitResultInterface {
+ PhabricatorConduitResultInterface,
+ PhabricatorColumnProxyInterface {
protected $name;
protected $status = PhabricatorProjectStatus::STATUS_ACTIVE;
@@ -663,4 +664,25 @@
);
}
+
+/* -( PhabricatorColumnProxyInterface )------------------------------------ */
+
+
+ public function getProxyColumnName() {
+ return $this->getName();
+ }
+
+ public function getProxyColumnIcon() {
+ return $this->getDisplayIconIcon();
+ }
+
+ public function getProxyColumnClass() {
+ if ($this->isMilestone()) {
+ return 'phui-workboard-column-milestone';
+ }
+
+ return null;
+ }
+
+
}
diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php
--- a/src/applications/project/storage/PhabricatorProjectColumn.php
+++ b/src/applications/project/storage/PhabricatorProjectColumn.php
@@ -17,10 +17,12 @@
protected $name;
protected $status;
protected $projectPHID;
+ protected $proxyPHID;
protected $sequence;
protected $properties = array();
private $project = self::ATTACHABLE;
+ private $proxy = self::ATTACHABLE;
public static function initializeNewColumn(PhabricatorUser $user) {
return id(new PhabricatorProjectColumn())
@@ -38,6 +40,7 @@
'name' => 'text255',
'status' => 'uint32',
'sequence' => 'uint32',
+ 'proxyPHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_status' => array(
@@ -46,6 +49,10 @@
'key_sequence' => array(
'columns' => array('projectPHID', 'sequence'),
),
+ 'key_proxy' => array(
+ 'columns' => array('projectPHID', 'proxyPHID'),
+ 'unique' => true,
+ ),
),
) + parent::getConfiguration();
}
@@ -64,6 +71,15 @@
return $this->assertAttached($this->project);
}
+ public function attachProxy($proxy) {
+ $this->proxy = $proxy;
+ return $this;
+ }
+
+ public function getProxy() {
+ return $this->assertAttached($this->proxy);
+ }
+
public function isDefaultColumn() {
return (bool)$this->getProperty('isDefault');
}
@@ -73,6 +89,11 @@
}
public function getDisplayName() {
+ $proxy = $this->getProxy();
+ if ($proxy) {
+ return $proxy->getProxyColumnName();
+ }
+
$name = $this->getName();
if (strlen($name)) {
return $name;
@@ -96,11 +117,23 @@
return null;
}
+ public function getDisplayClass() {
+ $proxy = $this->getProxy();
+ if ($proxy) {
+ return $proxy->getProxyColumnClass();
+ }
+
+ return null;
+ }
+
public function getHeaderIcon() {
- $icon = null;
+ $proxy = $this->getProxy();
+ if ($proxy) {
+ return $proxy->getProxyColumnIcon();
+ }
if ($this->isHidden()) {
- $icon = 'fa-eye-slash';
+ return 'fa-eye-slash';
}
return null;
diff --git a/src/view/phui/PHUIWorkpanelView.php b/src/view/phui/PHUIWorkpanelView.php
--- a/src/view/phui/PHUIWorkpanelView.php
+++ b/src/view/phui/PHUIWorkpanelView.php
@@ -10,8 +10,8 @@
private $headerTag;
private $headerIcon;
- public function setHeaderIcon(PHUIIconView $header_icon) {
- $this->headerIcon = $header_icon;
+ public function setHeaderIcon($icon) {
+ $this->headerIcon = $icon;
return $this;
}
diff --git a/webroot/rsrc/js/application/projects/behavior-project-boards.js b/webroot/rsrc/js/application/projects/behavior-project-boards.js
--- a/webroot/rsrc/js/application/projects/behavior-project-boards.js
+++ b/webroot/rsrc/js/application/projects/behavior-project-boards.js
@@ -280,13 +280,16 @@
// close the dropdown, but don't want to follow the link.
e.prevent();
- var column_phid = e.getNodeData('column-add-task').columnPHID;
+ var column_data = e.getNodeData('column-add-task');
+ var column_phid = column_data.columnPHID;
+
var request_data = {
responseType: 'card',
columnPHID: column_phid,
- projects: statics.projectPHID,
+ projects: column_data.projectPHID,
order: statics.order
};
+
var cols = getcolumns();
var ii;
var column;

File Metadata

Mime Type
text/plain
Expires
Wed, Oct 23, 5:22 AM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6743627
Default Alt Text
D15171.id36627.diff (27 KB)

Event Timeline