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

Merge branch 'master' into redesign-2015

+664 -671
+1 -11
resources/celerity/map.php
··· 10 10 'core.pkg.css' => '7f0d6232', 11 11 'core.pkg.js' => 'a590b451', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 - 'differential.pkg.css' => 'fe951924', 13 + 'differential.pkg.css' => '27498de3', 14 14 'differential.pkg.js' => 'ebef29b1', 15 15 'diffusion.pkg.css' => '385e85b3', 16 16 'diffusion.pkg.js' => '0115b37c', ··· 61 61 'rsrc/css/application/differential/changeset-view.css' => '9b8e8bb7', 62 62 'rsrc/css/application/differential/core.css' => '7ac3cabc', 63 63 'rsrc/css/application/differential/phui-inline-comment.css' => 'fa5b8d1f', 64 - 'rsrc/css/application/differential/results-table.css' => '181aa9d9', 65 64 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', 66 65 'rsrc/css/application/differential/revision-history.css' => '0e8eb855', 67 66 'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', ··· 357 356 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '037b59eb', 358 357 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492', 359 358 'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', 360 - 'rsrc/js/application/differential/behavior-show-field-details.js' => 'bba9eedf', 361 359 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 362 360 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 363 361 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'b42eddc7', ··· 511 509 'differential-changeset-view-css' => '9b8e8bb7', 512 510 'differential-core-view-css' => '7ac3cabc', 513 511 'differential-inline-comment-editor' => 'd4c87bf4', 514 - 'differential-results-table-css' => '181aa9d9', 515 512 'differential-revision-add-comment-css' => 'c47f8c40', 516 513 'differential-revision-comment-css' => '14b8565a', 517 514 'differential-revision-history-css' => '0e8eb855', ··· 565 562 'javelin-behavior-differential-feedback-preview' => 'b064af76', 566 563 'javelin-behavior-differential-keyboard-navigation' => '2c426492', 567 564 'javelin-behavior-differential-populate' => '8694b1df', 568 - 'javelin-behavior-differential-show-field-details' => 'bba9eedf', 569 565 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 570 566 'javelin-behavior-differential-user-select' => 'a8d8459d', 571 567 'javelin-behavior-diffusion-commit-branches' => 'bdaf4d04', ··· 1711 1707 'javelin-workflow', 1712 1708 'phabricator-draggable-list', 1713 1709 ), 1714 - 'bba9eedf' => array( 1715 - 'javelin-behavior', 1716 - 'javelin-stratcom', 1717 - 'javelin-dom', 1718 - ), 1719 1710 'bd4c8dca' => array( 1720 1711 'javelin-install', 1721 1712 'javelin-util', ··· 2192 2183 'differential.pkg.css' => array( 2193 2184 'differential-core-view-css', 2194 2185 'differential-changeset-view-css', 2195 - 'differential-results-table-css', 2196 2186 'differential-revision-history-css', 2197 2187 'differential-revision-list-css', 2198 2188 'differential-table-of-contents-css',
-1
resources/celerity/packages.php
··· 141 141 'differential.pkg.css' => array( 142 142 'differential-core-view-css', 143 143 'differential-changeset-view-css', 144 - 'differential-results-table-css', 145 144 'differential-revision-history-css', 146 145 'differential-revision-list-css', 147 146 'differential-table-of-contents-css',
+5 -2
src/__phutil_library_map__.php
··· 419 419 'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', 420 420 'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php', 421 421 'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php', 422 - 'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php', 423 422 'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', 424 423 'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', 425 424 'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', ··· 894 893 'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php', 895 894 'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php', 896 895 'HarbormasterLeaseHostBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php', 896 + 'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php', 897 897 'HarbormasterManagePlansCapability' => 'applications/harbormaster/capability/HarbormasterManagePlansCapability.php', 898 898 'HarbormasterManagementBuildWorkflow' => 'applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php', 899 899 'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php', ··· 921 921 'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php', 922 922 'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php', 923 923 'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php', 924 + 'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php', 924 925 'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php', 925 926 'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php', 926 927 'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php', ··· 3789 3790 'DifferentialRepositoryField' => 'DifferentialCoreCustomField', 3790 3791 'DifferentialRepositoryLookup' => 'Phobject', 3791 3792 'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField', 3792 - 'DifferentialResultsTableView' => 'AphrontView', 3793 3793 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 3794 3794 'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 3795 3795 'DifferentialReviewer' => 'Phobject', ··· 4356 4356 'HarbormasterDAO' => 'PhabricatorLiskDAO', 4357 4357 'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4358 4358 'HarbormasterLeaseHostBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4359 + 'HarbormasterLintPropertyView' => 'AphrontView', 4359 4360 'HarbormasterManagePlansCapability' => 'PhabricatorPolicyCapability', 4360 4361 'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow', 4361 4362 'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow', ··· 4383 4384 'HarbormasterTargetWorker' => 'HarbormasterWorker', 4384 4385 'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation', 4385 4386 'HarbormasterUIEventListener' => 'PhabricatorEventListener', 4387 + 'HarbormasterUnitPropertyView' => 'AphrontView', 4386 4388 'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4387 4389 'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4388 4390 'HarbormasterWorker' => 'PhabricatorWorker', ··· 6730 6732 'PhabricatorProjectInterface', 6731 6733 'PhabricatorDestructibleInterface', 6732 6734 'PhabricatorSpacesInterface', 6735 + 'PhabricatorMentionableInterface', 6733 6736 ), 6734 6737 'PholioMockCommentController' => 'PholioController', 6735 6738 'PholioMockEditController' => 'PholioController',
+40 -3
src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
··· 264 264 $list = new PHUIObjectItemListView(); 265 265 266 266 foreach ($events as $event) { 267 - $from = phabricator_datetime($event->getDateFrom(), $viewer); 268 267 $duration = ''; 268 + $event_date_info = $this->getEventDateLabel($event); 269 269 $creator_handle = $handles[$event->getUserPHID()]; 270 - 271 270 $attendees = array(); 271 + 272 272 foreach ($event->getInvitees() as $invitee) { 273 273 $attendees[] = $invitee->getInviteePHID(); 274 274 } ··· 287 287 288 288 $item = id(new PHUIObjectItemView()) 289 289 ->setHeader($viewer->renderHandle($event->getPHID())->render()) 290 + ->addAttribute($event_date_info) 290 291 ->addAttribute($attendees) 291 - ->addIcon('none', $from) 292 292 ->addIcon('none', $duration); 293 293 294 294 $list->addItem($item); ··· 519 519 } 520 520 521 521 return false; 522 + } 523 + 524 + private function getEventDateLabel($event) { 525 + $viewer = $this->requireViewer(); 526 + 527 + $from_datetime = PhabricatorTime::getDateTimeFromEpoch( 528 + $event->getDateFrom(), 529 + $viewer); 530 + $to_datetime = PhabricatorTime::getDateTimeFromEpoch( 531 + $event->getDateTo(), 532 + $viewer); 533 + 534 + $from_date_formatted = $from_datetime->format('Y m d'); 535 + $to_date_formatted = $to_datetime->format('Y m d'); 536 + 537 + if ($event->getIsAllDay()) { 538 + if ($from_date_formatted == $to_date_formatted) { 539 + return pht( 540 + '%s, All Day', 541 + phabricator_date($event->getDateFrom(), $viewer)); 542 + } else { 543 + return pht( 544 + '%s - %s, All Day', 545 + phabricator_date($event->getDateFrom(), $viewer), 546 + phabricator_date($event->getDateTo(), $viewer)); 547 + } 548 + } else if ($from_date_formatted == $to_date_formatted) { 549 + return pht( 550 + '%s - %s', 551 + phabricator_datetime($event->getDateFrom(), $viewer), 552 + phabricator_time($event->getDateTo(), $viewer)); 553 + } else { 554 + return pht( 555 + '%s - %s', 556 + phabricator_datetime($event->getDateFrom(), $viewer), 557 + phabricator_datetime($event->getDateTo(), $viewer)); 558 + } 522 559 } 523 560 }
+2 -1
src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php
··· 160 160 161 161 return array( 162 162 'diffid' => $diff->getID(), 163 - 'uri' => $uri, 163 + 'phid' => $diff->getPHID(), 164 + 'uri' => $uri, 164 165 ); 165 166 } 166 167
+18 -1
src/applications/differential/controller/DifferentialRevisionViewController.php
··· 265 265 $revision_detail_box->setInfoView($revision_warnings); 266 266 } 267 267 268 + $detail_diffs = array_select_keys( 269 + $diffs, 270 + array($diff_vs, $target->getID())); 271 + $detail_diffs = mpull($detail_diffs, null, 'getPHID'); 272 + 273 + $buildables = id(new HarbormasterBuildableQuery()) 274 + ->setViewer($user) 275 + ->withBuildablePHIDs(array_keys($detail_diffs)) 276 + ->withManualBuildables(false) 277 + ->needBuilds(true) 278 + ->needTargets(true) 279 + ->execute(); 280 + $buildables = mpull($buildables, null, 'getBuildablePHID'); 281 + foreach ($detail_diffs as $diff_phid => $detail_diff) { 282 + $detail_diff->attachBuildable(idx($buildables, $diff_phid)); 283 + } 284 + 268 285 $diff_detail_box = $this->buildDiffDetailView( 269 - array_select_keys($diffs, array($diff_vs, $target->getID())), 286 + $detail_diffs, 270 287 $revision, 271 288 $field_list); 272 289
+96 -187
src/applications/differential/customfield/DifferentialLintField.php
··· 38 38 $keys = array( 39 39 'arc:lint', 40 40 'arc:lint-excuse', 41 - 'arc:lint-postponed', 42 41 ); 43 42 44 43 $properties = id(new DifferentialDiffProperty())->loadAllWhere( ··· 51 50 $diff->attachProperty($key, idx($properties, $key)); 52 51 } 53 52 54 - $path_changesets = mpull($diff->loadChangesets(), 'getID', 'getFilename'); 55 - 56 - $lstar = DifferentialRevisionUpdateHistoryView::renderDiffLintStar($diff); 57 - $lmsg = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); 58 - $ldata = $diff->getProperty('arc:lint'); 59 - $ltail = null; 53 + $status = $this->renderLintStatus($diff); 60 54 61 - $rows = array(); 55 + $lint = array(); 62 56 63 - $rows[] = array( 64 - 'style' => 'star', 65 - 'name' => $lstar, 66 - 'value' => $lmsg, 67 - 'show' => true, 68 - ); 57 + $buildable = $diff->getBuildable(); 58 + if ($buildable) { 59 + $target_phids = array(); 60 + foreach ($buildable->getBuilds() as $build) { 61 + foreach ($build->getBuildTargets() as $target) { 62 + $target_phids[] = $target->getPHID(); 63 + } 64 + } 69 65 70 - $excuse = $diff->getProperty('arc:lint-excuse'); 71 - if ($excuse) { 72 - $rows[] = array( 73 - 'style' => 'excuse', 74 - 'name' => 'Excuse', 75 - 'value' => phutil_escape_html_newlines($excuse), 76 - 'show' => true, 77 - ); 66 + $lint = id(new HarbormasterBuildLintMessage())->loadAllWhere( 67 + 'buildTargetPHID IN (%Ls) LIMIT 25', 68 + $target_phids); 78 69 } 79 70 80 - $show_limit = 10; 81 - $hidden = array(); 82 - 83 - if ($ldata) { 84 - $ldata = igroup($ldata, 'path'); 85 - foreach ($ldata as $path => $messages) { 86 - 87 - $rows[] = array( 88 - 'style' => 'section', 89 - 'name' => $path, 90 - 'show' => $show_limit, 91 - ); 92 - 93 - foreach ($messages as $message) { 94 - $path = idx($message, 'path'); 95 - $line = idx($message, 'line'); 96 - 97 - $code = idx($message, 'code'); 98 - $severity = idx($message, 'severity'); 99 - 100 - $name = idx($message, 'name'); 101 - $description = idx($message, 'description'); 102 - 103 - $line_link = pht('line %d', intval($line)); 104 - if (isset($path_changesets[$path])) { 105 - $href = '#C'.$path_changesets[$path].'NL'.max(1, $line); 106 - 107 - // TODO: We are always showing the active diff 108 - // if ($diff->getID() != $this->getDiff()->getID()) { 109 - // $href = '/D'.$diff->getRevisionID().'?id='.$diff->getID().$href; 110 - // } 111 - 112 - $line_link = phutil_tag( 113 - 'a', 114 - array( 115 - 'href' => $href, 116 - ), 117 - $line_link); 118 - } 71 + if (!$lint) { 72 + // No Harbormaster messages, so look for legacy messages and make them 73 + // look like modern messages. 74 + $legacy_lint = $diff->getProperty('arc:lint'); 75 + if ($legacy_lint) { 76 + // Show the top 100 legacy lint messages. Previously, we showed some 77 + // by default and let the user toggle the rest. With modern messages, 78 + // we can send the user to the Harbormaster detail page. Just show 79 + // "a lot" of messages in legacy cases to try to strike a balance 80 + // between implementation simplicitly and compatibility. 81 + $legacy_lint = array_slice($legacy_lint, 0, 100); 119 82 120 - if ($show_limit) { 121 - --$show_limit; 122 - $show = true; 123 - } else { 124 - $show = false; 125 - if (empty($hidden[$severity])) { 126 - $hidden[$severity] = 0; 127 - } 128 - $hidden[$severity]++; 129 - } 130 - 131 - $rows[] = array( 132 - 'style' => $this->getSeverityStyle($severity), 133 - 'name' => ucwords($severity), 134 - 'value' => hsprintf( 135 - '(%s) %s at %s', 136 - $code, 137 - $name, 138 - $line_link), 139 - 'show' => $show, 140 - ); 141 - 142 - if (!empty($message['locations'])) { 143 - $locations = array(); 144 - foreach ($message['locations'] as $location) { 145 - $other_line = idx($location, 'line'); 146 - $locations[] = 147 - idx($location, 'path', $path). 148 - ($other_line ? ":{$other_line}" : ''); 149 - } 150 - $description .= "\n".pht( 151 - 'Other locations: %s', 152 - implode(', ', $locations)); 153 - } 154 - 155 - if (strlen($description)) { 156 - $rows[] = array( 157 - 'style' => 'details', 158 - 'value' => phutil_escape_html_newlines($description), 159 - 'show' => false, 160 - ); 161 - if (empty($hidden['details'])) { 162 - $hidden['details'] = 0; 163 - } 164 - $hidden['details']++; 83 + $target = new HarbormasterBuildTarget(); 84 + foreach ($legacy_lint as $message) { 85 + try { 86 + $modern = HarbormasterBuildLintMessage::newFromDictionary( 87 + $target, 88 + $this->getModernLintMessageDictionary($message)); 89 + $lint[] = $modern; 90 + } catch (Exception $ex) { 91 + // Ignore any poorly formatted messages. 165 92 } 166 93 } 167 94 } 168 95 } 169 96 170 - $postponed = $diff->getProperty('arc:lint-postponed'); 171 - if ($postponed) { 172 - foreach ($postponed as $linter) { 173 - $rows[] = array( 174 - 'style' => $this->getPostponedStyle(), 175 - 'name' => 'Postponed', 176 - 'value' => $linter, 177 - 'show' => false, 178 - ); 179 - if (empty($hidden['postponed'])) { 180 - $hidden['postponed'] = 0; 181 - } 182 - $hidden['postponed']++; 183 - } 184 - } 97 + if ($lint) { 98 + $path_map = mpull($diff->loadChangesets(), 'getID', 'getFilename'); 99 + foreach ($path_map as $path => $id) { 100 + $href = '#C'.$id.'NL'; 185 101 186 - $show_string = $this->renderShowString($hidden); 102 + // TODO: When the diff is not the right-hand-size diff, we should 103 + // ideally adjust this URI to be absolute. 187 104 188 - $view = new DifferentialResultsTableView(); 189 - $view->setRows($rows); 190 - $view->setShowMoreString($show_string); 105 + $path_map[$path] = $href; 106 + } 191 107 192 - return $view->render(); 193 - } 108 + $view = id(new HarbormasterLintPropertyView()) 109 + ->setPathURIMap($path_map) 110 + ->setLintMessages($lint); 111 + } else { 112 + $view = null; 113 + } 194 114 195 - private function getSeverityStyle($severity) { 196 - $map = array( 197 - ArcanistLintSeverity::SEVERITY_ERROR => 'red', 198 - ArcanistLintSeverity::SEVERITY_WARNING => 'yellow', 199 - ArcanistLintSeverity::SEVERITY_AUTOFIX => 'yellow', 200 - ArcanistLintSeverity::SEVERITY_ADVICE => 'yellow', 115 + return array( 116 + $status, 117 + $view, 201 118 ); 202 - return idx($map, $severity); 203 - } 204 - 205 - private function getPostponedStyle() { 206 - return 'blue'; 207 - } 208 - 209 - private function renderShowString(array $hidden) { 210 - if (!$hidden) { 211 - return null; 212 - } 213 - 214 - // Reorder hidden things by severity. 215 - $hidden = array_select_keys( 216 - $hidden, 217 - array( 218 - ArcanistLintSeverity::SEVERITY_ERROR, 219 - ArcanistLintSeverity::SEVERITY_WARNING, 220 - ArcanistLintSeverity::SEVERITY_AUTOFIX, 221 - ArcanistLintSeverity::SEVERITY_ADVICE, 222 - 'details', 223 - 'postponed', 224 - )) + $hidden; 225 - 226 - $show = array(); 227 - foreach ($hidden as $key => $value) { 228 - switch ($key) { 229 - case ArcanistLintSeverity::SEVERITY_ERROR: 230 - $show[] = pht('%d Error(s)', $value); 231 - break; 232 - case ArcanistLintSeverity::SEVERITY_WARNING: 233 - $show[] = pht('%d Warning(s)', $value); 234 - break; 235 - case ArcanistLintSeverity::SEVERITY_AUTOFIX: 236 - $show[] = pht('%d Auto-Fix(es)', $value); 237 - break; 238 - case ArcanistLintSeverity::SEVERITY_ADVICE: 239 - $show[] = pht('%d Advice(s)', $value); 240 - break; 241 - case 'details': 242 - $show[] = pht('%d Detail(s)', $value); 243 - break; 244 - case 'postponed': 245 - $show[] = pht('%d Postponed', $value); 246 - break; 247 - default: 248 - $show[] = $value; 249 - break; 250 - } 251 - } 252 - 253 - return pht( 254 - 'Show Full Lint Results (%s)', 255 - implode(', ', $show)); 256 119 } 257 120 258 121 public function getWarningsForDetailView() { ··· 277 140 278 141 return $warnings; 279 142 } 143 + 144 + private function renderLintStatus(DifferentialDiff $diff) { 145 + $colors = array( 146 + DifferentialLintStatus::LINT_NONE => 'grey', 147 + DifferentialLintStatus::LINT_OKAY => 'green', 148 + DifferentialLintStatus::LINT_WARN => 'yellow', 149 + DifferentialLintStatus::LINT_FAIL => 'red', 150 + DifferentialLintStatus::LINT_SKIP => 'blue', 151 + DifferentialLintStatus::LINT_AUTO_SKIP => 'blue', 152 + DifferentialLintStatus::LINT_POSTPONED => 'blue', 153 + ); 154 + $icon_color = idx($colors, $diff->getLintStatus(), 'grey'); 155 + 156 + $message = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); 157 + 158 + $excuse = $diff->getProperty('arc:lint-excuse'); 159 + if (strlen($excuse)) { 160 + $excuse = array( 161 + phutil_tag('strong', array(), pht('Excuse:')), 162 + ' ', 163 + phutil_escape_html_newlines($excuse), 164 + ); 165 + } 166 + 167 + $status = id(new PHUIStatusListView()) 168 + ->addItem( 169 + id(new PHUIStatusItemView()) 170 + ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) 171 + ->setTarget($message) 172 + ->setNote($excuse)); 173 + 174 + return $status; 175 + } 176 + 177 + private function getModernLintMessageDictionary(array $map) { 178 + // Strip out `null` values to satisfy stricter typechecks. 179 + foreach ($map as $key => $value) { 180 + if ($value === null) { 181 + unset($map[$key]); 182 + } 183 + } 184 + 185 + // TODO: We might need to remap some stuff here? 186 + return $map; 187 + } 188 + 280 189 281 190 }
+90 -158
src/applications/differential/customfield/DifferentialUnitField.php
··· 48 48 $diff->attachProperty($key, idx($properties, $key)); 49 49 } 50 50 51 - $ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff); 52 - $umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); 53 - 54 - $rows = array(); 55 - 56 - $rows[] = array( 57 - 'style' => 'star', 58 - 'name' => $ustar, 59 - 'value' => $umsg, 60 - 'show' => true, 61 - ); 51 + $status = $this->renderUnitStatus($diff); 62 52 63 - $excuse = $diff->getProperty('arc:unit-excuse'); 64 - if ($excuse) { 65 - $rows[] = array( 66 - 'style' => 'excuse', 67 - 'name' => pht('Excuse'), 68 - 'value' => phutil_escape_html_newlines($excuse), 69 - 'show' => true, 70 - ); 71 - } 72 - 73 - $show_limit = 10; 74 - $hidden = array(); 75 - 76 - $udata = $diff->getProperty('arc:unit'); 77 - if ($udata) { 78 - $sort_map = array( 79 - ArcanistUnitTestResult::RESULT_BROKEN => 0, 80 - ArcanistUnitTestResult::RESULT_FAIL => 1, 81 - ArcanistUnitTestResult::RESULT_UNSOUND => 2, 82 - ArcanistUnitTestResult::RESULT_SKIP => 3, 83 - ArcanistUnitTestResult::RESULT_POSTPONED => 4, 84 - ArcanistUnitTestResult::RESULT_PASS => 5, 85 - ); 53 + $unit = array(); 86 54 87 - foreach ($udata as $key => $test) { 88 - $udata[$key]['sort'] = idx($sort_map, idx($test, 'result')); 89 - } 90 - $udata = isort($udata, 'sort'); 91 - $engine = new PhabricatorMarkupEngine(); 92 - $engine->setViewer($this->getViewer()); 93 - $markup_objects = array(); 94 - foreach ($udata as $key => $test) { 95 - $userdata = idx($test, 'userdata'); 96 - if ($userdata) { 97 - if ($userdata !== false) { 98 - $userdata = str_replace("\000", '', $userdata); 99 - } 100 - $markup_object = id(new PhabricatorMarkupOneOff()) 101 - ->setContent($userdata) 102 - ->setPreserveLinebreaks(true); 103 - $engine->addObject($markup_object, 'default'); 104 - $markup_objects[$key] = $markup_object; 55 + $buildable = $diff->getBuildable(); 56 + if ($buildable) { 57 + $target_phids = array(); 58 + foreach ($buildable->getBuilds() as $build) { 59 + foreach ($build->getBuildTargets() as $target) { 60 + $target_phids[] = $target->getPHID(); 105 61 } 106 62 } 107 - $engine->process(); 108 - foreach ($udata as $key => $test) { 109 - $result = idx($test, 'result'); 110 63 111 - $default_hide = false; 112 - switch ($result) { 113 - case ArcanistUnitTestResult::RESULT_POSTPONED: 114 - case ArcanistUnitTestResult::RESULT_PASS: 115 - $default_hide = true; 116 - break; 117 - } 64 + $unit = id(new HarbormasterBuildUnitMessage())->loadAllWhere( 65 + 'buildTargetPHID IN (%Ls) LIMIT 25', 66 + $target_phids); 67 + } 118 68 119 - if ($show_limit && !$default_hide) { 120 - --$show_limit; 121 - $show = true; 122 - } else { 123 - $show = false; 124 - if (empty($hidden[$result])) { 125 - $hidden[$result] = 0; 126 - } 127 - $hidden[$result]++; 128 - } 129 - 130 - $value = idx($test, 'name'); 131 - 132 - $namespace = idx($test, 'namespace'); 133 - if ($namespace) { 134 - $value = $namespace.'::'.$value; 135 - } 136 - 137 - if (!empty($test['link'])) { 138 - $value = phutil_tag( 139 - 'a', 140 - array( 141 - 'href' => $test['link'], 142 - 'target' => '_blank', 143 - ), 144 - $value); 145 - } 146 - $rows[] = array( 147 - 'style' => $this->getResultStyle($result), 148 - 'name' => ucwords($result), 149 - 'value' => $value, 150 - 'show' => $show, 151 - ); 69 + if (!$unit) { 70 + $legacy_unit = $diff->getProperty('arc:unit'); 71 + if ($legacy_unit) { 72 + // Show the top 100 legacy unit messages. 73 + $legacy_unit = array_slice($legacy_unit, 0, 100); 152 74 153 - if (isset($markup_objects[$key])) { 154 - $rows[] = array( 155 - 'style' => 'details', 156 - 'value' => $engine->getOutput($markup_objects[$key], 'default'), 157 - 'show' => false, 158 - ); 159 - if (empty($hidden['details'])) { 160 - $hidden['details'] = 0; 75 + $target = new HarbormasterBuildTarget(); 76 + foreach ($legacy_unit as $message) { 77 + try { 78 + $modern = HarbormasterBuildUnitMessage::newFromDictionary( 79 + $target, 80 + $this->getModernUnitMessageDictionary($message)); 81 + $unit[] = $modern; 82 + } catch (Exception $ex) { 83 + // Just ignore it if legacy messages aren't formatted like 84 + // we expect. 161 85 } 162 - $hidden['details']++; 163 86 } 164 87 } 165 88 } 166 89 167 - $show_string = $this->renderShowString($hidden); 90 + if ($unit) { 91 + $path_map = mpull($diff->loadChangesets(), 'getID', 'getFilename'); 92 + foreach ($path_map as $path => $id) { 93 + $href = '#C'.$id.'NL'; 168 94 169 - $view = new DifferentialResultsTableView(); 170 - $view->setRows($rows); 171 - $view->setShowMoreString($show_string); 95 + // TODO: When the diff is not the right-hand-size diff, we should 96 + // ideally adjust this URI to be absolute. 172 97 173 - return $view->render(); 174 - } 175 - 176 - private function getResultStyle($result) { 177 - $map = array( 178 - ArcanistUnitTestResult::RESULT_PASS => 'green', 179 - ArcanistUnitTestResult::RESULT_FAIL => 'red', 180 - ArcanistUnitTestResult::RESULT_SKIP => 'blue', 181 - ArcanistUnitTestResult::RESULT_BROKEN => 'red', 182 - ArcanistUnitTestResult::RESULT_UNSOUND => 'yellow', 183 - ArcanistUnitTestResult::RESULT_POSTPONED => 'blue', 184 - ); 185 - return idx($map, $result); 186 - } 98 + $path_map[$path] = $href; 99 + } 187 100 188 - private function renderShowString(array $hidden) { 189 - if (!$hidden) { 190 - return null; 101 + $view = id(new HarbormasterUnitPropertyView()) 102 + ->setPathURIMap($path_map) 103 + ->setUnitMessages($unit); 104 + } else { 105 + $view = null; 191 106 } 192 107 193 - // Reorder hidden things by severity. 194 - $hidden = array_select_keys( 195 - $hidden, 196 - array( 197 - ArcanistUnitTestResult::RESULT_BROKEN, 198 - ArcanistUnitTestResult::RESULT_FAIL, 199 - ArcanistUnitTestResult::RESULT_UNSOUND, 200 - ArcanistUnitTestResult::RESULT_SKIP, 201 - ArcanistUnitTestResult::RESULT_POSTPONED, 202 - ArcanistUnitTestResult::RESULT_PASS, 203 - 'details', 204 - )) + $hidden; 205 - 206 - $noun = array( 207 - ArcanistUnitTestResult::RESULT_BROKEN => pht('Broken'), 208 - ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'), 209 - ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'), 210 - ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'), 211 - ArcanistUnitTestResult::RESULT_POSTPONED => pht('Postponed'), 212 - ArcanistUnitTestResult::RESULT_PASS => pht('Passed'), 108 + return array( 109 + $status, 110 + $view, 213 111 ); 214 - 215 - $show = array(); 216 - foreach ($hidden as $key => $value) { 217 - if ($key == 'details') { 218 - $show[] = pht('%d Detail(s)', $value); 219 - } else { 220 - $show[] = $value.' '.idx($noun, $key); 221 - } 222 - } 223 - 224 - return pht( 225 - 'Show Full Unit Results (%s)', 226 - implode(', ', $show)); 227 112 } 228 113 229 114 public function getWarningsForDetailView() { ··· 245 130 } 246 131 247 132 return $warnings; 133 + } 134 + 135 + 136 + private function renderUnitStatus(DifferentialDiff $diff) { 137 + $colors = array( 138 + DifferentialUnitStatus::UNIT_NONE => 'grey', 139 + DifferentialUnitStatus::UNIT_OKAY => 'green', 140 + DifferentialUnitStatus::UNIT_WARN => 'yellow', 141 + DifferentialUnitStatus::UNIT_FAIL => 'red', 142 + DifferentialUnitStatus::UNIT_SKIP => 'blue', 143 + DifferentialUnitStatus::UNIT_AUTO_SKIP => 'blue', 144 + DifferentialUnitStatus::UNIT_POSTPONED => 'blue', 145 + ); 146 + $icon_color = idx($colors, $diff->getUnitStatus(), 'grey'); 147 + 148 + $message = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); 149 + 150 + $excuse = $diff->getProperty('arc:unit-excuse'); 151 + if (strlen($excuse)) { 152 + $excuse = array( 153 + phutil_tag('strong', array(), pht('Excuse:')), 154 + ' ', 155 + phutil_escape_html_newlines($excuse), 156 + ); 157 + } 158 + 159 + $status = id(new PHUIStatusListView()) 160 + ->addItem( 161 + id(new PHUIStatusItemView()) 162 + ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) 163 + ->setTarget($message) 164 + ->setNote($excuse)); 165 + 166 + return $status; 167 + } 168 + 169 + private function getModernUnitMessageDictionary(array $map) { 170 + // Strip out `null` values to satisfy stricter typechecks. 171 + foreach ($map as $key => $value) { 172 + if ($value === null) { 173 + unset($map[$key]); 174 + } 175 + } 176 + 177 + // TODO: Remap more stuff here? 178 + 179 + return $map; 248 180 } 249 181 250 182
+15
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 587 587 588 588 $diff->setRevisionID($object->getID()); 589 589 $diff->save(); 590 + 591 + // Update Harbormaster to set the containerPHID correctly for any 592 + // existing buildables. We may otherwise have buildables stuck with 593 + // the old (`null`) container. 594 + 595 + // TODO: This is a bit iffy, maybe we can find a cleaner approach? 596 + $table = new HarbormasterBuildable(); 597 + $conn_w = $table->establishConnection('w'); 598 + queryfx( 599 + $conn_w, 600 + 'UPDATE %T SET containerPHID = %s WHERE buildablePHID = %s', 601 + $table->getTableName(), 602 + $object->getPHID(), 603 + $diff->getPHID()); 604 + 590 605 return; 591 606 } 592 607
+10
src/applications/differential/storage/DifferentialDiff.php
··· 39 39 private $changesets = self::ATTACHABLE; 40 40 private $revision = self::ATTACHABLE; 41 41 private $properties = array(); 42 + private $buildable = self::ATTACHABLE; 42 43 43 44 protected function getConfiguration() { 44 45 return array( ··· 321 322 322 323 public function getProperty($key) { 323 324 return $this->assertAttachedKey($this->properties, $key); 325 + } 326 + 327 + public function attachBuildable(HarbormasterBuildable $buildable = null) { 328 + $this->buildable = $buildable; 329 + return $this; 330 + } 331 + 332 + public function getBuildable() { 333 + return $this->assertAttached($this->buildable); 324 334 } 325 335 326 336
-115
src/applications/differential/view/DifferentialResultsTableView.php
··· 1 - <?php 2 - 3 - final class DifferentialResultsTableView extends AphrontView { 4 - 5 - private $rows; 6 - private $showMoreString; 7 - 8 - public function setRows(array $rows) { 9 - $this->rows = $rows; 10 - return $this; 11 - } 12 - 13 - public function setShowMoreString($show_more_string) { 14 - $this->showMoreString = $show_more_string; 15 - return $this; 16 - } 17 - 18 - public function render() { 19 - 20 - $rows = array(); 21 - 22 - $any_hidden = false; 23 - foreach ($this->rows as $row) { 24 - 25 - $style = idx($row, 'style'); 26 - switch ($style) { 27 - case 'section': 28 - $cells = phutil_tag( 29 - 'th', 30 - array( 31 - 'colspan' => 2, 32 - ), 33 - idx($row, 'name')); 34 - break; 35 - default: 36 - $name = phutil_tag( 37 - 'th', 38 - array( 39 - ), 40 - idx($row, 'name')); 41 - $value = phutil_tag( 42 - 'td', 43 - array( 44 - ), 45 - idx($row, 'value')); 46 - $cells = array($name, $value); 47 - break; 48 - } 49 - 50 - $show = idx($row, 'show'); 51 - 52 - $rows[] = javelin_tag( 53 - 'tr', 54 - array( 55 - 'style' => $show ? null : 'display: none', 56 - 'sigil' => $show ? null : 'differential-results-row-toggle', 57 - 'class' => 'differential-results-row-'.$style, 58 - ), 59 - $cells); 60 - 61 - if (!$show) { 62 - $any_hidden = true; 63 - } 64 - } 65 - 66 - if ($any_hidden) { 67 - $show_more = javelin_tag( 68 - 'a', 69 - array( 70 - 'href' => '#', 71 - 'mustcapture' => true, 72 - ), 73 - $this->showMoreString); 74 - 75 - $hide_more = javelin_tag( 76 - 'a', 77 - array( 78 - 'href' => '#', 79 - 'mustcapture' => true, 80 - ), 81 - pht('Hide')); 82 - 83 - $rows[] = javelin_tag( 84 - 'tr', 85 - array( 86 - 'class' => 'differential-results-row-show', 87 - 'sigil' => 'differential-results-row-show', 88 - ), 89 - phutil_tag('th', array('colspan' => 2), $show_more)); 90 - 91 - $rows[] = javelin_tag( 92 - 'tr', 93 - array( 94 - 'class' => 'differential-results-row-show', 95 - 'sigil' => 'differential-results-row-hide', 96 - 'style' => 'display: none', 97 - ), 98 - phutil_tag('th', array('colspan' => 2), $hide_more)); 99 - 100 - $this->initBehavior('differential-show-field-details'); 101 - } 102 - 103 - $this->requireResource('differential-results-table-css'); 104 - 105 - return javelin_tag( 106 - 'table', 107 - array( 108 - 'class' => 'differential-results-table', 109 - 'sigil' => 'differential-results-table', 110 - ), 111 - $rows); 112 - } 113 - 114 - 115 - }
+27 -4
src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php
··· 18 18 19 19 return array( 20 20 'buildTargetPHID' => 'required phid', 21 - 'type' => 'required '.$type_const, 21 + 'lint' => 'optional list<wild>', 22 + 'unit' => 'optional list<wild>', 23 + 'type' => 'required '.$type_const, 22 24 ); 23 25 } 24 26 ··· 40 42 throw new Exception(pht('No such build target!')); 41 43 } 42 44 43 - $message = HarbormasterBuildMessage::initializeNewMessage($viewer) 45 + $save = array(); 46 + 47 + $lint_messages = $request->getValue('lint', array()); 48 + foreach ($lint_messages as $lint) { 49 + $save[] = HarbormasterBuildLintMessage::newFromDictionary( 50 + $build_target, 51 + $lint); 52 + } 53 + 54 + $unit_messages = $request->getValue('unit', array()); 55 + foreach ($unit_messages as $unit) { 56 + $save[] = HarbormasterBuildUnitMessage::newFromDictionary( 57 + $build_target, 58 + $unit); 59 + } 60 + 61 + $save[] = HarbormasterBuildMessage::initializeNewMessage($viewer) 44 62 ->setBuildTargetPHID($build_target->getPHID()) 45 - ->setType($message_type) 46 - ->save(); 63 + ->setType($message_type); 64 + 65 + $build_target->openTransaction(); 66 + foreach ($save as $object) { 67 + $object->save(); 68 + } 69 + $build_target->saveTransaction(); 47 70 48 71 // If the build has completely paused because all steps are blocked on 49 72 // waiting targets, this will resume it.
+75 -25
src/applications/harbormaster/controller/HarbormasterBuildViewController.php
··· 48 48 $this->buildPropertyLists($box, $build, $actions); 49 49 50 50 $crumbs = $this->buildApplicationCrumbs(); 51 - $crumbs->addTextCrumb( 52 - $build->getBuildable()->getMonogram(), 53 - '/'.$build->getBuildable()->getMonogram()); 51 + $this->addBuildableCrumb($crumbs, $build->getBuildable()); 54 52 $crumbs->addTextCrumb($title); 55 53 56 54 if ($generation === null || $generation > $build->getBuildGeneration() || ··· 99 97 $item->setIcon($icon, $color); 100 98 $status_view->addItem($item); 101 99 102 - $properties->addProperty(pht('Name'), $build_target->getName()); 100 + $when = array(); 101 + $started = $build_target->getDateStarted(); 102 + $now = PhabricatorTime::getNow(); 103 + if ($started) { 104 + $ended = $build_target->getDateCompleted(); 105 + if ($ended) { 106 + $when[] = pht( 107 + 'Completed at %s', 108 + phabricator_datetime($started, $viewer)); 103 109 104 - if ($build_target->getDateStarted() !== null) { 105 - $properties->addProperty( 106 - pht('Started'), 107 - phabricator_datetime($build_target->getDateStarted(), $viewer)); 108 - if ($build_target->isComplete()) { 109 - $properties->addProperty( 110 - pht('Completed'), 111 - phabricator_datetime($build_target->getDateCompleted(), $viewer)); 112 - $properties->addProperty( 113 - pht('Duration'), 114 - phutil_format_relative_time_detailed( 115 - $build_target->getDateCompleted() - 116 - $build_target->getDateStarted())); 110 + $duration = ($ended - $started); 111 + if ($duration) { 112 + $when[] = pht( 113 + 'Built for %s', 114 + phutil_format_relative_time_detailed($duration)); 115 + } else { 116 + $when[] = pht('Built instantly'); 117 + } 117 118 } else { 118 - $properties->addProperty( 119 - pht('Elapsed'), 120 - phutil_format_relative_time_detailed( 121 - time() - $build_target->getDateStarted())); 119 + $when[] = pht( 120 + 'Started at %s', 121 + phabricator_datetime($started, $viewer)); 122 + $duration = ($now - $started); 123 + if ($duration) { 124 + $when[] = pht( 125 + 'Running for %s', 126 + phutil_format_relative_time_detailed($duration)); 127 + } 128 + } 129 + } else { 130 + $created = $build_target->getDateCreated(); 131 + $when[] = pht( 132 + 'Queued at %s', 133 + phabricator_datetime($started, $viewer)); 134 + $duration = ($now - $created); 135 + if ($duration) { 136 + $when[] = pht( 137 + 'Waiting for %s', 138 + phutil_format_relative_time_detailed($duration)); 122 139 } 123 140 } 141 + 142 + $properties->addProperty( 143 + pht('When'), 144 + phutil_implode_html(" \xC2\xB7 ", $when)); 124 145 125 146 $properties->addProperty(pht('Status'), $status_view); 126 147 ··· 162 183 $variables = $build_target->getVariables(); 163 184 if ($variables) { 164 185 $properties = new PHUIPropertyListView(); 165 - foreach ($variables as $key => $value) { 166 - $properties->addProperty($key, $value); 167 - } 186 + $properties->addRawContent($this->buildProperties($variables)); 168 187 $target_box->addPropertyList($properties, pht('Variables')); 169 188 } 170 189 ··· 183 202 } 184 203 185 204 $properties = new PHUIPropertyListView(); 186 - $properties->addProperty(pht('Build Target ID'), $build_target->getID()); 205 + $properties->addProperty( 206 + pht('Build Target ID'), 207 + $build_target->getID()); 208 + $properties->addProperty( 209 + pht('Build Target PHID'), 210 + $build_target->getPHID()); 187 211 $target_box->addPropertyList($properties, pht('Metadata')); 188 212 189 213 $targets[] = $target_box; ··· 524 548 '', 525 549 'date', 526 550 )); 551 + 552 + return $table; 553 + } 554 + 555 + private function buildProperties(array $properties) { 556 + ksort($properties); 557 + 558 + $rows = array(); 559 + foreach ($properties as $key => $value) { 560 + $rows[] = array( 561 + $key, 562 + $value, 563 + ); 564 + } 565 + 566 + $table = id(new AphrontTableView($rows)) 567 + ->setHeaders( 568 + array( 569 + pht('Key'), 570 + pht('Value'), 571 + )) 572 + ->setColumnClasses( 573 + array( 574 + 'pri right', 575 + 'wide', 576 + )); 527 577 528 578 return $table; 529 579 }
+75 -18
src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
··· 3 3 final class HarbormasterBuildableViewController 4 4 extends HarbormasterController { 5 5 6 - private $id; 7 - 8 - public function willProcessRequest(array $data) { 9 - $this->id = $data['id']; 10 - } 11 - 12 - public function processRequest() { 13 - $request = $this->getRequest(); 14 - $viewer = $request->getUser(); 15 - 16 - $id = $this->id; 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $this->getViewer(); 17 8 18 9 $buildable = id(new HarbormasterBuildableQuery()) 19 10 ->setViewer($viewer) 20 - ->withIDs(array($id)) 11 + ->withIDs(array($request->getURIData('id'))) 21 12 ->needBuildableHandles(true) 22 13 ->needContainerHandles(true) 23 14 ->executeOne(); ··· 25 16 return new Aphront404Response(); 26 17 } 27 18 19 + $id = $buildable->getID(); 20 + 28 21 // Pull builds and build targets. 29 22 $builds = id(new HarbormasterBuildQuery()) 30 23 ->setViewer($viewer) ··· 32 25 ->needBuildTargets(true) 33 26 ->execute(); 34 27 28 + list($lint, $unit) = $this->renderLintAndUnit($builds); 29 + 35 30 $buildable->attachBuilds($builds); 31 + $object = $buildable->getBuildableObject(); 36 32 37 33 $build_list = $this->buildBuildList($buildable); 38 34 ··· 55 51 $this->buildPropertyLists($box, $buildable, $actions); 56 52 57 53 $crumbs = $this->buildApplicationCrumbs(); 58 - $crumbs->addTextCrumb("B{$id}"); 54 + $crumbs->addTextCrumb($buildable->getMonogram()); 59 55 60 56 return $this->buildApplicationPage( 61 57 array( 62 58 $crumbs, 63 59 $box, 60 + $lint, 61 + $unit, 64 62 $build_list, 65 63 $timeline, 66 64 ), ··· 144 142 ->setActionList($actions); 145 143 $box->addPropertyList($properties); 146 144 147 - $properties->addProperty( 148 - pht('Buildable'), 149 - $buildable->getBuildableHandle()->renderLink()); 150 - 151 145 if ($buildable->getContainerHandle() !== null) { 152 146 $properties->addProperty( 153 147 pht('Container'), 154 148 $buildable->getContainerHandle()->renderLink()); 155 149 } 150 + 151 + $properties->addProperty( 152 + pht('Buildable'), 153 + $buildable->getBuildableHandle()->renderLink()); 156 154 157 155 $properties->addProperty( 158 156 pht('Origin'), ··· 250 248 $build_list->addItem($item); 251 249 } 252 250 253 - return $build_list; 251 + $build_list->setFlush(true); 252 + 253 + $box = id(new PHUIObjectBoxView()) 254 + ->setHeaderText(pht('Builds')) 255 + ->appendChild($build_list); 256 + 257 + return $box; 258 + } 259 + 260 + private function renderLintAndUnit(array $builds) { 261 + $viewer = $this->getViewer(); 262 + 263 + $targets = array(); 264 + foreach ($builds as $build) { 265 + foreach ($build->getBuildTargets() as $target) { 266 + $targets[] = $target; 267 + } 268 + } 269 + 270 + if (!$targets) { 271 + return; 272 + } 273 + 274 + $target_phids = mpull($targets, 'getPHID'); 275 + 276 + $lint_data = id(new HarbormasterBuildLintMessage())->loadAllWhere( 277 + 'buildTargetPHID IN (%Ls) LIMIT 25', 278 + $target_phids); 279 + 280 + $unit_data = id(new HarbormasterBuildUnitMessage())->loadAllWhere( 281 + 'buildTargetPHID IN (%Ls) LIMIT 25', 282 + $target_phids); 283 + 284 + if ($lint_data) { 285 + $lint_table = id(new HarbormasterLintPropertyView()) 286 + ->setUser($viewer) 287 + ->setLintMessages($lint_data); 288 + 289 + $lint = id(new PHUIObjectBoxView()) 290 + ->setHeaderText(pht('Lint Messages')) 291 + ->appendChild($lint_table); 292 + } else { 293 + $lint = null; 294 + } 295 + 296 + if ($unit_data) { 297 + $unit_table = id(new HarbormasterUnitPropertyView()) 298 + ->setUser($viewer) 299 + ->setUnitMessages($unit_data); 300 + 301 + $unit = id(new PHUIObjectBoxView()) 302 + ->setHeaderText(pht('Unit Tests')) 303 + ->appendChild($unit_table); 304 + } else { 305 + $unit = null; 306 + } 307 + 308 + return array($lint, $unit); 254 309 } 310 + 311 + 255 312 256 313 }
+10
src/applications/harbormaster/controller/HarbormasterController.php
··· 2 2 3 3 abstract class HarbormasterController extends PhabricatorController { 4 4 5 + protected function addBuildableCrumb( 6 + PHUICrumbsView $crumbs, 7 + HarbormasterBuildable $buildable) { 8 + 9 + $monogram = $buildable->getMonogram(); 10 + $uri = '/'.$monogram; 11 + 12 + $crumbs->addTextCrumb($monogram, $uri); 13 + } 14 + 5 15 }
+5
src/applications/harbormaster/engine/HarbormasterBuildEngine.php
··· 332 332 $message->save(); 333 333 334 334 $target->setTargetStatus($new_status); 335 + 336 + if ($target->isComplete()) { 337 + $target->setDateCompleted(PhabricatorTime::getNow()); 338 + } 339 + 335 340 $target->save(); 336 341 } 337 342 }
+20 -22
src/applications/harbormaster/query/HarbormasterBuildableQuery.php
··· 13 13 private $needContainerHandles; 14 14 private $needBuildableHandles; 15 15 private $needBuilds; 16 + private $needTargets; 16 17 17 18 public function withIDs(array $ids) { 18 19 $this->ids = $ids; ··· 59 60 return $this; 60 61 } 61 62 62 - protected function loadPage() { 63 - $table = new HarbormasterBuildable(); 64 - $conn_r = $table->establishConnection('r'); 63 + public function needTargets($need) { 64 + $this->needTargets = $need; 65 + return $this; 66 + } 65 67 66 - $data = queryfx_all( 67 - $conn_r, 68 - 'SELECT * FROM %T %Q %Q %Q', 69 - $table->getTableName(), 70 - $this->buildWhereClause($conn_r), 71 - $this->buildOrderClause($conn_r), 72 - $this->buildLimitClause($conn_r)); 68 + public function newResultObject() { 69 + return new HarbormasterBuildable(); 70 + } 73 71 74 - return $table->loadAllFromArray($data); 72 + protected function loadPage() { 73 + return $this->loadStandardPage($this->newResultObject()); 75 74 } 76 75 77 76 protected function willFilterPage(array $page) { ··· 157 156 } 158 157 } 159 158 160 - if ($this->needBuilds) { 159 + if ($this->needBuilds || $this->needTargets) { 161 160 $builds = id(new HarbormasterBuildQuery()) 162 161 ->setViewer($this->getViewer()) 163 162 ->setParentQuery($this) 164 163 ->withBuildablePHIDs(mpull($page, 'getPHID')) 164 + ->needBuildTargets($this->needTargets) 165 165 ->execute(); 166 166 $builds = mgroup($builds, 'getBuildablePHID'); 167 167 foreach ($page as $key => $buildable) { ··· 172 172 return $page; 173 173 } 174 174 175 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 176 - $where = array(); 175 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 176 + $where = parent::buildWhereClauseParts($conn); 177 177 178 178 if ($this->ids !== null) { 179 179 $where[] = qsprintf( 180 - $conn_r, 180 + $conn, 181 181 'id IN (%Ld)', 182 182 $this->ids); 183 183 } 184 184 185 185 if ($this->phids !== null) { 186 186 $where[] = qsprintf( 187 - $conn_r, 187 + $conn, 188 188 'phid IN (%Ls)', 189 189 $this->phids); 190 190 } 191 191 192 192 if ($this->buildablePHIDs !== null) { 193 193 $where[] = qsprintf( 194 - $conn_r, 194 + $conn, 195 195 'buildablePHID IN (%Ls)', 196 196 $this->buildablePHIDs); 197 197 } 198 198 199 199 if ($this->containerPHIDs !== null) { 200 200 $where[] = qsprintf( 201 - $conn_r, 201 + $conn, 202 202 'containerPHID in (%Ls)', 203 203 $this->containerPHIDs); 204 204 } 205 205 206 206 if ($this->manualBuildables !== null) { 207 207 $where[] = qsprintf( 208 - $conn_r, 208 + $conn, 209 209 'isManualBuildable = %d', 210 210 (int)$this->manualBuildables); 211 211 } 212 212 213 - $where[] = $this->buildPagingClause($conn_r); 214 - 215 - return $this->formatWhereClause($where); 213 + return $where; 216 214 } 217 215 218 216 public function getQueryApplicationClass() {
+1 -1
src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php
··· 15 15 16 16 public static function initializeNewUnitMessage( 17 17 HarbormasterBuildTarget $build_target) { 18 - return id(new HarbormasterBuildLintMessage()) 18 + return id(new HarbormasterBuildUnitMessage()) 19 19 ->setBuildTargetPHID($build_target->getPHID()); 20 20 } 21 21
+78
src/applications/harbormaster/view/HarbormasterLintPropertyView.php
··· 1 + <?php 2 + 3 + final class HarbormasterLintPropertyView extends AphrontView { 4 + 5 + private $pathURIMap = array(); 6 + private $lintMessages = array(); 7 + 8 + public function setPathURIMap(array $map) { 9 + $this->pathURIMap = $map; 10 + return $this; 11 + } 12 + 13 + public function setLintMessages(array $messages) { 14 + assert_instances_of($messages, 'HarbormasterBuildLintMessage'); 15 + $this->lintMessages = $messages; 16 + return $this; 17 + } 18 + 19 + public function render() { 20 + $rows = array(); 21 + foreach ($this->lintMessages as $message) { 22 + $path = $message->getPath(); 23 + $line = $message->getLine(); 24 + 25 + $href = null; 26 + if (strlen(idx($this->pathURIMap, $path))) { 27 + $href = $this->pathURIMap[$path].max($line, 1); 28 + } 29 + 30 + $severity = $this->renderSeverity($message->getSeverity()); 31 + 32 + $location = $path.':'.$line; 33 + if (strlen($href)) { 34 + $location = phutil_tag( 35 + 'a', 36 + array( 37 + 'href' => $href, 38 + ), 39 + $location); 40 + } 41 + 42 + $rows[] = array( 43 + $location, 44 + $severity, 45 + $message->getCode(), 46 + $message->getName(), 47 + ); 48 + } 49 + 50 + $table = id(new AphrontTableView($rows)) 51 + ->setHeaders( 52 + array( 53 + pht('Location'), 54 + pht('Severity'), 55 + pht('Code'), 56 + pht('Message'), 57 + )) 58 + ->setColumnClasses( 59 + array( 60 + 'pri', 61 + null, 62 + null, 63 + 'wide', 64 + )); 65 + 66 + return $table; 67 + } 68 + 69 + private function renderSeverity($severity) { 70 + $names = ArcanistLintSeverity::getLintSeverities(); 71 + $name = idx($names, $severity, $severity); 72 + 73 + // TODO: Add some color here? 74 + 75 + return $name; 76 + } 77 + 78 + }
+90
src/applications/harbormaster/view/HarbormasterUnitPropertyView.php
··· 1 + <?php 2 + 3 + final class HarbormasterUnitPropertyView extends AphrontView { 4 + 5 + private $pathURIMap = array(); 6 + private $unitMessages = array(); 7 + 8 + public function setPathURIMap(array $map) { 9 + $this->pathURIMap = $map; 10 + return $this; 11 + } 12 + 13 + public function setUnitMessages(array $messages) { 14 + assert_instances_of($messages, 'HarbormasterBuildUnitMessage'); 15 + $this->unitMessages = $messages; 16 + return $this; 17 + } 18 + 19 + public function render() { 20 + 21 + $rows = array(); 22 + $any_duration = false; 23 + foreach ($this->unitMessages as $message) { 24 + $result = $this->renderResult($message->getResult()); 25 + 26 + $duration = $message->getDuration(); 27 + if ($duration !== null) { 28 + $any_duration = true; 29 + $duration = pht('%s ms', new PhutilNumber((int)(1000 * $duration))); 30 + } 31 + 32 + $name = $message->getName(); 33 + 34 + $namespace = $message->getNamespace(); 35 + if (strlen($namespace)) { 36 + $name = $namespace.'::'.$name; 37 + } 38 + 39 + $engine = $message->getEngine(); 40 + if (strlen($engine)) { 41 + $name = $engine.' > '.$name; 42 + } 43 + 44 + $rows[] = array( 45 + $result, 46 + $duration, 47 + $name, 48 + ); 49 + } 50 + 51 + 52 + $table = id(new AphrontTableView($rows)) 53 + ->setHeaders( 54 + array( 55 + pht('Result'), 56 + pht('Time'), 57 + pht('Test'), 58 + )) 59 + ->setColumnClasses( 60 + array( 61 + null, 62 + null, 63 + 'pri wide', 64 + )) 65 + ->setColumnVisibility( 66 + array( 67 + true, 68 + $any_duration, 69 + )); 70 + 71 + return $table; 72 + } 73 + 74 + private function renderResult($result) { 75 + $names = array( 76 + ArcanistUnitTestResult::RESULT_BROKEN => pht('Broken'), 77 + ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'), 78 + ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'), 79 + ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'), 80 + ArcanistUnitTestResult::RESULT_POSTPONED => pht('Postponed'), 81 + ArcanistUnitTestResult::RESULT_PASS => pht('Passed'), 82 + ); 83 + $result = idx($names, $result, $result); 84 + 85 + // TODO: Add some color. 86 + 87 + return $result; 88 + } 89 + 90 + }
+3 -3
src/applications/harbormaster/worker/HarbormasterTargetWorker.php
··· 59 59 $target->setTargetStatus($next_status); 60 60 61 61 if ($target->isComplete()) { 62 - $target->setDateCompleted(time()); 62 + $target->setDateCompleted(PhabricatorTime::getNow()); 63 63 } 64 64 65 65 $target->save(); ··· 70 70 } catch (HarbormasterBuildFailureException $ex) { 71 71 // A build step wants to fail explicitly. 72 72 $target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED); 73 - $target->setDateCompleted(time()); 73 + $target->setDateCompleted(PhabricatorTime::getNow()); 74 74 $target->save(); 75 75 } catch (HarbormasterBuildAbortedException $ex) { 76 76 // A build step is aborting because the build has been restarted. 77 77 $target->setTargetStatus(HarbormasterBuildTarget::STATUS_ABORTED); 78 - $target->setDateCompleted(time()); 78 + $target->setDateCompleted(PhabricatorTime::getNow()); 79 79 $target->save(); 80 80 } catch (Exception $ex) { 81 81 phlog($ex);
+2 -1
src/applications/pholio/storage/PholioMock.php
··· 10 10 PhabricatorApplicationTransactionInterface, 11 11 PhabricatorProjectInterface, 12 12 PhabricatorDestructibleInterface, 13 - PhabricatorSpacesInterface { 13 + PhabricatorSpacesInterface, 14 + PhabricatorMentionableInterface { 14 15 15 16 const MARKUP_FIELD_DESCRIPTION = 'markup:description'; 16 17
+1
src/view/phui/PHUIStatusItemView.php
··· 22 22 const ICON_MINUS = 'fa-minus-circle'; 23 23 const ICON_OPEN = 'fa-circle-o'; 24 24 const ICON_CLOCK = 'fa-clock-o'; 25 + const ICON_STAR = 'fa-star'; 25 26 26 27 /* render_textarea */ 27 28 public function setIcon($icon, $color = null, $label = null) {
-78
webroot/rsrc/css/application/differential/results-table.css
··· 1 - /** 2 - * @provides differential-results-table-css 3 - */ 4 - 5 - table.differential-results-table { 6 - border-collapse: separate; 7 - width: 96%; 8 - font-size: 11px; 9 - } 10 - 11 - .differential-results-table th { 12 - text-align: center; 13 - white-space: nowrap; 14 - vertical-align: middle; 15 - padding: 2px 4px; 16 - width: 50px; 17 - border-right: 1px solid #fff; 18 - background: #f7f7f7; 19 - } 20 - 21 - .device .differential-results-table th { 22 - white-space: normal; 23 - } 24 - 25 - .differential-results-table td { 26 - padding: 0 8px; 27 - margin: 0; 28 - vertical-align: middle; 29 - background: #f7f7f7; 30 - } 31 - 32 - .differential-results-table tr.differential-results-row-star th, 33 - .differential-results-table tr.differential-results-row-star td { 34 - background: {$greybackground}; 35 - } 36 - 37 - .differential-results-table tr.differential-results-row-section th { 38 - padding-top: 4px; 39 - text-align: left; 40 - } 41 - 42 - .differential-results-table tr.differential-results-row-excuse th { 43 - background: #3399ff; 44 - } 45 - 46 - .differential-results-table tr.differential-results-row-excuse td { 47 - padding-top: 8px; 48 - padding-right: 8px; 49 - padding-bottom: 8px; 50 - } 51 - 52 - .differential-results-table tr.differential-results-row-red th { 53 - background: #ff4422; 54 - } 55 - 56 - .differential-results-table tr.differential-results-row-yellow th { 57 - background: #ffdd66; 58 - } 59 - 60 - .differential-results-table tr.differential-results-row-green th { 61 - background: #22dd44; 62 - } 63 - 64 - .differential-results-table tr.differential-results-row-blue th { 65 - background: #88bbff; 66 - } 67 - 68 - .differential-results-table tr.differential-results-row-details td { 69 - color: {$lightgreytext}; 70 - } 71 - 72 - .differential-results-table tr.differential-results-row-show th { 73 - border-top: 1px solid #fff; 74 - border-right: none; 75 - padding: 2px; 76 - color: {$bluetext}; 77 - background: {$greybackground}; 78 - }
-40
webroot/rsrc/js/application/differential/behavior-show-field-details.js
··· 1 - /** 2 - * @provides javelin-behavior-differential-show-field-details 3 - * @requires javelin-behavior 4 - * javelin-stratcom 5 - * javelin-dom 6 - */ 7 - 8 - JX.behavior('differential-show-field-details', function() { 9 - 10 - JX.Stratcom.listen( 11 - 'click', 12 - ['differential-results-row-show', 'tag:a'], 13 - function(e) { 14 - toggle(e, true); 15 - }); 16 - 17 - JX.Stratcom.listen( 18 - 'click', 19 - ['differential-results-row-hide', 'tag:a'], 20 - function(e) { 21 - toggle(e, false); 22 - }); 23 - 24 - function toggle(e, show) { 25 - e.kill(); 26 - 27 - var f = show ? JX.DOM.show : JX.DOM.hide; 28 - var g = show ? JX.DOM.hide : JX.DOM.show; 29 - 30 - var table = e.getNode('differential-results-table'); 31 - var rows = JX.DOM.scry(table, 'tr', 'differential-results-row-toggle'); 32 - for (var ii = 0; ii < rows.length; ii++) { 33 - f(rows[ii]); 34 - } 35 - 36 - g(JX.DOM.find(table, 'tr', 'differential-results-row-show')); 37 - f(JX.DOM.find(table, 'tr', 'differential-results-row-hide')); 38 - } 39 - 40 - });