Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/maniphest/controller/ManiphestTaskDetailController.php
12262 views
1
<?php
2
3
final class ManiphestTaskDetailController extends ManiphestController {
4
5
public function shouldAllowPublic() {
6
return true;
7
}
8
9
public function handleRequest(AphrontRequest $request) {
10
$viewer = $this->getViewer();
11
$id = $request->getURIData('id');
12
13
$task = id(new ManiphestTaskQuery())
14
->setViewer($viewer)
15
->withIDs(array($id))
16
->needSubscriberPHIDs(true)
17
->executeOne();
18
if (!$task) {
19
return new Aphront404Response();
20
}
21
22
$field_list = PhabricatorCustomField::getObjectFields(
23
$task,
24
PhabricatorCustomField::ROLE_VIEW);
25
$field_list
26
->setViewer($viewer)
27
->readFieldsFromStorage($task);
28
29
$edit_engine = id(new ManiphestEditEngine())
30
->setViewer($viewer)
31
->setTargetObject($task);
32
33
$edge_types = array(
34
ManiphestTaskHasCommitEdgeType::EDGECONST,
35
ManiphestTaskHasRevisionEdgeType::EDGECONST,
36
ManiphestTaskHasMockEdgeType::EDGECONST,
37
PhabricatorObjectMentionedByObjectEdgeType::EDGECONST,
38
PhabricatorObjectMentionsObjectEdgeType::EDGECONST,
39
ManiphestTaskHasDuplicateTaskEdgeType::EDGECONST,
40
);
41
42
$phid = $task->getPHID();
43
44
$query = id(new PhabricatorEdgeQuery())
45
->withSourcePHIDs(array($phid))
46
->withEdgeTypes($edge_types);
47
$edges = idx($query->execute(), $phid);
48
$phids = array_fill_keys($query->getDestinationPHIDs(), true);
49
50
if ($task->getOwnerPHID()) {
51
$phids[$task->getOwnerPHID()] = true;
52
}
53
$phids[$task->getAuthorPHID()] = true;
54
55
$phids = array_keys($phids);
56
$handles = $viewer->loadHandles($phids);
57
58
$timeline = $this->buildTransactionTimeline(
59
$task,
60
new ManiphestTransactionQuery());
61
62
$monogram = $task->getMonogram();
63
$crumbs = $this->buildApplicationCrumbs()
64
->addTextCrumb($monogram)
65
->setBorder(true);
66
67
$header = $this->buildHeaderView($task);
68
$details = $this->buildPropertyView($task, $field_list, $edges, $handles);
69
$description = $this->buildDescriptionView($task);
70
$curtain = $this->buildCurtain($task, $edit_engine);
71
72
$title = pht('%s %s', $monogram, $task->getTitle());
73
74
$comment_view = $edit_engine
75
->buildEditEngineCommentView($task);
76
77
$timeline->setQuoteRef($monogram);
78
$comment_view->setTransactionTimeline($timeline);
79
80
$related_tabs = array();
81
$graph_menu = null;
82
83
$graph_limit = 200;
84
$overflow_message = null;
85
$task_graph = id(new ManiphestTaskGraph())
86
->setViewer($viewer)
87
->setSeedPHID($task->getPHID())
88
->setLimit($graph_limit)
89
->loadGraph();
90
if (!$task_graph->isEmpty()) {
91
$parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
92
$subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
93
$parent_map = $task_graph->getEdges($parent_type);
94
$subtask_map = $task_graph->getEdges($subtask_type);
95
$parent_list = idx($parent_map, $task->getPHID(), array());
96
$subtask_list = idx($subtask_map, $task->getPHID(), array());
97
$has_parents = (bool)$parent_list;
98
$has_subtasks = (bool)$subtask_list;
99
100
// First, get a count of direct parent tasks and subtasks. If there
101
// are too many of these, we just don't draw anything. You can use
102
// the search button to browse tasks with the search UI instead.
103
$direct_count = count($parent_list) + count($subtask_list);
104
105
if ($direct_count > $graph_limit) {
106
$overflow_message = pht(
107
'This task is directly connected to more than %s other tasks. '.
108
'Use %s to browse parents or subtasks, or %s to show more of the '.
109
'graph.',
110
new PhutilNumber($graph_limit),
111
phutil_tag('strong', array(), pht('Search...')),
112
phutil_tag('strong', array(), pht('View Standalone Graph')));
113
114
$graph_table = null;
115
} else {
116
// If there aren't too many direct tasks, but there are too many total
117
// tasks, we'll only render directly connected tasks.
118
if ($task_graph->isOverLimit()) {
119
$task_graph->setRenderOnlyAdjacentNodes(true);
120
121
$overflow_message = pht(
122
'This task is connected to more than %s other tasks. '.
123
'Only direct parents and subtasks are shown here. Use '.
124
'%s to show more of the graph.',
125
new PhutilNumber($graph_limit),
126
phutil_tag('strong', array(), pht('View Standalone Graph')));
127
}
128
129
$graph_table = $task_graph->newGraphTable();
130
}
131
132
if ($overflow_message) {
133
$overflow_view = $this->newTaskGraphOverflowView(
134
$task,
135
$overflow_message,
136
true);
137
138
$graph_table = array(
139
$overflow_view,
140
$graph_table,
141
);
142
}
143
144
$graph_menu = $this->newTaskGraphDropdownMenu(
145
$task,
146
$has_parents,
147
$has_subtasks,
148
true);
149
150
$related_tabs[] = id(new PHUITabView())
151
->setName(pht('Task Graph'))
152
->setKey('graph')
153
->appendChild($graph_table);
154
}
155
156
$related_tabs[] = $this->newMocksTab($task, $query);
157
$related_tabs[] = $this->newMentionsTab($task, $query);
158
$related_tabs[] = $this->newDuplicatesTab($task, $query);
159
160
$tab_view = null;
161
162
$related_tabs = array_filter($related_tabs);
163
if ($related_tabs) {
164
$tab_group = new PHUITabGroupView();
165
foreach ($related_tabs as $tab) {
166
$tab_group->addTab($tab);
167
}
168
169
$related_header = id(new PHUIHeaderView())
170
->setHeader(pht('Related Objects'));
171
172
if ($graph_menu) {
173
$related_header->addActionLink($graph_menu);
174
}
175
176
$tab_view = id(new PHUIObjectBoxView())
177
->setHeader($related_header)
178
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
179
->addTabGroup($tab_group);
180
}
181
182
$changes_view = $this->newChangesView($task, $edges);
183
184
$view = id(new PHUITwoColumnView())
185
->setHeader($header)
186
->setCurtain($curtain)
187
->setMainColumn(
188
array(
189
$changes_view,
190
$tab_view,
191
$timeline,
192
$comment_view,
193
))
194
->addPropertySection(pht('Description'), $description)
195
->addPropertySection(pht('Details'), $details);
196
197
198
return $this->newPage()
199
->setTitle($title)
200
->setCrumbs($crumbs)
201
->setPageObjectPHIDs(
202
array(
203
$task->getPHID(),
204
))
205
->appendChild($view);
206
207
}
208
209
private function buildHeaderView(ManiphestTask $task) {
210
$view = id(new PHUIHeaderView())
211
->setHeader($task->getTitle())
212
->setUser($this->getRequest()->getUser())
213
->setPolicyObject($task);
214
215
$priority_name = ManiphestTaskPriority::getTaskPriorityName(
216
$task->getPriority());
217
$priority_color = ManiphestTaskPriority::getTaskPriorityColor(
218
$task->getPriority());
219
220
$status = $task->getStatus();
221
$status_name = ManiphestTaskStatus::renderFullDescription(
222
$status, $priority_name);
223
$view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
224
225
$view->setHeaderIcon(ManiphestTaskStatus::getStatusIcon(
226
$task->getStatus()).' '.$priority_color);
227
228
if (ManiphestTaskPoints::getIsEnabled()) {
229
$points = $task->getPoints();
230
if ($points !== null) {
231
$points_name = pht('%s %s',
232
$task->getPoints(),
233
ManiphestTaskPoints::getPointsLabel());
234
$tag = id(new PHUITagView())
235
->setName($points_name)
236
->setColor(PHUITagView::COLOR_BLUE)
237
->setType(PHUITagView::TYPE_SHADE);
238
239
$view->addTag($tag);
240
}
241
}
242
243
$subtype = $task->newSubtypeObject();
244
if ($subtype && $subtype->hasTagView()) {
245
$subtype_tag = $subtype->newTagView();
246
$view->addTag($subtype_tag);
247
}
248
249
return $view;
250
}
251
252
253
private function buildCurtain(
254
ManiphestTask $task,
255
PhabricatorEditEngine $edit_engine) {
256
$viewer = $this->getViewer();
257
258
$id = $task->getID();
259
$phid = $task->getPHID();
260
261
$can_edit = PhabricatorPolicyFilter::hasCapability(
262
$viewer,
263
$task,
264
PhabricatorPolicyCapability::CAN_EDIT);
265
266
$can_interact = PhabricatorPolicyFilter::canInteract($viewer, $task);
267
268
// We expect a policy dialog if you can't edit the task, and expect a
269
// lock override dialog if you can't interact with it.
270
$workflow_edit = (!$can_edit || !$can_interact);
271
272
$curtain = $this->newCurtainView($task);
273
274
$curtain->addAction(
275
id(new PhabricatorActionView())
276
->setName(pht('Edit Task'))
277
->setIcon('fa-pencil')
278
->setHref($this->getApplicationURI("/task/edit/{$id}/"))
279
->setDisabled(!$can_edit)
280
->setWorkflow($workflow_edit));
281
282
$subtype_map = $task->newEditEngineSubtypeMap();
283
$subtask_options = $subtype_map->getCreateFormsForSubtype(
284
$edit_engine,
285
$task);
286
287
// If no forms are available, we want to show the user an error.
288
// If one form is available, we take them user directly to the form.
289
// If two or more forms are available, we give the user a choice.
290
291
// The "subtask" controller handles the first case (no forms) and the
292
// third case (more than one form). In the case of one form, we link
293
// directly to the form.
294
$subtask_uri = "/task/subtask/{$id}/";
295
$subtask_workflow = true;
296
297
if (count($subtask_options) == 1) {
298
$subtask_form = head($subtask_options);
299
$form_key = $subtask_form->getIdentifier();
300
$subtask_uri = id(new PhutilURI("/task/edit/form/{$form_key}/"))
301
->replaceQueryParam('parent', $id)
302
->replaceQueryParam('template', $id)
303
->replaceQueryParam('status', ManiphestTaskStatus::getDefaultStatus());
304
$subtask_workflow = false;
305
}
306
307
$subtask_uri = $this->getApplicationURI($subtask_uri);
308
309
$subtask_item = id(new PhabricatorActionView())
310
->setName(pht('Create Subtask'))
311
->setHref($subtask_uri)
312
->setIcon('fa-level-down')
313
->setDisabled(!$subtask_options)
314
->setWorkflow($subtask_workflow);
315
316
$relationship_list = PhabricatorObjectRelationshipList::newForObject(
317
$viewer,
318
$task);
319
320
$submenu_actions = array(
321
$subtask_item,
322
ManiphestTaskHasParentRelationship::RELATIONSHIPKEY,
323
ManiphestTaskHasSubtaskRelationship::RELATIONSHIPKEY,
324
ManiphestTaskMergeInRelationship::RELATIONSHIPKEY,
325
ManiphestTaskCloseAsDuplicateRelationship::RELATIONSHIPKEY,
326
);
327
328
$task_submenu = $relationship_list->newActionSubmenu($submenu_actions)
329
->setName(pht('Edit Related Tasks...'))
330
->setIcon('fa-anchor');
331
332
$curtain->addAction($task_submenu);
333
334
$relationship_submenu = $relationship_list->newActionMenu();
335
if ($relationship_submenu) {
336
$curtain->addAction($relationship_submenu);
337
}
338
339
$viewer_phid = $viewer->getPHID();
340
$owner_phid = $task->getOwnerPHID();
341
$author_phid = $task->getAuthorPHID();
342
$handles = $viewer->loadHandles(array($owner_phid, $author_phid));
343
344
$assigned_refs = id(new PHUICurtainObjectRefListView())
345
->setViewer($viewer)
346
->setEmptyMessage(pht('None'));
347
348
if ($owner_phid) {
349
$assigned_ref = $assigned_refs->newObjectRefView()
350
->setHandle($handles[$owner_phid])
351
->setHighlighted($owner_phid === $viewer_phid);
352
}
353
354
$curtain->newPanel()
355
->setHeaderText(pht('Assigned To'))
356
->appendChild($assigned_refs);
357
358
$author_refs = id(new PHUICurtainObjectRefListView())
359
->setViewer($viewer);
360
361
$author_ref = $author_refs->newObjectRefView()
362
->setHandle($handles[$author_phid])
363
->setEpoch($task->getDateCreated())
364
->setHighlighted($author_phid === $viewer_phid);
365
366
$curtain->newPanel()
367
->setHeaderText(pht('Authored By'))
368
->appendChild($author_refs);
369
370
return $curtain;
371
}
372
373
private function buildPropertyView(
374
ManiphestTask $task,
375
PhabricatorCustomFieldList $field_list,
376
array $edges,
377
$handles) {
378
379
$viewer = $this->getRequest()->getUser();
380
$view = id(new PHUIPropertyListView())
381
->setUser($viewer);
382
383
$source = $task->getOriginalEmailSource();
384
if ($source) {
385
$subject = '[T'.$task->getID().'] '.$task->getTitle();
386
$view->addProperty(
387
pht('From Email'),
388
phutil_tag(
389
'a',
390
array(
391
'href' => 'mailto:'.$source.'?subject='.$subject,
392
),
393
$source));
394
}
395
396
$field_list->appendFieldsToPropertyList(
397
$task,
398
$viewer,
399
$view);
400
401
if ($view->hasAnyProperties()) {
402
return $view;
403
}
404
405
return null;
406
}
407
408
private function buildDescriptionView(ManiphestTask $task) {
409
$viewer = $this->getViewer();
410
411
$section = null;
412
413
$description = $task->getDescription();
414
if (strlen($description)) {
415
$section = new PHUIPropertyListView();
416
$section->addTextContent(
417
phutil_tag(
418
'div',
419
array(
420
'class' => 'phabricator-remarkup',
421
),
422
id(new PHUIRemarkupView($viewer, $description))
423
->setContextObject($task)));
424
}
425
426
return $section;
427
}
428
429
private function newMocksTab(
430
ManiphestTask $task,
431
PhabricatorEdgeQuery $edge_query) {
432
433
$mock_type = ManiphestTaskHasMockEdgeType::EDGECONST;
434
$mock_phids = $edge_query->getDestinationPHIDs(array(), array($mock_type));
435
if (!$mock_phids) {
436
return null;
437
}
438
439
$viewer = $this->getViewer();
440
$handles = $viewer->loadHandles($mock_phids);
441
442
// TODO: It would be nice to render this as pinboard-style thumbnails,
443
// similar to "{M123}", instead of a list of links.
444
445
$view = id(new PHUIPropertyListView())
446
->addProperty(pht('Mocks'), $handles->renderList());
447
448
return id(new PHUITabView())
449
->setName(pht('Mocks'))
450
->setKey('mocks')
451
->appendChild($view);
452
}
453
454
private function newMentionsTab(
455
ManiphestTask $task,
456
PhabricatorEdgeQuery $edge_query) {
457
458
$in_type = PhabricatorObjectMentionedByObjectEdgeType::EDGECONST;
459
$out_type = PhabricatorObjectMentionsObjectEdgeType::EDGECONST;
460
461
$in_phids = $edge_query->getDestinationPHIDs(array(), array($in_type));
462
$out_phids = $edge_query->getDestinationPHIDs(array(), array($out_type));
463
464
// Filter out any mentioned users from the list. These are not generally
465
// very interesting to show in a relationship summary since they usually
466
// end up as subscribers anyway.
467
468
$user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
469
foreach ($out_phids as $key => $out_phid) {
470
if (phid_get_type($out_phid) == $user_type) {
471
unset($out_phids[$key]);
472
}
473
}
474
475
if (!$in_phids && !$out_phids) {
476
return null;
477
}
478
479
$viewer = $this->getViewer();
480
$in_handles = $viewer->loadHandles($in_phids);
481
$out_handles = $viewer->loadHandles($out_phids);
482
483
$in_handles = $this->getCompleteHandles($in_handles);
484
$out_handles = $this->getCompleteHandles($out_handles);
485
486
if (!count($in_handles) && !count($out_handles)) {
487
return null;
488
}
489
490
$view = new PHUIPropertyListView();
491
492
if (count($in_handles)) {
493
$view->addProperty(pht('Mentioned In'), $in_handles->renderList());
494
}
495
496
if (count($out_handles)) {
497
$view->addProperty(pht('Mentioned Here'), $out_handles->renderList());
498
}
499
500
return id(new PHUITabView())
501
->setName(pht('Mentions'))
502
->setKey('mentions')
503
->appendChild($view);
504
}
505
506
private function newDuplicatesTab(
507
ManiphestTask $task,
508
PhabricatorEdgeQuery $edge_query) {
509
510
$in_type = ManiphestTaskHasDuplicateTaskEdgeType::EDGECONST;
511
$in_phids = $edge_query->getDestinationPHIDs(array(), array($in_type));
512
513
$viewer = $this->getViewer();
514
$in_handles = $viewer->loadHandles($in_phids);
515
$in_handles = $this->getCompleteHandles($in_handles);
516
517
$view = new PHUIPropertyListView();
518
519
if (!count($in_handles)) {
520
return null;
521
}
522
523
$view->addProperty(
524
pht('Duplicates Merged Here'), $in_handles->renderList());
525
526
return id(new PHUITabView())
527
->setName(pht('Duplicates'))
528
->setKey('duplicates')
529
->appendChild($view);
530
}
531
532
private function getCompleteHandles(PhabricatorHandleList $handles) {
533
$phids = array();
534
535
foreach ($handles as $phid => $handle) {
536
if (!$handle->isComplete()) {
537
continue;
538
}
539
$phids[] = $phid;
540
}
541
542
return $handles->newSublist($phids);
543
}
544
545
private function newChangesView(ManiphestTask $task, array $edges) {
546
$viewer = $this->getViewer();
547
548
$revision_type = ManiphestTaskHasRevisionEdgeType::EDGECONST;
549
$commit_type = ManiphestTaskHasCommitEdgeType::EDGECONST;
550
551
$revision_phids = idx($edges, $revision_type, array());
552
$revision_phids = array_keys($revision_phids);
553
$revision_phids = array_fuse($revision_phids);
554
555
$commit_phids = idx($edges, $commit_type, array());
556
$commit_phids = array_keys($commit_phids);
557
$commit_phids = array_fuse($commit_phids);
558
559
if (!$revision_phids && !$commit_phids) {
560
return null;
561
}
562
563
if ($commit_phids) {
564
$link_type = DiffusionCommitHasRevisionEdgeType::EDGECONST;
565
$link_query = id(new PhabricatorEdgeQuery())
566
->withSourcePHIDs($commit_phids)
567
->withEdgeTypes(array($link_type));
568
$link_query->execute();
569
570
$commits = id(new DiffusionCommitQuery())
571
->setViewer($viewer)
572
->withPHIDs($commit_phids)
573
->execute();
574
$commits = mpull($commits, null, 'getPHID');
575
} else {
576
$commits = array();
577
}
578
579
if ($revision_phids) {
580
$revisions = id(new DifferentialRevisionQuery())
581
->setViewer($viewer)
582
->withPHIDs($revision_phids)
583
->execute();
584
$revisions = mpull($revisions, null, 'getPHID');
585
} else {
586
$revisions = array();
587
}
588
589
$handle_phids = array();
590
$any_linked = false;
591
$any_status = false;
592
593
$idx = 0;
594
$objects = array();
595
foreach ($commit_phids as $commit_phid) {
596
$handle_phids[] = $commit_phid;
597
598
$link_phids = $link_query->getDestinationPHIDs(array($commit_phid));
599
foreach ($link_phids as $link_phid) {
600
$handle_phids[] = $link_phid;
601
unset($revision_phids[$link_phid]);
602
$any_linked = true;
603
}
604
605
$commit = idx($commits, $commit_phid);
606
if ($commit) {
607
$repository_phid = $commit->getRepository()->getPHID();
608
$handle_phids[] = $repository_phid;
609
} else {
610
$repository_phid = null;
611
}
612
613
$status_view = null;
614
if ($commit) {
615
$status = $commit->getAuditStatusObject();
616
if (!$status->isNoAudit()) {
617
$status_view = id(new PHUITagView())
618
->setType(PHUITagView::TYPE_SHADE)
619
->setIcon($status->getIcon())
620
->setColor($status->getColor())
621
->setName($status->getName());
622
}
623
}
624
625
$object_link = null;
626
if ($commit) {
627
$commit_monogram = $commit->getDisplayName();
628
$commit_monogram = phutil_tag(
629
'span',
630
array(
631
'class' => 'object-name',
632
),
633
$commit_monogram);
634
635
$commit_link = javelin_tag(
636
'a',
637
array(
638
'href' => $commit->getURI(),
639
'sigil' => 'hovercard',
640
'meta' => array(
641
'hovercardSpec' => array(
642
'objectPHID' => $commit->getPHID(),
643
),
644
),
645
),
646
$commit->getSummary());
647
648
$object_link = array(
649
$commit_monogram,
650
' ',
651
$commit_link,
652
);
653
}
654
655
$objects[] = array(
656
'objectPHID' => $commit_phid,
657
'objectLink' => $object_link,
658
'repositoryPHID' => $repository_phid,
659
'revisionPHIDs' => $link_phids,
660
'status' => $status_view,
661
'order' => id(new PhutilSortVector())
662
->addInt($repository_phid ? 1 : 0)
663
->addString((string)$repository_phid)
664
->addInt(1)
665
->addInt($idx++),
666
);
667
}
668
669
foreach ($revision_phids as $revision_phid) {
670
$handle_phids[] = $revision_phid;
671
672
$revision = idx($revisions, $revision_phid);
673
if ($revision) {
674
$repository_phid = $revision->getRepositoryPHID();
675
$handle_phids[] = $repository_phid;
676
} else {
677
$repository_phid = null;
678
}
679
680
if ($revision) {
681
$icon = $revision->getStatusIcon();
682
$color = $revision->getStatusIconColor();
683
$name = $revision->getStatusDisplayName();
684
685
$status_view = id(new PHUITagView())
686
->setType(PHUITagView::TYPE_SHADE)
687
->setIcon($icon)
688
->setColor($color)
689
->setName($name);
690
} else {
691
$status_view = null;
692
}
693
694
$object_link = null;
695
if ($revision) {
696
$revision_monogram = $revision->getMonogram();
697
$revision_monogram = phutil_tag(
698
'span',
699
array(
700
'class' => 'object-name',
701
),
702
$revision_monogram);
703
704
$revision_link = javelin_tag(
705
'a',
706
array(
707
'href' => $revision->getURI(),
708
'sigil' => 'hovercard',
709
'meta' => array(
710
'hovercardSpec' => array(
711
'objectPHID' => $revision->getPHID(),
712
),
713
),
714
),
715
$revision->getTitle());
716
717
$object_link = array(
718
$revision_monogram,
719
' ',
720
$revision_link,
721
);
722
}
723
724
$objects[] = array(
725
'objectPHID' => $revision_phid,
726
'objectLink' => $object_link,
727
'repositoryPHID' => $repository_phid,
728
'revisionPHIDs' => array(),
729
'status' => $status_view,
730
'order' => id(new PhutilSortVector())
731
->addInt($repository_phid ? 1 : 0)
732
->addString((string)$repository_phid)
733
->addInt(0)
734
->addInt($idx++),
735
);
736
}
737
738
$handles = $viewer->loadHandles($handle_phids);
739
740
$order = ipull($objects, 'order');
741
$order = msortv($order, 'getSelf');
742
$objects = array_select_keys($objects, array_keys($order));
743
744
$last_repository = false;
745
$rows = array();
746
$rowd = array();
747
foreach ($objects as $object) {
748
$repository_phid = $object['repositoryPHID'];
749
if ($repository_phid !== $last_repository) {
750
$repository_link = null;
751
if ($repository_phid) {
752
$repository_handle = $handles[$repository_phid];
753
$rows[] = array(
754
$repository_handle->renderLink(),
755
);
756
$rowd[] = true;
757
}
758
759
$last_repository = $repository_phid;
760
}
761
762
$object_phid = $object['objectPHID'];
763
$handle = $handles[$object_phid];
764
765
$object_link = $object['objectLink'];
766
if ($object_link === null) {
767
$object_link = $handle->renderLink();
768
}
769
770
$object_icon = id(new PHUIIconView())
771
->setIcon($handle->getIcon());
772
773
$status_view = $object['status'];
774
if ($status_view) {
775
$any_status = true;
776
}
777
778
$revision_tags = array();
779
foreach ($object['revisionPHIDs'] as $link_phid) {
780
$revision_handle = $handles[$link_phid];
781
782
$revision_name = $revision_handle->getName();
783
$revision_tags[] = $revision_handle
784
->renderHovercardLink($revision_name);
785
}
786
$revision_tags = phutil_implode_html(
787
phutil_tag('br'),
788
$revision_tags);
789
790
$rowd[] = false;
791
$rows[] = array(
792
$object_icon,
793
$status_view,
794
$revision_tags,
795
$object_link,
796
);
797
}
798
799
$changes_table = id(new AphrontTableView($rows))
800
->setNoDataString(pht('This task has no related commits or revisions.'))
801
->setRowDividers($rowd)
802
->setColumnClasses(
803
array(
804
'indent center',
805
null,
806
null,
807
'wide pri object-link',
808
))
809
->setColumnVisibility(
810
array(
811
true,
812
$any_status,
813
$any_linked,
814
true,
815
))
816
->setDeviceVisibility(
817
array(
818
false,
819
$any_status,
820
false,
821
true,
822
));
823
824
$changes_header = id(new PHUIHeaderView())
825
->setHeader(pht('Revisions and Commits'));
826
827
$changes_view = id(new PHUIObjectBoxView())
828
->setHeader($changes_header)
829
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
830
->setTable($changes_table);
831
832
return $changes_view;
833
}
834
835
836
}
837
838