Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13990668
D15171.id36627.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
27 KB
Referenced Files
None
Subscribers
None
D15171.id36627.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D15171: Roughly implement milestone columns on workboards
Attached
Detach File
Event Timeline
Log In to Comment