Merge branch 'master' into redesign-2015
This commit is contained in:
		| @@ -10,7 +10,7 @@ return array( | |||||||
|     'core.pkg.css' => '7f0d6232', |     'core.pkg.css' => '7f0d6232', | ||||||
|     'core.pkg.js' => 'a590b451', |     'core.pkg.js' => 'a590b451', | ||||||
|     'darkconsole.pkg.js' => 'e7393ebb', |     'darkconsole.pkg.js' => 'e7393ebb', | ||||||
|     'differential.pkg.css' => 'fe951924', |     'differential.pkg.css' => '27498de3', | ||||||
|     'differential.pkg.js' => 'ebef29b1', |     'differential.pkg.js' => 'ebef29b1', | ||||||
|     'diffusion.pkg.css' => '385e85b3', |     'diffusion.pkg.css' => '385e85b3', | ||||||
|     'diffusion.pkg.js' => '0115b37c', |     'diffusion.pkg.js' => '0115b37c', | ||||||
| @@ -61,7 +61,6 @@ return array( | |||||||
|     'rsrc/css/application/differential/changeset-view.css' => '9b8e8bb7', |     'rsrc/css/application/differential/changeset-view.css' => '9b8e8bb7', | ||||||
|     'rsrc/css/application/differential/core.css' => '7ac3cabc', |     'rsrc/css/application/differential/core.css' => '7ac3cabc', | ||||||
|     'rsrc/css/application/differential/phui-inline-comment.css' => 'fa5b8d1f', |     'rsrc/css/application/differential/phui-inline-comment.css' => 'fa5b8d1f', | ||||||
|     'rsrc/css/application/differential/results-table.css' => '181aa9d9', |  | ||||||
|     'rsrc/css/application/differential/revision-comment.css' => '14b8565a', |     'rsrc/css/application/differential/revision-comment.css' => '14b8565a', | ||||||
|     'rsrc/css/application/differential/revision-history.css' => '0e8eb855', |     'rsrc/css/application/differential/revision-history.css' => '0e8eb855', | ||||||
|     'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', |     'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', | ||||||
| @@ -357,7 +356,6 @@ return array( | |||||||
|     'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '037b59eb', |     'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '037b59eb', | ||||||
|     'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492', |     'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492', | ||||||
|     'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', |     'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', | ||||||
|     'rsrc/js/application/differential/behavior-show-field-details.js' => 'bba9eedf', |  | ||||||
|     'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', |     'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', | ||||||
|     'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', |     'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', | ||||||
|     'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'b42eddc7', |     'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'b42eddc7', | ||||||
| @@ -511,7 +509,6 @@ return array( | |||||||
|     'differential-changeset-view-css' => '9b8e8bb7', |     'differential-changeset-view-css' => '9b8e8bb7', | ||||||
|     'differential-core-view-css' => '7ac3cabc', |     'differential-core-view-css' => '7ac3cabc', | ||||||
|     'differential-inline-comment-editor' => 'd4c87bf4', |     'differential-inline-comment-editor' => 'd4c87bf4', | ||||||
|     'differential-results-table-css' => '181aa9d9', |  | ||||||
|     'differential-revision-add-comment-css' => 'c47f8c40', |     'differential-revision-add-comment-css' => 'c47f8c40', | ||||||
|     'differential-revision-comment-css' => '14b8565a', |     'differential-revision-comment-css' => '14b8565a', | ||||||
|     'differential-revision-history-css' => '0e8eb855', |     'differential-revision-history-css' => '0e8eb855', | ||||||
| @@ -565,7 +562,6 @@ return array( | |||||||
|     'javelin-behavior-differential-feedback-preview' => 'b064af76', |     'javelin-behavior-differential-feedback-preview' => 'b064af76', | ||||||
|     'javelin-behavior-differential-keyboard-navigation' => '2c426492', |     'javelin-behavior-differential-keyboard-navigation' => '2c426492', | ||||||
|     'javelin-behavior-differential-populate' => '8694b1df', |     'javelin-behavior-differential-populate' => '8694b1df', | ||||||
|     'javelin-behavior-differential-show-field-details' => 'bba9eedf', |  | ||||||
|     'javelin-behavior-differential-toggle-files' => 'ca3f91eb', |     'javelin-behavior-differential-toggle-files' => 'ca3f91eb', | ||||||
|     'javelin-behavior-differential-user-select' => 'a8d8459d', |     'javelin-behavior-differential-user-select' => 'a8d8459d', | ||||||
|     'javelin-behavior-diffusion-commit-branches' => 'bdaf4d04', |     'javelin-behavior-diffusion-commit-branches' => 'bdaf4d04', | ||||||
| @@ -1711,11 +1707,6 @@ return array( | |||||||
|       'javelin-workflow', |       'javelin-workflow', | ||||||
|       'phabricator-draggable-list', |       'phabricator-draggable-list', | ||||||
|     ), |     ), | ||||||
|     'bba9eedf' => array( |  | ||||||
|       'javelin-behavior', |  | ||||||
|       'javelin-stratcom', |  | ||||||
|       'javelin-dom', |  | ||||||
|     ), |  | ||||||
|     'bd4c8dca' => array( |     'bd4c8dca' => array( | ||||||
|       'javelin-install', |       'javelin-install', | ||||||
|       'javelin-util', |       'javelin-util', | ||||||
| @@ -2192,7 +2183,6 @@ return array( | |||||||
|     'differential.pkg.css' => array( |     'differential.pkg.css' => array( | ||||||
|       'differential-core-view-css', |       'differential-core-view-css', | ||||||
|       'differential-changeset-view-css', |       'differential-changeset-view-css', | ||||||
|       'differential-results-table-css', |  | ||||||
|       'differential-revision-history-css', |       'differential-revision-history-css', | ||||||
|       'differential-revision-list-css', |       'differential-revision-list-css', | ||||||
|       'differential-table-of-contents-css', |       'differential-table-of-contents-css', | ||||||
|   | |||||||
| @@ -141,7 +141,6 @@ return array( | |||||||
|   'differential.pkg.css' => array( |   'differential.pkg.css' => array( | ||||||
|     'differential-core-view-css', |     'differential-core-view-css', | ||||||
|     'differential-changeset-view-css', |     'differential-changeset-view-css', | ||||||
|     'differential-results-table-css', |  | ||||||
|     'differential-revision-history-css', |     'differential-revision-history-css', | ||||||
|     'differential-revision-list-css', |     'differential-revision-list-css', | ||||||
|     'differential-table-of-contents-css', |     'differential-table-of-contents-css', | ||||||
|   | |||||||
| @@ -419,7 +419,6 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', |     'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', | ||||||
|     'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php', |     'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php', | ||||||
|     'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php', |     'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php', | ||||||
|     'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php', |  | ||||||
|     'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', |     'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', | ||||||
|     'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', |     'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', | ||||||
|     'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', |     'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', | ||||||
| @@ -894,6 +893,7 @@ phutil_register_library_map(array( | |||||||
|     'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php', |     'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php', | ||||||
|     'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php', |     'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php', | ||||||
|     'HarbormasterLeaseHostBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php', |     'HarbormasterLeaseHostBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php', | ||||||
|  |     'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php', | ||||||
|     'HarbormasterManagePlansCapability' => 'applications/harbormaster/capability/HarbormasterManagePlansCapability.php', |     'HarbormasterManagePlansCapability' => 'applications/harbormaster/capability/HarbormasterManagePlansCapability.php', | ||||||
|     'HarbormasterManagementBuildWorkflow' => 'applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php', |     'HarbormasterManagementBuildWorkflow' => 'applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php', | ||||||
|     'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php', |     'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php', | ||||||
| @@ -921,6 +921,7 @@ phutil_register_library_map(array( | |||||||
|     'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php', |     'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php', | ||||||
|     'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php', |     'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php', | ||||||
|     'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php', |     'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php', | ||||||
|  |     'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php', | ||||||
|     'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php', |     'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php', | ||||||
|     'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php', |     'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php', | ||||||
|     'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php', |     'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php', | ||||||
| @@ -3789,7 +3790,6 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialRepositoryField' => 'DifferentialCoreCustomField', |     'DifferentialRepositoryField' => 'DifferentialCoreCustomField', | ||||||
|     'DifferentialRepositoryLookup' => 'Phobject', |     'DifferentialRepositoryLookup' => 'Phobject', | ||||||
|     'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField', |     'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField', | ||||||
|     'DifferentialResultsTableView' => 'AphrontView', |  | ||||||
|     'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', |     'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', | ||||||
|     'DifferentialReviewedByField' => 'DifferentialCoreCustomField', |     'DifferentialReviewedByField' => 'DifferentialCoreCustomField', | ||||||
|     'DifferentialReviewer' => 'Phobject', |     'DifferentialReviewer' => 'Phobject', | ||||||
| @@ -4356,6 +4356,7 @@ phutil_register_library_map(array( | |||||||
|     'HarbormasterDAO' => 'PhabricatorLiskDAO', |     'HarbormasterDAO' => 'PhabricatorLiskDAO', | ||||||
|     'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation', |     'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation', | ||||||
|     'HarbormasterLeaseHostBuildStepImplementation' => 'HarbormasterBuildStepImplementation', |     'HarbormasterLeaseHostBuildStepImplementation' => 'HarbormasterBuildStepImplementation', | ||||||
|  |     'HarbormasterLintPropertyView' => 'AphrontView', | ||||||
|     'HarbormasterManagePlansCapability' => 'PhabricatorPolicyCapability', |     'HarbormasterManagePlansCapability' => 'PhabricatorPolicyCapability', | ||||||
|     'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow', |     'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow', | ||||||
|     'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow', |     'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow', | ||||||
| @@ -4383,6 +4384,7 @@ phutil_register_library_map(array( | |||||||
|     'HarbormasterTargetWorker' => 'HarbormasterWorker', |     'HarbormasterTargetWorker' => 'HarbormasterWorker', | ||||||
|     'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation', |     'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation', | ||||||
|     'HarbormasterUIEventListener' => 'PhabricatorEventListener', |     'HarbormasterUIEventListener' => 'PhabricatorEventListener', | ||||||
|  |     'HarbormasterUnitPropertyView' => 'AphrontView', | ||||||
|     'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation', |     'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation', | ||||||
|     'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation', |     'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation', | ||||||
|     'HarbormasterWorker' => 'PhabricatorWorker', |     'HarbormasterWorker' => 'PhabricatorWorker', | ||||||
| @@ -6730,6 +6732,7 @@ phutil_register_library_map(array( | |||||||
|       'PhabricatorProjectInterface', |       'PhabricatorProjectInterface', | ||||||
|       'PhabricatorDestructibleInterface', |       'PhabricatorDestructibleInterface', | ||||||
|       'PhabricatorSpacesInterface', |       'PhabricatorSpacesInterface', | ||||||
|  |       'PhabricatorMentionableInterface', | ||||||
|     ), |     ), | ||||||
|     'PholioMockCommentController' => 'PholioController', |     'PholioMockCommentController' => 'PholioController', | ||||||
|     'PholioMockEditController' => 'PholioController', |     'PholioMockEditController' => 'PholioController', | ||||||
|   | |||||||
| @@ -264,11 +264,11 @@ final class PhabricatorCalendarEventSearchEngine | |||||||
|     $list = new PHUIObjectItemListView(); |     $list = new PHUIObjectItemListView(); | ||||||
|  |  | ||||||
|     foreach ($events as $event) { |     foreach ($events as $event) { | ||||||
|       $from = phabricator_datetime($event->getDateFrom(), $viewer); |  | ||||||
|       $duration = ''; |       $duration = ''; | ||||||
|  |       $event_date_info = $this->getEventDateLabel($event); | ||||||
|       $creator_handle = $handles[$event->getUserPHID()]; |       $creator_handle = $handles[$event->getUserPHID()]; | ||||||
|  |  | ||||||
|       $attendees = array(); |       $attendees = array(); | ||||||
|  |  | ||||||
|       foreach ($event->getInvitees() as $invitee) { |       foreach ($event->getInvitees() as $invitee) { | ||||||
|         $attendees[] = $invitee->getInviteePHID(); |         $attendees[] = $invitee->getInviteePHID(); | ||||||
|       } |       } | ||||||
| @@ -287,8 +287,8 @@ final class PhabricatorCalendarEventSearchEngine | |||||||
|  |  | ||||||
|       $item = id(new PHUIObjectItemView()) |       $item = id(new PHUIObjectItemView()) | ||||||
|         ->setHeader($viewer->renderHandle($event->getPHID())->render()) |         ->setHeader($viewer->renderHandle($event->getPHID())->render()) | ||||||
|  |         ->addAttribute($event_date_info) | ||||||
|         ->addAttribute($attendees) |         ->addAttribute($attendees) | ||||||
|         ->addIcon('none', $from) |  | ||||||
|         ->addIcon('none', $duration); |         ->addIcon('none', $duration); | ||||||
|  |  | ||||||
|       $list->addItem($item); |       $list->addItem($item); | ||||||
| @@ -520,4 +520,41 @@ final class PhabricatorCalendarEventSearchEngine | |||||||
|  |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function getEventDateLabel($event) { | ||||||
|  |     $viewer = $this->requireViewer(); | ||||||
|  |  | ||||||
|  |     $from_datetime = PhabricatorTime::getDateTimeFromEpoch( | ||||||
|  |       $event->getDateFrom(), | ||||||
|  |       $viewer); | ||||||
|  |     $to_datetime = PhabricatorTime::getDateTimeFromEpoch( | ||||||
|  |       $event->getDateTo(), | ||||||
|  |       $viewer); | ||||||
|  |  | ||||||
|  |     $from_date_formatted = $from_datetime->format('Y m d'); | ||||||
|  |     $to_date_formatted = $to_datetime->format('Y m d'); | ||||||
|  |  | ||||||
|  |     if ($event->getIsAllDay()) { | ||||||
|  |       if ($from_date_formatted == $to_date_formatted) { | ||||||
|  |         return pht( | ||||||
|  |           '%s, All Day', | ||||||
|  |           phabricator_date($event->getDateFrom(), $viewer)); | ||||||
|  |       } else { | ||||||
|  |         return pht( | ||||||
|  |           '%s - %s, All Day', | ||||||
|  |           phabricator_date($event->getDateFrom(), $viewer), | ||||||
|  |           phabricator_date($event->getDateTo(), $viewer)); | ||||||
|  |       } | ||||||
|  |     } else if ($from_date_formatted == $to_date_formatted) { | ||||||
|  |       return pht( | ||||||
|  |         '%s - %s', | ||||||
|  |         phabricator_datetime($event->getDateFrom(), $viewer), | ||||||
|  |         phabricator_time($event->getDateTo(), $viewer)); | ||||||
|  |     } else { | ||||||
|  |       return pht( | ||||||
|  |         '%s - %s', | ||||||
|  |         phabricator_datetime($event->getDateFrom(), $viewer), | ||||||
|  |         phabricator_datetime($event->getDateTo(), $viewer)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -160,6 +160,7 @@ final class DifferentialCreateDiffConduitAPIMethod | |||||||
|  |  | ||||||
|     return array( |     return array( | ||||||
|       'diffid' => $diff->getID(), |       'diffid' => $diff->getID(), | ||||||
|  |       'phid' => $diff->getPHID(), | ||||||
|       'uri' => $uri, |       'uri' => $uri, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -265,8 +265,25 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|       $revision_detail_box->setInfoView($revision_warnings); |       $revision_detail_box->setInfoView($revision_warnings); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     $detail_diffs = array_select_keys( | ||||||
|  |       $diffs, | ||||||
|  |       array($diff_vs, $target->getID())); | ||||||
|  |     $detail_diffs = mpull($detail_diffs, null, 'getPHID'); | ||||||
|  |  | ||||||
|  |     $buildables = id(new HarbormasterBuildableQuery()) | ||||||
|  |       ->setViewer($user) | ||||||
|  |       ->withBuildablePHIDs(array_keys($detail_diffs)) | ||||||
|  |       ->withManualBuildables(false) | ||||||
|  |       ->needBuilds(true) | ||||||
|  |       ->needTargets(true) | ||||||
|  |       ->execute(); | ||||||
|  |     $buildables = mpull($buildables, null, 'getBuildablePHID'); | ||||||
|  |     foreach ($detail_diffs as $diff_phid => $detail_diff) { | ||||||
|  |       $detail_diff->attachBuildable(idx($buildables, $diff_phid)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $diff_detail_box = $this->buildDiffDetailView( |     $diff_detail_box = $this->buildDiffDetailView( | ||||||
|       array_select_keys($diffs, array($diff_vs, $target->getID())), |       $detail_diffs, | ||||||
|       $revision, |       $revision, | ||||||
|       $field_list); |       $field_list); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -38,7 +38,6 @@ final class DifferentialLintField | |||||||
|     $keys = array( |     $keys = array( | ||||||
|       'arc:lint', |       'arc:lint', | ||||||
|       'arc:lint-excuse', |       'arc:lint-excuse', | ||||||
|       'arc:lint-postponed', |  | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     $properties = id(new DifferentialDiffProperty())->loadAllWhere( |     $properties = id(new DifferentialDiffProperty())->loadAllWhere( | ||||||
| @@ -51,208 +50,72 @@ final class DifferentialLintField | |||||||
|       $diff->attachProperty($key, idx($properties, $key)); |       $diff->attachProperty($key, idx($properties, $key)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $path_changesets = mpull($diff->loadChangesets(), 'getID', 'getFilename'); |     $status = $this->renderLintStatus($diff); | ||||||
|  |  | ||||||
|     $lstar = DifferentialRevisionUpdateHistoryView::renderDiffLintStar($diff); |     $lint = array(); | ||||||
|     $lmsg = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); |  | ||||||
|     $ldata = $diff->getProperty('arc:lint'); |  | ||||||
|     $ltail = null; |  | ||||||
|  |  | ||||||
|     $rows = array(); |     $buildable = $diff->getBuildable(); | ||||||
|  |     if ($buildable) { | ||||||
|     $rows[] = array( |       $target_phids = array(); | ||||||
|       'style'     => 'star', |       foreach ($buildable->getBuilds() as $build) { | ||||||
|       'name'      => $lstar, |         foreach ($build->getBuildTargets() as $target) { | ||||||
|       'value'     => $lmsg, |           $target_phids[] = $target->getPHID(); | ||||||
|       'show'      => true, |         } | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     $excuse = $diff->getProperty('arc:lint-excuse'); |  | ||||||
|     if ($excuse) { |  | ||||||
|       $rows[] = array( |  | ||||||
|         'style'   => 'excuse', |  | ||||||
|         'name'    => 'Excuse', |  | ||||||
|         'value'   => phutil_escape_html_newlines($excuse), |  | ||||||
|         'show'    => true, |  | ||||||
|       ); |  | ||||||
|       } |       } | ||||||
|  |  | ||||||
|     $show_limit = 10; |       $lint = id(new HarbormasterBuildLintMessage())->loadAllWhere( | ||||||
|     $hidden = array(); |         'buildTargetPHID IN (%Ls) LIMIT 25', | ||||||
|  |         $target_phids); | ||||||
|     if ($ldata) { |  | ||||||
|       $ldata = igroup($ldata, 'path'); |  | ||||||
|       foreach ($ldata as $path => $messages) { |  | ||||||
|  |  | ||||||
|         $rows[] = array( |  | ||||||
|           'style' => 'section', |  | ||||||
|           'name'  => $path, |  | ||||||
|           'show'  => $show_limit, |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         foreach ($messages as $message) { |  | ||||||
|           $path = idx($message, 'path'); |  | ||||||
|           $line = idx($message, 'line'); |  | ||||||
|  |  | ||||||
|           $code = idx($message, 'code'); |  | ||||||
|           $severity = idx($message, 'severity'); |  | ||||||
|  |  | ||||||
|           $name = idx($message, 'name'); |  | ||||||
|           $description = idx($message, 'description'); |  | ||||||
|  |  | ||||||
|           $line_link = pht('line %d', intval($line)); |  | ||||||
|           if (isset($path_changesets[$path])) { |  | ||||||
|             $href = '#C'.$path_changesets[$path].'NL'.max(1, $line); |  | ||||||
|  |  | ||||||
|             // TODO: We are always showing the active diff |  | ||||||
|             // if ($diff->getID() != $this->getDiff()->getID()) { |  | ||||||
|             //  $href = '/D'.$diff->getRevisionID().'?id='.$diff->getID().$href; |  | ||||||
|             // } |  | ||||||
|  |  | ||||||
|             $line_link = phutil_tag( |  | ||||||
|               'a', |  | ||||||
|               array( |  | ||||||
|                 'href' => $href, |  | ||||||
|               ), |  | ||||||
|               $line_link); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|           if ($show_limit) { |     if (!$lint) { | ||||||
|             --$show_limit; |       // No Harbormaster messages, so look for legacy messages and make them | ||||||
|             $show = true; |       // look like modern messages. | ||||||
|  |       $legacy_lint = $diff->getProperty('arc:lint'); | ||||||
|  |       if ($legacy_lint) { | ||||||
|  |         // Show the top 100 legacy lint messages. Previously, we showed some | ||||||
|  |         // by default and let the user toggle the rest. With modern messages, | ||||||
|  |         // we can send the user to the Harbormaster detail page. Just show | ||||||
|  |         // "a lot" of messages in legacy cases to try to strike a balance | ||||||
|  |         // between implementation simplicitly and compatibility. | ||||||
|  |         $legacy_lint = array_slice($legacy_lint, 0, 100); | ||||||
|  |  | ||||||
|  |         $target = new HarbormasterBuildTarget(); | ||||||
|  |         foreach ($legacy_lint as $message) { | ||||||
|  |           try { | ||||||
|  |             $modern = HarbormasterBuildLintMessage::newFromDictionary( | ||||||
|  |               $target, | ||||||
|  |               $this->getModernLintMessageDictionary($message)); | ||||||
|  |             $lint[] = $modern; | ||||||
|  |           } catch (Exception $ex) { | ||||||
|  |             // Ignore any poorly formatted messages. | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($lint) { | ||||||
|  |       $path_map = mpull($diff->loadChangesets(), 'getID', 'getFilename'); | ||||||
|  |       foreach ($path_map as $path => $id) { | ||||||
|  |         $href = '#C'.$id.'NL'; | ||||||
|  |  | ||||||
|  |         // TODO: When the diff is not the right-hand-size diff, we should | ||||||
|  |         // ideally adjust this URI to be absolute. | ||||||
|  |  | ||||||
|  |         $path_map[$path] = $href; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $view = id(new HarbormasterLintPropertyView()) | ||||||
|  |         ->setPathURIMap($path_map) | ||||||
|  |         ->setLintMessages($lint); | ||||||
|     } else { |     } else { | ||||||
|             $show = false; |       $view = null; | ||||||
|             if (empty($hidden[$severity])) { |  | ||||||
|               $hidden[$severity] = 0; |  | ||||||
|             } |  | ||||||
|             $hidden[$severity]++; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|           $rows[] = array( |     return array( | ||||||
|             'style' => $this->getSeverityStyle($severity), |       $status, | ||||||
|             'name'  => ucwords($severity), |       $view, | ||||||
|             'value' => hsprintf( |  | ||||||
|               '(%s) %s at %s', |  | ||||||
|               $code, |  | ||||||
|               $name, |  | ||||||
|               $line_link), |  | ||||||
|             'show'  => $show, |  | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|           if (!empty($message['locations'])) { |  | ||||||
|             $locations = array(); |  | ||||||
|             foreach ($message['locations'] as $location) { |  | ||||||
|               $other_line = idx($location, 'line'); |  | ||||||
|               $locations[] = |  | ||||||
|                 idx($location, 'path', $path). |  | ||||||
|                 ($other_line ? ":{$other_line}" : ''); |  | ||||||
|             } |  | ||||||
|             $description .= "\n".pht( |  | ||||||
|               'Other locations: %s', |  | ||||||
|               implode(', ', $locations)); |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           if (strlen($description)) { |  | ||||||
|             $rows[] = array( |  | ||||||
|               'style' => 'details', |  | ||||||
|               'value' => phutil_escape_html_newlines($description), |  | ||||||
|               'show'  => false, |  | ||||||
|             ); |  | ||||||
|             if (empty($hidden['details'])) { |  | ||||||
|               $hidden['details'] = 0; |  | ||||||
|             } |  | ||||||
|             $hidden['details']++; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $postponed = $diff->getProperty('arc:lint-postponed'); |  | ||||||
|     if ($postponed) { |  | ||||||
|       foreach ($postponed as $linter) { |  | ||||||
|         $rows[] = array( |  | ||||||
|           'style' => $this->getPostponedStyle(), |  | ||||||
|           'name' => 'Postponed', |  | ||||||
|           'value' => $linter, |  | ||||||
|           'show'  => false, |  | ||||||
|           ); |  | ||||||
|         if (empty($hidden['postponed'])) { |  | ||||||
|           $hidden['postponed'] = 0; |  | ||||||
|         } |  | ||||||
|         $hidden['postponed']++; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $show_string = $this->renderShowString($hidden); |  | ||||||
|  |  | ||||||
|     $view = new DifferentialResultsTableView(); |  | ||||||
|     $view->setRows($rows); |  | ||||||
|     $view->setShowMoreString($show_string); |  | ||||||
|  |  | ||||||
|     return $view->render(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private function getSeverityStyle($severity) { |  | ||||||
|     $map = array( |  | ||||||
|       ArcanistLintSeverity::SEVERITY_ERROR      => 'red', |  | ||||||
|       ArcanistLintSeverity::SEVERITY_WARNING    => 'yellow', |  | ||||||
|       ArcanistLintSeverity::SEVERITY_AUTOFIX    => 'yellow', |  | ||||||
|       ArcanistLintSeverity::SEVERITY_ADVICE     => 'yellow', |  | ||||||
|     ); |  | ||||||
|     return idx($map, $severity); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private function getPostponedStyle() { |  | ||||||
|     return 'blue'; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private function renderShowString(array $hidden) { |  | ||||||
|     if (!$hidden) { |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Reorder hidden things by severity. |  | ||||||
|     $hidden = array_select_keys( |  | ||||||
|       $hidden, |  | ||||||
|       array( |  | ||||||
|         ArcanistLintSeverity::SEVERITY_ERROR, |  | ||||||
|         ArcanistLintSeverity::SEVERITY_WARNING, |  | ||||||
|         ArcanistLintSeverity::SEVERITY_AUTOFIX, |  | ||||||
|         ArcanistLintSeverity::SEVERITY_ADVICE, |  | ||||||
|         'details', |  | ||||||
|         'postponed', |  | ||||||
|       )) + $hidden; |  | ||||||
|  |  | ||||||
|     $show = array(); |  | ||||||
|     foreach ($hidden as $key => $value) { |  | ||||||
|       switch ($key) { |  | ||||||
|         case ArcanistLintSeverity::SEVERITY_ERROR: |  | ||||||
|           $show[] = pht('%d Error(s)', $value); |  | ||||||
|           break; |  | ||||||
|         case ArcanistLintSeverity::SEVERITY_WARNING: |  | ||||||
|           $show[] = pht('%d Warning(s)', $value); |  | ||||||
|           break; |  | ||||||
|         case ArcanistLintSeverity::SEVERITY_AUTOFIX: |  | ||||||
|           $show[] = pht('%d Auto-Fix(es)', $value); |  | ||||||
|           break; |  | ||||||
|         case ArcanistLintSeverity::SEVERITY_ADVICE: |  | ||||||
|           $show[] = pht('%d Advice(s)', $value); |  | ||||||
|           break; |  | ||||||
|         case 'details': |  | ||||||
|           $show[] = pht('%d Detail(s)', $value); |  | ||||||
|           break; |  | ||||||
|         case 'postponed': |  | ||||||
|           $show[] = pht('%d Postponed', $value); |  | ||||||
|           break; |  | ||||||
|         default: |  | ||||||
|           $show[] = $value; |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return pht( |  | ||||||
|       'Show Full Lint Results (%s)', |  | ||||||
|       implode(', ', $show)); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getWarningsForDetailView() { |   public function getWarningsForDetailView() { | ||||||
| @@ -278,4 +141,50 @@ final class DifferentialLintField | |||||||
|     return $warnings; |     return $warnings; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function renderLintStatus(DifferentialDiff $diff) { | ||||||
|  |     $colors = array( | ||||||
|  |       DifferentialLintStatus::LINT_NONE => 'grey', | ||||||
|  |       DifferentialLintStatus::LINT_OKAY => 'green', | ||||||
|  |       DifferentialLintStatus::LINT_WARN => 'yellow', | ||||||
|  |       DifferentialLintStatus::LINT_FAIL => 'red', | ||||||
|  |       DifferentialLintStatus::LINT_SKIP => 'blue', | ||||||
|  |       DifferentialLintStatus::LINT_AUTO_SKIP => 'blue', | ||||||
|  |       DifferentialLintStatus::LINT_POSTPONED => 'blue', | ||||||
|  |     ); | ||||||
|  |     $icon_color = idx($colors, $diff->getLintStatus(), 'grey'); | ||||||
|  |  | ||||||
|  |     $message = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); | ||||||
|  |  | ||||||
|  |     $excuse = $diff->getProperty('arc:lint-excuse'); | ||||||
|  |     if (strlen($excuse)) { | ||||||
|  |       $excuse = array( | ||||||
|  |         phutil_tag('strong', array(), pht('Excuse:')), | ||||||
|  |         ' ', | ||||||
|  |         phutil_escape_html_newlines($excuse), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $status = id(new PHUIStatusListView()) | ||||||
|  |       ->addItem( | ||||||
|  |         id(new PHUIStatusItemView()) | ||||||
|  |           ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) | ||||||
|  |           ->setTarget($message) | ||||||
|  |           ->setNote($excuse)); | ||||||
|  |  | ||||||
|  |     return $status; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function getModernLintMessageDictionary(array $map) { | ||||||
|  |     // Strip out `null` values to satisfy stricter typechecks. | ||||||
|  |     foreach ($map as $key => $value) { | ||||||
|  |       if ($value === null) { | ||||||
|  |         unset($map[$key]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TODO: We might need to remap some stuff here? | ||||||
|  |     return $map; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -48,182 +48,67 @@ final class DifferentialUnitField | |||||||
|       $diff->attachProperty($key, idx($properties, $key)); |       $diff->attachProperty($key, idx($properties, $key)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff); |     $status = $this->renderUnitStatus($diff); | ||||||
|     $umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); |  | ||||||
|  |  | ||||||
|     $rows = array(); |     $unit = array(); | ||||||
|  |  | ||||||
|     $rows[] = array( |     $buildable = $diff->getBuildable(); | ||||||
|       'style' => 'star', |     if ($buildable) { | ||||||
|       'name'  => $ustar, |       $target_phids = array(); | ||||||
|       'value' => $umsg, |       foreach ($buildable->getBuilds() as $build) { | ||||||
|       'show'  => true, |         foreach ($build->getBuildTargets() as $target) { | ||||||
|     ); |           $target_phids[] = $target->getPHID(); | ||||||
|  |  | ||||||
|     $excuse = $diff->getProperty('arc:unit-excuse'); |  | ||||||
|     if ($excuse) { |  | ||||||
|       $rows[] = array( |  | ||||||
|         'style' => 'excuse', |  | ||||||
|         'name'  => pht('Excuse'), |  | ||||||
|         'value' => phutil_escape_html_newlines($excuse), |  | ||||||
|         'show'  => true, |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $show_limit = 10; |  | ||||||
|     $hidden = array(); |  | ||||||
|  |  | ||||||
|     $udata = $diff->getProperty('arc:unit'); |  | ||||||
|     if ($udata) { |  | ||||||
|       $sort_map = array( |  | ||||||
|         ArcanistUnitTestResult::RESULT_BROKEN     => 0, |  | ||||||
|         ArcanistUnitTestResult::RESULT_FAIL       => 1, |  | ||||||
|         ArcanistUnitTestResult::RESULT_UNSOUND    => 2, |  | ||||||
|         ArcanistUnitTestResult::RESULT_SKIP       => 3, |  | ||||||
|         ArcanistUnitTestResult::RESULT_POSTPONED  => 4, |  | ||||||
|         ArcanistUnitTestResult::RESULT_PASS       => 5, |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|       foreach ($udata as $key => $test) { |  | ||||||
|         $udata[$key]['sort'] = idx($sort_map, idx($test, 'result')); |  | ||||||
|       } |  | ||||||
|       $udata = isort($udata, 'sort'); |  | ||||||
|       $engine = new PhabricatorMarkupEngine(); |  | ||||||
|       $engine->setViewer($this->getViewer()); |  | ||||||
|       $markup_objects = array(); |  | ||||||
|       foreach ($udata as $key => $test) { |  | ||||||
|         $userdata = idx($test, 'userdata'); |  | ||||||
|         if ($userdata) { |  | ||||||
|           if ($userdata !== false) { |  | ||||||
|             $userdata = str_replace("\000", '', $userdata); |  | ||||||
|           } |  | ||||||
|           $markup_object = id(new PhabricatorMarkupOneOff()) |  | ||||||
|             ->setContent($userdata) |  | ||||||
|             ->setPreserveLinebreaks(true); |  | ||||||
|           $engine->addObject($markup_object, 'default'); |  | ||||||
|           $markup_objects[$key] = $markup_object; |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       $engine->process(); |  | ||||||
|       foreach ($udata as $key => $test) { |  | ||||||
|         $result = idx($test, 'result'); |  | ||||||
|  |  | ||||||
|         $default_hide = false; |       $unit = id(new HarbormasterBuildUnitMessage())->loadAllWhere( | ||||||
|         switch ($result) { |         'buildTargetPHID IN (%Ls) LIMIT 25', | ||||||
|           case ArcanistUnitTestResult::RESULT_POSTPONED: |         $target_phids); | ||||||
|           case ArcanistUnitTestResult::RESULT_PASS: |  | ||||||
|             $default_hide = true; |  | ||||||
|             break; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         if ($show_limit && !$default_hide) { |     if (!$unit) { | ||||||
|           --$show_limit; |       $legacy_unit = $diff->getProperty('arc:unit'); | ||||||
|           $show = true; |       if ($legacy_unit) { | ||||||
|  |         // Show the top 100 legacy unit messages. | ||||||
|  |         $legacy_unit = array_slice($legacy_unit, 0, 100); | ||||||
|  |  | ||||||
|  |         $target = new HarbormasterBuildTarget(); | ||||||
|  |         foreach ($legacy_unit as $message) { | ||||||
|  |           try { | ||||||
|  |             $modern = HarbormasterBuildUnitMessage::newFromDictionary( | ||||||
|  |               $target, | ||||||
|  |               $this->getModernUnitMessageDictionary($message)); | ||||||
|  |             $unit[] = $modern; | ||||||
|  |           } catch (Exception $ex) { | ||||||
|  |             // Just ignore it if legacy messages aren't formatted like | ||||||
|  |             // we expect. | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($unit) { | ||||||
|  |       $path_map = mpull($diff->loadChangesets(), 'getID', 'getFilename'); | ||||||
|  |       foreach ($path_map as $path => $id) { | ||||||
|  |         $href = '#C'.$id.'NL'; | ||||||
|  |  | ||||||
|  |         // TODO: When the diff is not the right-hand-size diff, we should | ||||||
|  |         // ideally adjust this URI to be absolute. | ||||||
|  |  | ||||||
|  |         $path_map[$path] = $href; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $view = id(new HarbormasterUnitPropertyView()) | ||||||
|  |         ->setPathURIMap($path_map) | ||||||
|  |         ->setUnitMessages($unit); | ||||||
|     } else { |     } else { | ||||||
|           $show = false; |       $view = null; | ||||||
|           if (empty($hidden[$result])) { |  | ||||||
|             $hidden[$result] = 0; |  | ||||||
|           } |  | ||||||
|           $hidden[$result]++; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         $value = idx($test, 'name'); |     return array( | ||||||
|  |       $status, | ||||||
|         $namespace = idx($test, 'namespace'); |       $view, | ||||||
|         if ($namespace) { |  | ||||||
|           $value = $namespace.'::'.$value; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!empty($test['link'])) { |  | ||||||
|           $value = phutil_tag( |  | ||||||
|             'a', |  | ||||||
|             array( |  | ||||||
|               'href' => $test['link'], |  | ||||||
|               'target' => '_blank', |  | ||||||
|             ), |  | ||||||
|             $value); |  | ||||||
|         } |  | ||||||
|         $rows[] = array( |  | ||||||
|           'style' => $this->getResultStyle($result), |  | ||||||
|           'name'  => ucwords($result), |  | ||||||
|           'value' => $value, |  | ||||||
|           'show'  => $show, |  | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|         if (isset($markup_objects[$key])) { |  | ||||||
|           $rows[] = array( |  | ||||||
|             'style' => 'details', |  | ||||||
|             'value' => $engine->getOutput($markup_objects[$key], 'default'), |  | ||||||
|             'show'  => false, |  | ||||||
|           ); |  | ||||||
|           if (empty($hidden['details'])) { |  | ||||||
|             $hidden['details'] = 0; |  | ||||||
|           } |  | ||||||
|           $hidden['details']++; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $show_string = $this->renderShowString($hidden); |  | ||||||
|  |  | ||||||
|     $view = new DifferentialResultsTableView(); |  | ||||||
|     $view->setRows($rows); |  | ||||||
|     $view->setShowMoreString($show_string); |  | ||||||
|  |  | ||||||
|     return $view->render(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private function getResultStyle($result) { |  | ||||||
|     $map = array( |  | ||||||
|       ArcanistUnitTestResult::RESULT_PASS       => 'green', |  | ||||||
|       ArcanistUnitTestResult::RESULT_FAIL       => 'red', |  | ||||||
|       ArcanistUnitTestResult::RESULT_SKIP       => 'blue', |  | ||||||
|       ArcanistUnitTestResult::RESULT_BROKEN     => 'red', |  | ||||||
|       ArcanistUnitTestResult::RESULT_UNSOUND    => 'yellow', |  | ||||||
|       ArcanistUnitTestResult::RESULT_POSTPONED  => 'blue', |  | ||||||
|     ); |  | ||||||
|     return idx($map, $result); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private function renderShowString(array $hidden) { |  | ||||||
|     if (!$hidden) { |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Reorder hidden things by severity. |  | ||||||
|     $hidden = array_select_keys( |  | ||||||
|       $hidden, |  | ||||||
|       array( |  | ||||||
|         ArcanistUnitTestResult::RESULT_BROKEN, |  | ||||||
|         ArcanistUnitTestResult::RESULT_FAIL, |  | ||||||
|         ArcanistUnitTestResult::RESULT_UNSOUND, |  | ||||||
|         ArcanistUnitTestResult::RESULT_SKIP, |  | ||||||
|         ArcanistUnitTestResult::RESULT_POSTPONED, |  | ||||||
|         ArcanistUnitTestResult::RESULT_PASS, |  | ||||||
|         'details', |  | ||||||
|       )) + $hidden; |  | ||||||
|  |  | ||||||
|     $noun = array( |  | ||||||
|       ArcanistUnitTestResult::RESULT_BROKEN     => pht('Broken'), |  | ||||||
|       ArcanistUnitTestResult::RESULT_FAIL       => pht('Failed'), |  | ||||||
|       ArcanistUnitTestResult::RESULT_UNSOUND    => pht('Unsound'), |  | ||||||
|       ArcanistUnitTestResult::RESULT_SKIP       => pht('Skipped'), |  | ||||||
|       ArcanistUnitTestResult::RESULT_POSTPONED  => pht('Postponed'), |  | ||||||
|       ArcanistUnitTestResult::RESULT_PASS       => pht('Passed'), |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     $show = array(); |  | ||||||
|     foreach ($hidden as $key => $value) { |  | ||||||
|       if ($key == 'details') { |  | ||||||
|         $show[] = pht('%d Detail(s)', $value); |  | ||||||
|       } else { |  | ||||||
|         $show[] = $value.' '.idx($noun, $key); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return pht( |  | ||||||
|       'Show Full Unit Results (%s)', |  | ||||||
|       implode(', ', $show)); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getWarningsForDetailView() { |   public function getWarningsForDetailView() { | ||||||
| @@ -248,4 +133,51 @@ final class DifferentialUnitField | |||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   private function renderUnitStatus(DifferentialDiff $diff) { | ||||||
|  |     $colors = array( | ||||||
|  |       DifferentialUnitStatus::UNIT_NONE => 'grey', | ||||||
|  |       DifferentialUnitStatus::UNIT_OKAY => 'green', | ||||||
|  |       DifferentialUnitStatus::UNIT_WARN => 'yellow', | ||||||
|  |       DifferentialUnitStatus::UNIT_FAIL => 'red', | ||||||
|  |       DifferentialUnitStatus::UNIT_SKIP => 'blue', | ||||||
|  |       DifferentialUnitStatus::UNIT_AUTO_SKIP => 'blue', | ||||||
|  |       DifferentialUnitStatus::UNIT_POSTPONED => 'blue', | ||||||
|  |     ); | ||||||
|  |     $icon_color = idx($colors, $diff->getUnitStatus(), 'grey'); | ||||||
|  |  | ||||||
|  |     $message = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); | ||||||
|  |  | ||||||
|  |     $excuse = $diff->getProperty('arc:unit-excuse'); | ||||||
|  |     if (strlen($excuse)) { | ||||||
|  |       $excuse = array( | ||||||
|  |         phutil_tag('strong', array(), pht('Excuse:')), | ||||||
|  |         ' ', | ||||||
|  |         phutil_escape_html_newlines($excuse), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $status = id(new PHUIStatusListView()) | ||||||
|  |       ->addItem( | ||||||
|  |         id(new PHUIStatusItemView()) | ||||||
|  |           ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) | ||||||
|  |           ->setTarget($message) | ||||||
|  |           ->setNote($excuse)); | ||||||
|  |  | ||||||
|  |     return $status; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function getModernUnitMessageDictionary(array $map) { | ||||||
|  |     // Strip out `null` values to satisfy stricter typechecks. | ||||||
|  |     foreach ($map as $key => $value) { | ||||||
|  |       if ($value === null) { | ||||||
|  |         unset($map[$key]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TODO: Remap more stuff here? | ||||||
|  |  | ||||||
|  |     return $map; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -587,6 +587,21 @@ final class DifferentialTransactionEditor | |||||||
|  |  | ||||||
|         $diff->setRevisionID($object->getID()); |         $diff->setRevisionID($object->getID()); | ||||||
|         $diff->save(); |         $diff->save(); | ||||||
|  |  | ||||||
|  |         // Update Harbormaster to set the containerPHID correctly for any | ||||||
|  |         // existing buildables. We may otherwise have buildables stuck with | ||||||
|  |         // the old (`null`) container. | ||||||
|  |  | ||||||
|  |         // TODO: This is a bit iffy, maybe we can find a cleaner approach? | ||||||
|  |         $table = new HarbormasterBuildable(); | ||||||
|  |         $conn_w = $table->establishConnection('w'); | ||||||
|  |         queryfx( | ||||||
|  |           $conn_w, | ||||||
|  |           'UPDATE %T SET containerPHID = %s WHERE buildablePHID = %s', | ||||||
|  |           $table->getTableName(), | ||||||
|  |           $object->getPHID(), | ||||||
|  |           $diff->getPHID()); | ||||||
|  |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,6 +39,7 @@ final class DifferentialDiff | |||||||
|   private $changesets = self::ATTACHABLE; |   private $changesets = self::ATTACHABLE; | ||||||
|   private $revision = self::ATTACHABLE; |   private $revision = self::ATTACHABLE; | ||||||
|   private $properties = array(); |   private $properties = array(); | ||||||
|  |   private $buildable = self::ATTACHABLE; | ||||||
|  |  | ||||||
|   protected function getConfiguration() { |   protected function getConfiguration() { | ||||||
|     return array( |     return array( | ||||||
| @@ -323,6 +324,15 @@ final class DifferentialDiff | |||||||
|     return $this->assertAttachedKey($this->properties, $key); |     return $this->assertAttachedKey($this->properties, $key); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function attachBuildable(HarbormasterBuildable $buildable = null) { | ||||||
|  |     $this->buildable = $buildable; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getBuildable() { | ||||||
|  |     return $this->assertAttached($this->buildable); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* -(  PhabricatorPolicyInterface  )----------------------------------------- */ | /* -(  PhabricatorPolicyInterface  )----------------------------------------- */ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,115 +0,0 @@ | |||||||
| <?php |  | ||||||
|  |  | ||||||
| final class DifferentialResultsTableView extends AphrontView { |  | ||||||
|  |  | ||||||
|   private $rows; |  | ||||||
|   private $showMoreString; |  | ||||||
|  |  | ||||||
|   public function setRows(array $rows) { |  | ||||||
|     $this->rows = $rows; |  | ||||||
|     return $this; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public function setShowMoreString($show_more_string) { |  | ||||||
|     $this->showMoreString = $show_more_string; |  | ||||||
|     return $this; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public function render() { |  | ||||||
|  |  | ||||||
|     $rows = array(); |  | ||||||
|  |  | ||||||
|     $any_hidden = false; |  | ||||||
|     foreach ($this->rows as $row) { |  | ||||||
|  |  | ||||||
|       $style = idx($row, 'style'); |  | ||||||
|       switch ($style) { |  | ||||||
|         case 'section': |  | ||||||
|           $cells = phutil_tag( |  | ||||||
|             'th', |  | ||||||
|             array( |  | ||||||
|               'colspan' => 2, |  | ||||||
|             ), |  | ||||||
|             idx($row, 'name')); |  | ||||||
|           break; |  | ||||||
|         default: |  | ||||||
|           $name = phutil_tag( |  | ||||||
|             'th', |  | ||||||
|             array( |  | ||||||
|             ), |  | ||||||
|             idx($row, 'name')); |  | ||||||
|           $value = phutil_tag( |  | ||||||
|             'td', |  | ||||||
|             array( |  | ||||||
|             ), |  | ||||||
|             idx($row, 'value')); |  | ||||||
|           $cells = array($name, $value); |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       $show = idx($row, 'show'); |  | ||||||
|  |  | ||||||
|       $rows[] = javelin_tag( |  | ||||||
|         'tr', |  | ||||||
|         array( |  | ||||||
|           'style' => $show ? null : 'display: none', |  | ||||||
|           'sigil' => $show ? null : 'differential-results-row-toggle', |  | ||||||
|           'class' => 'differential-results-row-'.$style, |  | ||||||
|         ), |  | ||||||
|         $cells); |  | ||||||
|  |  | ||||||
|       if (!$show) { |  | ||||||
|         $any_hidden = true; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ($any_hidden) { |  | ||||||
|       $show_more = javelin_tag( |  | ||||||
|         'a', |  | ||||||
|         array( |  | ||||||
|           'href'        => '#', |  | ||||||
|           'mustcapture' => true, |  | ||||||
|         ), |  | ||||||
|         $this->showMoreString); |  | ||||||
|  |  | ||||||
|       $hide_more = javelin_tag( |  | ||||||
|         'a', |  | ||||||
|         array( |  | ||||||
|           'href'        => '#', |  | ||||||
|           'mustcapture' => true, |  | ||||||
|         ), |  | ||||||
|         pht('Hide')); |  | ||||||
|  |  | ||||||
|       $rows[] = javelin_tag( |  | ||||||
|         'tr', |  | ||||||
|         array( |  | ||||||
|           'class' => 'differential-results-row-show', |  | ||||||
|           'sigil' => 'differential-results-row-show', |  | ||||||
|         ), |  | ||||||
|         phutil_tag('th', array('colspan' => 2), $show_more)); |  | ||||||
|  |  | ||||||
|       $rows[] = javelin_tag( |  | ||||||
|         'tr', |  | ||||||
|         array( |  | ||||||
|           'class' => 'differential-results-row-show', |  | ||||||
|           'sigil' => 'differential-results-row-hide', |  | ||||||
|           'style' => 'display: none', |  | ||||||
|         ), |  | ||||||
|         phutil_tag('th', array('colspan' => 2), $hide_more)); |  | ||||||
|  |  | ||||||
|       $this->initBehavior('differential-show-field-details'); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $this->requireResource('differential-results-table-css'); |  | ||||||
|  |  | ||||||
|     return javelin_tag( |  | ||||||
|       'table', |  | ||||||
|       array( |  | ||||||
|         'class' => 'differential-results-table', |  | ||||||
|         'sigil' => 'differential-results-table', |  | ||||||
|       ), |  | ||||||
|       $rows); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -18,6 +18,8 @@ final class HarbormasterSendMessageConduitAPIMethod | |||||||
|  |  | ||||||
|     return array( |     return array( | ||||||
|       'buildTargetPHID' => 'required phid', |       'buildTargetPHID' => 'required phid', | ||||||
|  |       'lint' => 'optional list<wild>', | ||||||
|  |       'unit' => 'optional list<wild>', | ||||||
|       'type' => 'required '.$type_const, |       'type' => 'required '.$type_const, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| @@ -40,10 +42,31 @@ final class HarbormasterSendMessageConduitAPIMethod | |||||||
|       throw new Exception(pht('No such build target!')); |       throw new Exception(pht('No such build target!')); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $message = HarbormasterBuildMessage::initializeNewMessage($viewer) |     $save = array(); | ||||||
|  |  | ||||||
|  |     $lint_messages = $request->getValue('lint', array()); | ||||||
|  |     foreach ($lint_messages as $lint) { | ||||||
|  |       $save[] = HarbormasterBuildLintMessage::newFromDictionary( | ||||||
|  |         $build_target, | ||||||
|  |         $lint); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $unit_messages = $request->getValue('unit', array()); | ||||||
|  |     foreach ($unit_messages as $unit) { | ||||||
|  |       $save[] = HarbormasterBuildUnitMessage::newFromDictionary( | ||||||
|  |         $build_target, | ||||||
|  |         $unit); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $save[] = HarbormasterBuildMessage::initializeNewMessage($viewer) | ||||||
|       ->setBuildTargetPHID($build_target->getPHID()) |       ->setBuildTargetPHID($build_target->getPHID()) | ||||||
|       ->setType($message_type) |       ->setType($message_type); | ||||||
|       ->save(); |  | ||||||
|  |     $build_target->openTransaction(); | ||||||
|  |     foreach ($save as $object) { | ||||||
|  |       $object->save(); | ||||||
|  |     } | ||||||
|  |     $build_target->saveTransaction(); | ||||||
|  |  | ||||||
|     // If the build has completely paused because all steps are blocked on |     // If the build has completely paused because all steps are blocked on | ||||||
|     // waiting targets, this will resume it. |     // waiting targets, this will resume it. | ||||||
|   | |||||||
| @@ -48,9 +48,7 @@ final class HarbormasterBuildViewController | |||||||
|     $this->buildPropertyLists($box, $build, $actions); |     $this->buildPropertyLists($box, $build, $actions); | ||||||
|  |  | ||||||
|     $crumbs = $this->buildApplicationCrumbs(); |     $crumbs = $this->buildApplicationCrumbs(); | ||||||
|     $crumbs->addTextCrumb( |     $this->addBuildableCrumb($crumbs, $build->getBuildable()); | ||||||
|       $build->getBuildable()->getMonogram(), |  | ||||||
|       '/'.$build->getBuildable()->getMonogram()); |  | ||||||
|     $crumbs->addTextCrumb($title); |     $crumbs->addTextCrumb($title); | ||||||
|  |  | ||||||
|     if ($generation === null || $generation > $build->getBuildGeneration() || |     if ($generation === null || $generation > $build->getBuildGeneration() || | ||||||
| @@ -99,28 +97,51 @@ final class HarbormasterBuildViewController | |||||||
|       $item->setIcon($icon, $color); |       $item->setIcon($icon, $color); | ||||||
|       $status_view->addItem($item); |       $status_view->addItem($item); | ||||||
|  |  | ||||||
|       $properties->addProperty(pht('Name'), $build_target->getName()); |       $when = array(); | ||||||
|  |       $started = $build_target->getDateStarted(); | ||||||
|  |       $now = PhabricatorTime::getNow(); | ||||||
|  |       if ($started) { | ||||||
|  |         $ended = $build_target->getDateCompleted(); | ||||||
|  |         if ($ended) { | ||||||
|  |           $when[] = pht( | ||||||
|  |             'Completed at %s', | ||||||
|  |             phabricator_datetime($started, $viewer)); | ||||||
|  |  | ||||||
|       if ($build_target->getDateStarted() !== null) { |           $duration = ($ended - $started); | ||||||
|         $properties->addProperty( |           if ($duration) { | ||||||
|           pht('Started'), |             $when[] = pht( | ||||||
|           phabricator_datetime($build_target->getDateStarted(), $viewer)); |               'Built for %s', | ||||||
|         if ($build_target->isComplete()) { |               phutil_format_relative_time_detailed($duration)); | ||||||
|           $properties->addProperty( |  | ||||||
|             pht('Completed'), |  | ||||||
|             phabricator_datetime($build_target->getDateCompleted(), $viewer)); |  | ||||||
|           $properties->addProperty( |  | ||||||
|             pht('Duration'), |  | ||||||
|             phutil_format_relative_time_detailed( |  | ||||||
|               $build_target->getDateCompleted() - |  | ||||||
|               $build_target->getDateStarted())); |  | ||||||
|           } else { |           } else { | ||||||
|  |             $when[] = pht('Built instantly'); | ||||||
|  |           } | ||||||
|  |         } else { | ||||||
|  |           $when[] = pht( | ||||||
|  |             'Started at %s', | ||||||
|  |             phabricator_datetime($started, $viewer)); | ||||||
|  |           $duration = ($now - $started); | ||||||
|  |           if ($duration) { | ||||||
|  |             $when[] = pht( | ||||||
|  |               'Running for %s', | ||||||
|  |               phutil_format_relative_time_detailed($duration)); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         $created = $build_target->getDateCreated(); | ||||||
|  |         $when[] = pht( | ||||||
|  |           'Queued at %s', | ||||||
|  |           phabricator_datetime($started, $viewer)); | ||||||
|  |         $duration = ($now - $created); | ||||||
|  |         if ($duration) { | ||||||
|  |           $when[] = pht( | ||||||
|  |             'Waiting for %s', | ||||||
|  |             phutil_format_relative_time_detailed($duration)); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|       $properties->addProperty( |       $properties->addProperty( | ||||||
|             pht('Elapsed'), |         pht('When'), | ||||||
|             phutil_format_relative_time_detailed( |         phutil_implode_html(" \xC2\xB7 ", $when)); | ||||||
|               time() - $build_target->getDateStarted())); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       $properties->addProperty(pht('Status'), $status_view); |       $properties->addProperty(pht('Status'), $status_view); | ||||||
|  |  | ||||||
| @@ -162,9 +183,7 @@ final class HarbormasterBuildViewController | |||||||
|       $variables = $build_target->getVariables(); |       $variables = $build_target->getVariables(); | ||||||
|       if ($variables) { |       if ($variables) { | ||||||
|         $properties = new PHUIPropertyListView(); |         $properties = new PHUIPropertyListView(); | ||||||
|         foreach ($variables as $key => $value) { |         $properties->addRawContent($this->buildProperties($variables)); | ||||||
|           $properties->addProperty($key, $value); |  | ||||||
|         } |  | ||||||
|         $target_box->addPropertyList($properties, pht('Variables')); |         $target_box->addPropertyList($properties, pht('Variables')); | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -183,7 +202,12 @@ final class HarbormasterBuildViewController | |||||||
|       } |       } | ||||||
|  |  | ||||||
|       $properties = new PHUIPropertyListView(); |       $properties = new PHUIPropertyListView(); | ||||||
|       $properties->addProperty(pht('Build Target ID'), $build_target->getID()); |       $properties->addProperty( | ||||||
|  |         pht('Build Target ID'), | ||||||
|  |         $build_target->getID()); | ||||||
|  |       $properties->addProperty( | ||||||
|  |         pht('Build Target PHID'), | ||||||
|  |         $build_target->getPHID()); | ||||||
|       $target_box->addPropertyList($properties, pht('Metadata')); |       $target_box->addPropertyList($properties, pht('Metadata')); | ||||||
|  |  | ||||||
|       $targets[] = $target_box; |       $targets[] = $target_box; | ||||||
| @@ -528,4 +552,30 @@ final class HarbormasterBuildViewController | |||||||
|     return $table; |     return $table; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function buildProperties(array $properties) { | ||||||
|  |     ksort($properties); | ||||||
|  |  | ||||||
|  |     $rows = array(); | ||||||
|  |     foreach ($properties as $key => $value) { | ||||||
|  |       $rows[] = array( | ||||||
|  |         $key, | ||||||
|  |         $value, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $table = id(new AphrontTableView($rows)) | ||||||
|  |       ->setHeaders( | ||||||
|  |         array( | ||||||
|  |           pht('Key'), | ||||||
|  |           pht('Value'), | ||||||
|  |         )) | ||||||
|  |       ->setColumnClasses( | ||||||
|  |         array( | ||||||
|  |           'pri right', | ||||||
|  |           'wide', | ||||||
|  |         )); | ||||||
|  |  | ||||||
|  |     return $table; | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,21 +3,12 @@ | |||||||
| final class HarbormasterBuildableViewController | final class HarbormasterBuildableViewController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|   private $id; |   public function handleRequest(AphrontRequest $request) { | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|   public function willProcessRequest(array $data) { |  | ||||||
|     $this->id = $data['id']; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public function processRequest() { |  | ||||||
|     $request = $this->getRequest(); |  | ||||||
|     $viewer = $request->getUser(); |  | ||||||
|  |  | ||||||
|     $id = $this->id; |  | ||||||
|  |  | ||||||
|     $buildable = id(new HarbormasterBuildableQuery()) |     $buildable = id(new HarbormasterBuildableQuery()) | ||||||
|       ->setViewer($viewer) |       ->setViewer($viewer) | ||||||
|       ->withIDs(array($id)) |       ->withIDs(array($request->getURIData('id'))) | ||||||
|       ->needBuildableHandles(true) |       ->needBuildableHandles(true) | ||||||
|       ->needContainerHandles(true) |       ->needContainerHandles(true) | ||||||
|       ->executeOne(); |       ->executeOne(); | ||||||
| @@ -25,6 +16,8 @@ final class HarbormasterBuildableViewController | |||||||
|       return new Aphront404Response(); |       return new Aphront404Response(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     $id = $buildable->getID(); | ||||||
|  |  | ||||||
|     // Pull builds and build targets. |     // Pull builds and build targets. | ||||||
|     $builds = id(new HarbormasterBuildQuery()) |     $builds = id(new HarbormasterBuildQuery()) | ||||||
|       ->setViewer($viewer) |       ->setViewer($viewer) | ||||||
| @@ -32,7 +25,10 @@ final class HarbormasterBuildableViewController | |||||||
|       ->needBuildTargets(true) |       ->needBuildTargets(true) | ||||||
|       ->execute(); |       ->execute(); | ||||||
|  |  | ||||||
|  |     list($lint, $unit) = $this->renderLintAndUnit($builds); | ||||||
|  |  | ||||||
|     $buildable->attachBuilds($builds); |     $buildable->attachBuilds($builds); | ||||||
|  |     $object = $buildable->getBuildableObject(); | ||||||
|  |  | ||||||
|     $build_list = $this->buildBuildList($buildable); |     $build_list = $this->buildBuildList($buildable); | ||||||
|  |  | ||||||
| @@ -55,12 +51,14 @@ final class HarbormasterBuildableViewController | |||||||
|     $this->buildPropertyLists($box, $buildable, $actions); |     $this->buildPropertyLists($box, $buildable, $actions); | ||||||
|  |  | ||||||
|     $crumbs = $this->buildApplicationCrumbs(); |     $crumbs = $this->buildApplicationCrumbs(); | ||||||
|     $crumbs->addTextCrumb("B{$id}"); |     $crumbs->addTextCrumb($buildable->getMonogram()); | ||||||
|  |  | ||||||
|     return $this->buildApplicationPage( |     return $this->buildApplicationPage( | ||||||
|       array( |       array( | ||||||
|         $crumbs, |         $crumbs, | ||||||
|         $box, |         $box, | ||||||
|  |         $lint, | ||||||
|  |         $unit, | ||||||
|         $build_list, |         $build_list, | ||||||
|         $timeline, |         $timeline, | ||||||
|       ), |       ), | ||||||
| @@ -144,16 +142,16 @@ final class HarbormasterBuildableViewController | |||||||
|       ->setActionList($actions); |       ->setActionList($actions); | ||||||
|     $box->addPropertyList($properties); |     $box->addPropertyList($properties); | ||||||
|  |  | ||||||
|     $properties->addProperty( |  | ||||||
|       pht('Buildable'), |  | ||||||
|       $buildable->getBuildableHandle()->renderLink()); |  | ||||||
|  |  | ||||||
|     if ($buildable->getContainerHandle() !== null) { |     if ($buildable->getContainerHandle() !== null) { | ||||||
|       $properties->addProperty( |       $properties->addProperty( | ||||||
|         pht('Container'), |         pht('Container'), | ||||||
|         $buildable->getContainerHandle()->renderLink()); |         $buildable->getContainerHandle()->renderLink()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     $properties->addProperty( | ||||||
|  |       pht('Buildable'), | ||||||
|  |       $buildable->getBuildableHandle()->renderLink()); | ||||||
|  |  | ||||||
|     $properties->addProperty( |     $properties->addProperty( | ||||||
|       pht('Origin'), |       pht('Origin'), | ||||||
|       $buildable->getIsManualBuildable() |       $buildable->getIsManualBuildable() | ||||||
| @@ -250,7 +248,66 @@ final class HarbormasterBuildableViewController | |||||||
|       $build_list->addItem($item); |       $build_list->addItem($item); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return $build_list; |     $build_list->setFlush(true); | ||||||
|  |  | ||||||
|  |     $box = id(new PHUIObjectBoxView()) | ||||||
|  |       ->setHeaderText(pht('Builds')) | ||||||
|  |       ->appendChild($build_list); | ||||||
|  |  | ||||||
|  |     return $box; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function renderLintAndUnit(array $builds) { | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     $targets = array(); | ||||||
|  |     foreach ($builds as $build) { | ||||||
|  |       foreach ($build->getBuildTargets() as $target) { | ||||||
|  |         $targets[] = $target; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!$targets) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $target_phids = mpull($targets, 'getPHID'); | ||||||
|  |  | ||||||
|  |     $lint_data = id(new HarbormasterBuildLintMessage())->loadAllWhere( | ||||||
|  |       'buildTargetPHID IN (%Ls) LIMIT 25', | ||||||
|  |       $target_phids); | ||||||
|  |  | ||||||
|  |     $unit_data = id(new HarbormasterBuildUnitMessage())->loadAllWhere( | ||||||
|  |       'buildTargetPHID IN (%Ls) LIMIT 25', | ||||||
|  |       $target_phids); | ||||||
|  |  | ||||||
|  |     if ($lint_data) { | ||||||
|  |       $lint_table = id(new HarbormasterLintPropertyView()) | ||||||
|  |         ->setUser($viewer) | ||||||
|  |         ->setLintMessages($lint_data); | ||||||
|  |  | ||||||
|  |       $lint = id(new PHUIObjectBoxView()) | ||||||
|  |         ->setHeaderText(pht('Lint Messages')) | ||||||
|  |         ->appendChild($lint_table); | ||||||
|  |     } else { | ||||||
|  |       $lint = null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($unit_data) { | ||||||
|  |       $unit_table = id(new HarbormasterUnitPropertyView()) | ||||||
|  |         ->setUser($viewer) | ||||||
|  |         ->setUnitMessages($unit_data); | ||||||
|  |  | ||||||
|  |       $unit = id(new PHUIObjectBoxView()) | ||||||
|  |         ->setHeaderText(pht('Unit Tests')) | ||||||
|  |         ->appendChild($unit_table); | ||||||
|  |     } else { | ||||||
|  |       $unit = null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return array($lint, $unit); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,4 +2,14 @@ | |||||||
|  |  | ||||||
| abstract class HarbormasterController extends PhabricatorController { | abstract class HarbormasterController extends PhabricatorController { | ||||||
|  |  | ||||||
|  |   protected function addBuildableCrumb( | ||||||
|  |     PHUICrumbsView $crumbs, | ||||||
|  |     HarbormasterBuildable $buildable) { | ||||||
|  |  | ||||||
|  |     $monogram = $buildable->getMonogram(); | ||||||
|  |     $uri = '/'.$monogram; | ||||||
|  |  | ||||||
|  |     $crumbs->addTextCrumb($monogram, $uri); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -332,6 +332,11 @@ final class HarbormasterBuildEngine extends Phobject { | |||||||
|         $message->save(); |         $message->save(); | ||||||
|  |  | ||||||
|         $target->setTargetStatus($new_status); |         $target->setTargetStatus($new_status); | ||||||
|  |  | ||||||
|  |         if ($target->isComplete()) { | ||||||
|  |           $target->setDateCompleted(PhabricatorTime::getNow()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         $target->save(); |         $target->save(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ final class HarbormasterBuildableQuery | |||||||
|   private $needContainerHandles; |   private $needContainerHandles; | ||||||
|   private $needBuildableHandles; |   private $needBuildableHandles; | ||||||
|   private $needBuilds; |   private $needBuilds; | ||||||
|  |   private $needTargets; | ||||||
|  |  | ||||||
|   public function withIDs(array $ids) { |   public function withIDs(array $ids) { | ||||||
|     $this->ids = $ids; |     $this->ids = $ids; | ||||||
| @@ -59,19 +60,17 @@ final class HarbormasterBuildableQuery | |||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function needTargets($need) { | ||||||
|  |     $this->needTargets = $need; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function newResultObject() { | ||||||
|  |     return new HarbormasterBuildable(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected function loadPage() { |   protected function loadPage() { | ||||||
|     $table = new HarbormasterBuildable(); |     return $this->loadStandardPage($this->newResultObject()); | ||||||
|     $conn_r = $table->establishConnection('r'); |  | ||||||
|  |  | ||||||
|     $data = queryfx_all( |  | ||||||
|       $conn_r, |  | ||||||
|       'SELECT * FROM %T %Q %Q %Q', |  | ||||||
|       $table->getTableName(), |  | ||||||
|       $this->buildWhereClause($conn_r), |  | ||||||
|       $this->buildOrderClause($conn_r), |  | ||||||
|       $this->buildLimitClause($conn_r)); |  | ||||||
|  |  | ||||||
|     return $table->loadAllFromArray($data); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function willFilterPage(array $page) { |   protected function willFilterPage(array $page) { | ||||||
| @@ -157,11 +156,12 @@ final class HarbormasterBuildableQuery | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->needBuilds) { |     if ($this->needBuilds || $this->needTargets) { | ||||||
|       $builds = id(new HarbormasterBuildQuery()) |       $builds = id(new HarbormasterBuildQuery()) | ||||||
|         ->setViewer($this->getViewer()) |         ->setViewer($this->getViewer()) | ||||||
|         ->setParentQuery($this) |         ->setParentQuery($this) | ||||||
|         ->withBuildablePHIDs(mpull($page, 'getPHID')) |         ->withBuildablePHIDs(mpull($page, 'getPHID')) | ||||||
|  |         ->needBuildTargets($this->needTargets) | ||||||
|         ->execute(); |         ->execute(); | ||||||
|       $builds = mgroup($builds, 'getBuildablePHID'); |       $builds = mgroup($builds, 'getBuildablePHID'); | ||||||
|       foreach ($page as $key => $buildable) { |       foreach ($page as $key => $buildable) { | ||||||
| @@ -172,47 +172,45 @@ final class HarbormasterBuildableQuery | |||||||
|     return $page; |     return $page; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { |   protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { | ||||||
|     $where = array(); |     $where = parent::buildWhereClauseParts($conn); | ||||||
|  |  | ||||||
|     if ($this->ids !== null) { |     if ($this->ids !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'id IN (%Ld)', |         'id IN (%Ld)', | ||||||
|         $this->ids); |         $this->ids); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->phids !== null) { |     if ($this->phids !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'phid IN (%Ls)', |         'phid IN (%Ls)', | ||||||
|         $this->phids); |         $this->phids); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->buildablePHIDs !== null) { |     if ($this->buildablePHIDs !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'buildablePHID IN (%Ls)', |         'buildablePHID IN (%Ls)', | ||||||
|         $this->buildablePHIDs); |         $this->buildablePHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->containerPHIDs !== null) { |     if ($this->containerPHIDs !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'containerPHID in (%Ls)', |         'containerPHID in (%Ls)', | ||||||
|         $this->containerPHIDs); |         $this->containerPHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->manualBuildables !== null) { |     if ($this->manualBuildables !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'isManualBuildable = %d', |         'isManualBuildable = %d', | ||||||
|         (int)$this->manualBuildables); |         (int)$this->manualBuildables); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $where[] = $this->buildPagingClause($conn_r); |     return $where; | ||||||
|  |  | ||||||
|     return $this->formatWhereClause($where); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getQueryApplicationClass() { |   public function getQueryApplicationClass() { | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ final class HarbormasterBuildUnitMessage | |||||||
|  |  | ||||||
|   public static function initializeNewUnitMessage( |   public static function initializeNewUnitMessage( | ||||||
|     HarbormasterBuildTarget $build_target) { |     HarbormasterBuildTarget $build_target) { | ||||||
|     return id(new HarbormasterBuildLintMessage()) |     return id(new HarbormasterBuildUnitMessage()) | ||||||
|       ->setBuildTargetPHID($build_target->getPHID()); |       ->setBuildTargetPHID($build_target->getPHID()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,78 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class HarbormasterLintPropertyView extends AphrontView { | ||||||
|  |  | ||||||
|  |   private $pathURIMap = array(); | ||||||
|  |   private $lintMessages = array(); | ||||||
|  |  | ||||||
|  |   public function setPathURIMap(array $map) { | ||||||
|  |     $this->pathURIMap = $map; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setLintMessages(array $messages) { | ||||||
|  |     assert_instances_of($messages, 'HarbormasterBuildLintMessage'); | ||||||
|  |     $this->lintMessages = $messages; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function render() { | ||||||
|  |     $rows = array(); | ||||||
|  |     foreach ($this->lintMessages as $message) { | ||||||
|  |       $path = $message->getPath(); | ||||||
|  |       $line = $message->getLine(); | ||||||
|  |  | ||||||
|  |       $href = null; | ||||||
|  |       if (strlen(idx($this->pathURIMap, $path))) { | ||||||
|  |         $href = $this->pathURIMap[$path].max($line, 1); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $severity = $this->renderSeverity($message->getSeverity()); | ||||||
|  |  | ||||||
|  |       $location = $path.':'.$line; | ||||||
|  |       if (strlen($href)) { | ||||||
|  |         $location = phutil_tag( | ||||||
|  |           'a', | ||||||
|  |           array( | ||||||
|  |             'href' => $href, | ||||||
|  |           ), | ||||||
|  |           $location); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $rows[] = array( | ||||||
|  |         $location, | ||||||
|  |         $severity, | ||||||
|  |         $message->getCode(), | ||||||
|  |         $message->getName(), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $table = id(new AphrontTableView($rows)) | ||||||
|  |       ->setHeaders( | ||||||
|  |         array( | ||||||
|  |           pht('Location'), | ||||||
|  |           pht('Severity'), | ||||||
|  |           pht('Code'), | ||||||
|  |           pht('Message'), | ||||||
|  |         )) | ||||||
|  |       ->setColumnClasses( | ||||||
|  |         array( | ||||||
|  |           'pri', | ||||||
|  |           null, | ||||||
|  |           null, | ||||||
|  |           'wide', | ||||||
|  |         )); | ||||||
|  |  | ||||||
|  |     return $table; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function renderSeverity($severity) { | ||||||
|  |     $names = ArcanistLintSeverity::getLintSeverities(); | ||||||
|  |     $name = idx($names, $severity, $severity); | ||||||
|  |  | ||||||
|  |     // TODO: Add some color here? | ||||||
|  |  | ||||||
|  |     return $name; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,90 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class HarbormasterUnitPropertyView extends AphrontView { | ||||||
|  |  | ||||||
|  |   private $pathURIMap = array(); | ||||||
|  |   private $unitMessages = array(); | ||||||
|  |  | ||||||
|  |   public function setPathURIMap(array $map) { | ||||||
|  |     $this->pathURIMap = $map; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setUnitMessages(array $messages) { | ||||||
|  |     assert_instances_of($messages, 'HarbormasterBuildUnitMessage'); | ||||||
|  |     $this->unitMessages = $messages; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function render() { | ||||||
|  |  | ||||||
|  |     $rows = array(); | ||||||
|  |     $any_duration = false; | ||||||
|  |     foreach ($this->unitMessages as $message) { | ||||||
|  |       $result = $this->renderResult($message->getResult()); | ||||||
|  |  | ||||||
|  |       $duration = $message->getDuration(); | ||||||
|  |       if ($duration !== null) { | ||||||
|  |         $any_duration = true; | ||||||
|  |         $duration = pht('%s ms', new PhutilNumber((int)(1000 * $duration))); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $name = $message->getName(); | ||||||
|  |  | ||||||
|  |       $namespace = $message->getNamespace(); | ||||||
|  |       if (strlen($namespace)) { | ||||||
|  |         $name = $namespace.'::'.$name; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $engine = $message->getEngine(); | ||||||
|  |       if (strlen($engine)) { | ||||||
|  |         $name = $engine.' > '.$name; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $rows[] = array( | ||||||
|  |         $result, | ||||||
|  |         $duration, | ||||||
|  |         $name, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     $table = id(new AphrontTableView($rows)) | ||||||
|  |       ->setHeaders( | ||||||
|  |         array( | ||||||
|  |           pht('Result'), | ||||||
|  |           pht('Time'), | ||||||
|  |           pht('Test'), | ||||||
|  |         )) | ||||||
|  |       ->setColumnClasses( | ||||||
|  |         array( | ||||||
|  |           null, | ||||||
|  |           null, | ||||||
|  |           'pri wide', | ||||||
|  |         )) | ||||||
|  |       ->setColumnVisibility( | ||||||
|  |         array( | ||||||
|  |           true, | ||||||
|  |           $any_duration, | ||||||
|  |         )); | ||||||
|  |  | ||||||
|  |     return $table; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function renderResult($result) { | ||||||
|  |     $names = array( | ||||||
|  |       ArcanistUnitTestResult::RESULT_BROKEN     => pht('Broken'), | ||||||
|  |       ArcanistUnitTestResult::RESULT_FAIL       => pht('Failed'), | ||||||
|  |       ArcanistUnitTestResult::RESULT_UNSOUND    => pht('Unsound'), | ||||||
|  |       ArcanistUnitTestResult::RESULT_SKIP       => pht('Skipped'), | ||||||
|  |       ArcanistUnitTestResult::RESULT_POSTPONED  => pht('Postponed'), | ||||||
|  |       ArcanistUnitTestResult::RESULT_PASS       => pht('Passed'), | ||||||
|  |     ); | ||||||
|  |     $result = idx($names, $result, $result); | ||||||
|  |  | ||||||
|  |     // TODO: Add some color. | ||||||
|  |  | ||||||
|  |     return $result; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -59,7 +59,7 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { | |||||||
|       $target->setTargetStatus($next_status); |       $target->setTargetStatus($next_status); | ||||||
|  |  | ||||||
|       if ($target->isComplete()) { |       if ($target->isComplete()) { | ||||||
|         $target->setDateCompleted(time()); |         $target->setDateCompleted(PhabricatorTime::getNow()); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       $target->save(); |       $target->save(); | ||||||
| @@ -70,12 +70,12 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { | |||||||
|     } catch (HarbormasterBuildFailureException $ex) { |     } catch (HarbormasterBuildFailureException $ex) { | ||||||
|       // A build step wants to fail explicitly. |       // A build step wants to fail explicitly. | ||||||
|       $target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED); |       $target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED); | ||||||
|       $target->setDateCompleted(time()); |       $target->setDateCompleted(PhabricatorTime::getNow()); | ||||||
|       $target->save(); |       $target->save(); | ||||||
|     } catch (HarbormasterBuildAbortedException $ex) { |     } catch (HarbormasterBuildAbortedException $ex) { | ||||||
|       // A build step is aborting because the build has been restarted. |       // A build step is aborting because the build has been restarted. | ||||||
|       $target->setTargetStatus(HarbormasterBuildTarget::STATUS_ABORTED); |       $target->setTargetStatus(HarbormasterBuildTarget::STATUS_ABORTED); | ||||||
|       $target->setDateCompleted(time()); |       $target->setDateCompleted(PhabricatorTime::getNow()); | ||||||
|       $target->save(); |       $target->save(); | ||||||
|     } catch (Exception $ex) { |     } catch (Exception $ex) { | ||||||
|       phlog($ex); |       phlog($ex); | ||||||
|   | |||||||
| @@ -10,7 +10,8 @@ final class PholioMock extends PholioDAO | |||||||
|     PhabricatorApplicationTransactionInterface, |     PhabricatorApplicationTransactionInterface, | ||||||
|     PhabricatorProjectInterface, |     PhabricatorProjectInterface, | ||||||
|     PhabricatorDestructibleInterface, |     PhabricatorDestructibleInterface, | ||||||
|     PhabricatorSpacesInterface { |     PhabricatorSpacesInterface, | ||||||
|  |     PhabricatorMentionableInterface { | ||||||
|  |  | ||||||
|   const MARKUP_FIELD_DESCRIPTION  = 'markup:description'; |   const MARKUP_FIELD_DESCRIPTION  = 'markup:description'; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ final class PHUIStatusItemView extends AphrontTagView { | |||||||
|   const ICON_MINUS = 'fa-minus-circle'; |   const ICON_MINUS = 'fa-minus-circle'; | ||||||
|   const ICON_OPEN = 'fa-circle-o'; |   const ICON_OPEN = 'fa-circle-o'; | ||||||
|   const ICON_CLOCK = 'fa-clock-o'; |   const ICON_CLOCK = 'fa-clock-o'; | ||||||
|  |   const ICON_STAR = 'fa-star'; | ||||||
|  |  | ||||||
|   /* render_textarea */ |   /* render_textarea */ | ||||||
|   public function setIcon($icon, $color = null, $label = null) { |   public function setIcon($icon, $color = null, $label = null) { | ||||||
|   | |||||||
| @@ -1,78 +0,0 @@ | |||||||
| /** |  | ||||||
|  * @provides differential-results-table-css |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| table.differential-results-table { |  | ||||||
|   border-collapse: separate; |  | ||||||
|   width: 96%; |  | ||||||
|   font-size: 11px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table th { |  | ||||||
|   text-align: center; |  | ||||||
|   white-space: nowrap; |  | ||||||
|   vertical-align: middle; |  | ||||||
|   padding: 2px 4px; |  | ||||||
|   width: 50px; |  | ||||||
|   border-right: 1px solid #fff; |  | ||||||
|   background: #f7f7f7; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .device .differential-results-table th { |  | ||||||
|   white-space: normal; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table td { |  | ||||||
|   padding: 0 8px; |  | ||||||
|   margin: 0; |  | ||||||
|   vertical-align: middle; |  | ||||||
|   background: #f7f7f7; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-star th, |  | ||||||
| .differential-results-table tr.differential-results-row-star td { |  | ||||||
|   background: {$greybackground}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-section th { |  | ||||||
|   padding-top: 4px; |  | ||||||
|   text-align: left; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-excuse th { |  | ||||||
|   background: #3399ff; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-excuse td { |  | ||||||
|   padding-top: 8px; |  | ||||||
|   padding-right: 8px; |  | ||||||
|   padding-bottom: 8px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-red th { |  | ||||||
|   background: #ff4422; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-yellow th { |  | ||||||
|   background: #ffdd66; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-green th { |  | ||||||
|   background: #22dd44; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-blue th { |  | ||||||
|   background: #88bbff; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-details td { |  | ||||||
|   color: {$lightgreytext}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .differential-results-table tr.differential-results-row-show th { |  | ||||||
|   border-top: 1px solid #fff; |  | ||||||
|   border-right: none; |  | ||||||
|   padding: 2px; |  | ||||||
|   color: {$bluetext}; |  | ||||||
|   background: {$greybackground}; |  | ||||||
| } |  | ||||||
| @@ -1,40 +0,0 @@ | |||||||
| /** |  | ||||||
|  * @provides javelin-behavior-differential-show-field-details |  | ||||||
|  * @requires javelin-behavior |  | ||||||
|  *           javelin-stratcom |  | ||||||
|  *           javelin-dom |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| JX.behavior('differential-show-field-details', function() { |  | ||||||
|  |  | ||||||
|   JX.Stratcom.listen( |  | ||||||
|     'click', |  | ||||||
|     ['differential-results-row-show', 'tag:a'], |  | ||||||
|     function(e) { |  | ||||||
|       toggle(e, true); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|   JX.Stratcom.listen( |  | ||||||
|     'click', |  | ||||||
|     ['differential-results-row-hide', 'tag:a'], |  | ||||||
|     function(e) { |  | ||||||
|       toggle(e, false); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|   function toggle(e, show) { |  | ||||||
|     e.kill(); |  | ||||||
|  |  | ||||||
|     var f = show ? JX.DOM.show : JX.DOM.hide; |  | ||||||
|     var g = show ? JX.DOM.hide : JX.DOM.show; |  | ||||||
|  |  | ||||||
|     var table = e.getNode('differential-results-table'); |  | ||||||
|     var rows  = JX.DOM.scry(table, 'tr', 'differential-results-row-toggle'); |  | ||||||
|     for (var ii = 0; ii < rows.length; ii++) { |  | ||||||
|       f(rows[ii]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     g(JX.DOM.find(table, 'tr', 'differential-results-row-show')); |  | ||||||
|     f(JX.DOM.find(table, 'tr', 'differential-results-row-hide')); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
		Reference in New Issue
	
	Block a user
	 epriestley
					epriestley