1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 02:02:41 +01:00

(stable) Promote 2016 Week 10

This commit is contained in:
epriestley 2016-03-04 17:24:53 -08:00
commit 2c67d9c8ac
145 changed files with 3717 additions and 2425 deletions

View file

@ -7,7 +7,7 @@
*/ */
return array( return array(
'names' => array( 'names' => array(
'core.pkg.css' => 'ecdca229', 'core.pkg.css' => 'dd1447be',
'core.pkg.js' => '7d8faf57', 'core.pkg.js' => '7d8faf57',
'darkconsole.pkg.js' => 'e7393ebb', 'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9', 'differential.pkg.css' => '2de124c9',
@ -25,7 +25,7 @@ return array(
'rsrc/css/aphront/notification.css' => '7f684b62', 'rsrc/css/aphront/notification.css' => '7f684b62',
'rsrc/css/aphront/panel-view.css' => '8427b78d', 'rsrc/css/aphront/panel-view.css' => '8427b78d',
'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758', 'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758',
'rsrc/css/aphront/table-view.css' => '6d01d468', 'rsrc/css/aphront/table-view.css' => '036b6cdc',
'rsrc/css/aphront/tokenizer.css' => '056da01b', 'rsrc/css/aphront/tokenizer.css' => '056da01b',
'rsrc/css/aphront/tooltip.css' => '1a07aea8', 'rsrc/css/aphront/tooltip.css' => '1a07aea8',
'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c', 'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c',
@ -52,7 +52,7 @@ return array(
'rsrc/css/application/conpherence/update.css' => 'faf6be09', 'rsrc/css/application/conpherence/update.css' => 'faf6be09',
'rsrc/css/application/conpherence/widget-pane.css' => '775eaaba', 'rsrc/css/application/conpherence/widget-pane.css' => '775eaaba',
'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4', 'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4',
'rsrc/css/application/countdown/timer.css' => 'e7544472', 'rsrc/css/application/countdown/timer.css' => '96696f21',
'rsrc/css/application/daemon/bulk-job.css' => 'df9c1d4a', 'rsrc/css/application/daemon/bulk-job.css' => 'df9c1d4a',
'rsrc/css/application/dashboard/dashboard.css' => 'eb458607', 'rsrc/css/application/dashboard/dashboard.css' => 'eb458607',
'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a',
@ -70,16 +70,16 @@ return array(
'rsrc/css/application/feed/feed.css' => 'ecd4ec57', 'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
'rsrc/css/application/files/global-drag-and-drop.css' => '5c1b47c2', 'rsrc/css/application/files/global-drag-and-drop.css' => '5c1b47c2',
'rsrc/css/application/flag/flag.css' => '5337623f', 'rsrc/css/application/flag/flag.css' => '5337623f',
'rsrc/css/application/harbormaster/harbormaster.css' => 'b0758ca5', 'rsrc/css/application/harbormaster/harbormaster.css' => '834879db',
'rsrc/css/application/herald/herald-test.css' => 'a52e323e', 'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
'rsrc/css/application/herald/herald.css' => '826075fa', 'rsrc/css/application/herald/herald.css' => '46596280',
'rsrc/css/application/maniphest/batch-editor.css' => 'b0f0b6d5', 'rsrc/css/application/maniphest/batch-editor.css' => 'b0f0b6d5',
'rsrc/css/application/maniphest/report.css' => '9b9580b7', 'rsrc/css/application/maniphest/report.css' => '9b9580b7',
'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b', 'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b',
'rsrc/css/application/maniphest/task-summary.css' => '11cc5344', 'rsrc/css/application/maniphest/task-summary.css' => '11cc5344',
'rsrc/css/application/objectselector/object-selector.css' => '85ee8ce6', 'rsrc/css/application/objectselector/object-selector.css' => '85ee8ce6',
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b', 'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
'rsrc/css/application/paste/paste.css' => 'a5157c48', 'rsrc/css/application/paste/paste.css' => '1898e534',
'rsrc/css/application/people/people-profile.css' => '2473d929', 'rsrc/css/application/people/people-profile.css' => '2473d929',
'rsrc/css/application/phame/phame.css' => '737792d6', 'rsrc/css/application/phame/phame.css' => '737792d6',
'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee', 'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee',
@ -92,18 +92,18 @@ return array(
'rsrc/css/application/policy/policy-edit.css' => '815c66f7', 'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43', 'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
'rsrc/css/application/policy/policy.css' => '957ea14c', 'rsrc/css/application/policy/policy.css' => '957ea14c',
'rsrc/css/application/ponder/ponder-view.css' => '212495e0', 'rsrc/css/application/ponder/ponder-view.css' => 'fbd45f96',
'rsrc/css/application/project/project-card-view.css' => '9418c97d', 'rsrc/css/application/project/project-card-view.css' => '9418c97d',
'rsrc/css/application/project/project-view.css' => '298b7c5b', 'rsrc/css/application/project/project-view.css' => '9ce99f21',
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd',
'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae', 'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae',
'rsrc/css/application/search/search-results.css' => '7dea472c', 'rsrc/css/application/search/search-results.css' => '7dea472c',
'rsrc/css/application/slowvote/slowvote.css' => 'da0afb1b', 'rsrc/css/application/slowvote/slowvote.css' => 'a94b7230',
'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
'rsrc/css/application/uiexample/example.css' => '528b19de', 'rsrc/css/application/uiexample/example.css' => '528b19de',
'rsrc/css/core/core.css' => '5b3563c8', 'rsrc/css/core/core.css' => 'd0801452',
'rsrc/css/core/remarkup.css' => 'fc228f08', 'rsrc/css/core/remarkup.css' => 'fc228f08',
'rsrc/css/core/syntax.css' => '9fd11da8', 'rsrc/css/core/syntax.css' => '9fd11da8',
'rsrc/css/core/z-index.css' => '5b6fcf3f', 'rsrc/css/core/z-index.css' => '5b6fcf3f',
@ -123,8 +123,8 @@ return array(
'rsrc/css/phui/phui-action-panel.css' => '91c7b835', 'rsrc/css/phui/phui-action-panel.css' => '91c7b835',
'rsrc/css/phui/phui-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-badge.css' => 'f25c3476',
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
'rsrc/css/phui/phui-box.css' => 'dd1294d3', 'rsrc/css/phui/phui-box.css' => 'c9e01148',
'rsrc/css/phui/phui-button.css' => 'edf464e9', 'rsrc/css/phui/phui-button.css' => 'a64a8de6',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5', 'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5',
'rsrc/css/phui/phui-document-pro.css' => '92d5b648', 'rsrc/css/phui/phui-document-pro.css' => '92d5b648',
@ -134,7 +134,8 @@ return array(
'rsrc/css/phui/phui-fontkit.css' => '9cda225e', 'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e', 'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e',
'rsrc/css/phui/phui-form.css' => 'aac1d51d', 'rsrc/css/phui/phui-form.css' => 'aac1d51d',
'rsrc/css/phui/phui-header-view.css' => 'a6d7b20d', 'rsrc/css/phui/phui-head-thing.css' => '11731da0',
'rsrc/css/phui/phui-header-view.css' => 'fc4acf14',
'rsrc/css/phui/phui-hovercard.css' => 'de1a2119', 'rsrc/css/phui/phui-hovercard.css' => 'de1a2119',
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad', 'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
'rsrc/css/phui/phui-icon.css' => '3f33ab57', 'rsrc/css/phui/phui-icon.css' => '3f33ab57',
@ -142,7 +143,7 @@ return array(
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1', 'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
'rsrc/css/phui/phui-info-view.css' => '6d7c3509', 'rsrc/css/phui/phui-info-view.css' => '6d7c3509',
'rsrc/css/phui/phui-list.css' => '9da2aa00', 'rsrc/css/phui/phui-list.css' => '9da2aa00',
'rsrc/css/phui/phui-object-box.css' => '407eaf5a', 'rsrc/css/phui/phui-object-box.css' => '91628842',
'rsrc/css/phui/phui-object-item-list-view.css' => '18b2ce8e', 'rsrc/css/phui/phui-object-item-list-view.css' => '18b2ce8e',
'rsrc/css/phui/phui-pager.css' => 'bea33d23', 'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', 'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
@ -151,10 +152,10 @@ return array(
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591', 'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
'rsrc/css/phui/phui-segment-bar-view.css' => '46342871', 'rsrc/css/phui/phui-segment-bar-view.css' => '46342871',
'rsrc/css/phui/phui-spacing.css' => '042804d6', 'rsrc/css/phui/phui-spacing.css' => '042804d6',
'rsrc/css/phui/phui-status.css' => '888cedb8', 'rsrc/css/phui/phui-status.css' => '37309046',
'rsrc/css/phui/phui-tag-view.css' => '9d5d4400', 'rsrc/css/phui/phui-tag-view.css' => '9d5d4400',
'rsrc/css/phui/phui-timeline-view.css' => '2efceff8', 'rsrc/css/phui/phui-timeline-view.css' => '2efceff8',
'rsrc/css/phui/phui-two-column-view.css' => 'a317616a', 'rsrc/css/phui/phui-two-column-view.css' => 'd0ad8c10',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7', 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96', 'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96',
@ -523,7 +524,7 @@ return array(
'aphront-list-filter-view-css' => '5d6f0526', 'aphront-list-filter-view-css' => '5d6f0526',
'aphront-multi-column-view-css' => 'fd18389d', 'aphront-multi-column-view-css' => 'fd18389d',
'aphront-panel-view-css' => '8427b78d', 'aphront-panel-view-css' => '8427b78d',
'aphront-table-view-css' => '6d01d468', 'aphront-table-view-css' => '036b6cdc',
'aphront-tokenizer-control-css' => '056da01b', 'aphront-tokenizer-control-css' => '056da01b',
'aphront-tooltip-css' => '1a07aea8', 'aphront-tooltip-css' => '1a07aea8',
'aphront-typeahead-control-css' => 'd4f16145', 'aphront-typeahead-control-css' => 'd4f16145',
@ -558,8 +559,8 @@ return array(
'font-fontawesome' => 'c43323c5', 'font-fontawesome' => 'c43323c5',
'font-lato' => 'c7ccd872', 'font-lato' => 'c7ccd872',
'global-drag-and-drop-css' => '5c1b47c2', 'global-drag-and-drop-css' => '5c1b47c2',
'harbormaster-css' => 'b0758ca5', 'harbormaster-css' => '834879db',
'herald-css' => '826075fa', 'herald-css' => '46596280',
'herald-rule-editor' => '746ca158', 'herald-rule-editor' => '746ca158',
'herald-test-css' => 'a52e323e', 'herald-test-css' => 'a52e323e',
'inline-comment-summary-css' => '51efda3a', 'inline-comment-summary-css' => '51efda3a',
@ -740,7 +741,7 @@ return array(
'multirow-row-manager' => 'b5d57730', 'multirow-row-manager' => 'b5d57730',
'owners-path-editor' => 'aa1733d0', 'owners-path-editor' => 'aa1733d0',
'owners-path-editor-css' => '2f00933b', 'owners-path-editor-css' => '2f00933b',
'paste-css' => 'a5157c48', 'paste-css' => '1898e534',
'path-typeahead' => 'f7fc67ec', 'path-typeahead' => 'f7fc67ec',
'people-profile-css' => '2473d929', 'people-profile-css' => '2473d929',
'phabricator-action-list-view-css' => 'c5eba19d', 'phabricator-action-list-view-css' => 'c5eba19d',
@ -748,8 +749,8 @@ return array(
'phabricator-busy' => '59a7976a', 'phabricator-busy' => '59a7976a',
'phabricator-chatlog-css' => 'd295b020', 'phabricator-chatlog-css' => 'd295b020',
'phabricator-content-source-view-css' => '4b8b05d4', 'phabricator-content-source-view-css' => '4b8b05d4',
'phabricator-core-css' => '5b3563c8', 'phabricator-core-css' => 'd0801452',
'phabricator-countdown-css' => 'e7544472', 'phabricator-countdown-css' => '96696f21',
'phabricator-dashboard-css' => 'eb458607', 'phabricator-dashboard-css' => 'eb458607',
'phabricator-drag-and-drop-file-upload' => '81f182b5', 'phabricator-drag-and-drop-file-upload' => '81f182b5',
'phabricator-draggable-list' => '5a13c79f', 'phabricator-draggable-list' => '5a13c79f',
@ -772,7 +773,7 @@ return array(
'phabricator-search-results-css' => '7dea472c', 'phabricator-search-results-css' => '7dea472c',
'phabricator-shaped-request' => '7cbe244b', 'phabricator-shaped-request' => '7cbe244b',
'phabricator-side-menu-view-css' => '3a3d9f41', 'phabricator-side-menu-view-css' => '3a3d9f41',
'phabricator-slowvote-css' => 'da0afb1b', 'phabricator-slowvote-css' => 'a94b7230',
'phabricator-source-code-view-css' => 'cbeef983', 'phabricator-source-code-view-css' => 'cbeef983',
'phabricator-standard-page-view' => 'e709f6d0', 'phabricator-standard-page-view' => 'e709f6d0',
'phabricator-textareautils' => '5813016a', 'phabricator-textareautils' => '5813016a',
@ -802,8 +803,8 @@ return array(
'phui-action-panel-css' => '91c7b835', 'phui-action-panel-css' => '91c7b835',
'phui-badge-view-css' => 'f25c3476', 'phui-badge-view-css' => 'f25c3476',
'phui-big-info-view-css' => 'bd903741', 'phui-big-info-view-css' => 'bd903741',
'phui-box-css' => 'dd1294d3', 'phui-box-css' => 'c9e01148',
'phui-button-css' => 'edf464e9', 'phui-button-css' => 'a64a8de6',
'phui-calendar-css' => 'ccabe893', 'phui-calendar-css' => 'ccabe893',
'phui-calendar-day-css' => 'd1cf6f93', 'phui-calendar-day-css' => 'd1cf6f93',
'phui-calendar-list-css' => 'c1c7f338', 'phui-calendar-list-css' => 'c1c7f338',
@ -818,7 +819,8 @@ return array(
'phui-fontkit-css' => '9cda225e', 'phui-fontkit-css' => '9cda225e',
'phui-form-css' => 'aac1d51d', 'phui-form-css' => 'aac1d51d',
'phui-form-view-css' => '4a1a0f5e', 'phui-form-view-css' => '4a1a0f5e',
'phui-header-view-css' => 'a6d7b20d', 'phui-head-thing-view-css' => '11731da0',
'phui-header-view-css' => 'fc4acf14',
'phui-hovercard' => '1bd28176', 'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'de1a2119', 'phui-hovercard-view-css' => 'de1a2119',
'phui-icon-set-selector-css' => '1ab67aad', 'phui-icon-set-selector-css' => '1ab67aad',
@ -828,7 +830,7 @@ return array(
'phui-info-view-css' => '6d7c3509', 'phui-info-view-css' => '6d7c3509',
'phui-inline-comment-view-css' => '0fdb3667', 'phui-inline-comment-view-css' => '0fdb3667',
'phui-list-view-css' => '9da2aa00', 'phui-list-view-css' => '9da2aa00',
'phui-object-box-css' => '407eaf5a', 'phui-object-box-css' => '91628842',
'phui-object-item-list-view-css' => '18b2ce8e', 'phui-object-item-list-view-css' => '18b2ce8e',
'phui-pager-css' => 'bea33d23', 'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e', 'phui-pinboard-view-css' => '2495140e',
@ -837,11 +839,11 @@ return array(
'phui-remarkup-preview-css' => '1a8f2591', 'phui-remarkup-preview-css' => '1a8f2591',
'phui-segment-bar-view-css' => '46342871', 'phui-segment-bar-view-css' => '46342871',
'phui-spacing-css' => '042804d6', 'phui-spacing-css' => '042804d6',
'phui-status-list-view-css' => '888cedb8', 'phui-status-list-view-css' => '37309046',
'phui-tag-view-css' => '9d5d4400', 'phui-tag-view-css' => '9d5d4400',
'phui-theme-css' => '027ba77e', 'phui-theme-css' => '027ba77e',
'phui-timeline-view-css' => '2efceff8', 'phui-timeline-view-css' => '2efceff8',
'phui-two-column-view-css' => 'a317616a', 'phui-two-column-view-css' => 'd0ad8c10',
'phui-workboard-color-css' => 'ac6fe6a7', 'phui-workboard-color-css' => 'ac6fe6a7',
'phui-workboard-view-css' => 'e6d89647', 'phui-workboard-view-css' => 'e6d89647',
'phui-workcard-view-css' => '3646fb96', 'phui-workcard-view-css' => '3646fb96',
@ -855,9 +857,9 @@ return array(
'policy-css' => '957ea14c', 'policy-css' => '957ea14c',
'policy-edit-css' => '815c66f7', 'policy-edit-css' => '815c66f7',
'policy-transaction-detail-css' => '82100a43', 'policy-transaction-detail-css' => '82100a43',
'ponder-view-css' => '212495e0', 'ponder-view-css' => 'fbd45f96',
'project-card-view-css' => '9418c97d', 'project-card-view-css' => '9418c97d',
'project-view-css' => '298b7c5b', 'project-view-css' => '9ce99f21',
'releeph-core' => '9b3c5733', 'releeph-core' => '9b3c5733',
'releeph-preview-branch' => 'b7a6f4a5', 'releeph-preview-branch' => 'b7a6f4a5',
'releeph-request-differential-create-dialog' => '8d8b92cd', 'releeph-request-differential-create-dialog' => '8d8b92cd',

View file

@ -0,0 +1,7 @@
CREATE TABLE {$NAMESPACE}_harbormaster.harbormaster_buildplanname_ngrams (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectID INT UNSIGNED NOT NULL,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
KEY `key_object` (objectID),
KEY `key_ngram` (ngram, objectID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,11 @@
<?php
$table = new HarbormasterBuildPlan();
foreach (new LiskMigrationIterator($table) as $plan) {
PhabricatorSearchWorker::queueDocumentForIndexing(
$plan->getPHID(),
array(
'force' => true,
));
}

View file

@ -0,0 +1,7 @@
CREATE TABLE {$NAMESPACE}_drydock.drydock_blueprintname_ngrams (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectID INT UNSIGNED NOT NULL,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
KEY `key_object` (objectID),
KEY `key_ngram` (ngram, objectID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,11 @@
<?php
$table = new DrydockBlueprint();
foreach (new LiskMigrationIterator($table) as $blueprint) {
PhabricatorSearchWorker::queueDocumentForIndexing(
$blueprint->getPHID(),
array(
'force' => true,
));
}

View file

@ -0,0 +1,16 @@
CREATE TABLE {$NAMESPACE}_drydock.edge (
src VARBINARY(64) NOT NULL,
type INT UNSIGNED NOT NULL,
dst VARBINARY(64) NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
seq INT UNSIGNED NOT NULL,
dataID INT UNSIGNED,
PRIMARY KEY (src, type, dst),
KEY `src` (src, type, dateCreated, seq),
UNIQUE KEY `key_dst` (dst, type, src)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
CREATE TABLE {$NAMESPACE}_drydock.edgedata (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -36,6 +36,7 @@ phutil_register_library_map(array(
'AlmanacDevice' => 'applications/almanac/storage/AlmanacDevice.php', 'AlmanacDevice' => 'applications/almanac/storage/AlmanacDevice.php',
'AlmanacDeviceController' => 'applications/almanac/controller/AlmanacDeviceController.php', 'AlmanacDeviceController' => 'applications/almanac/controller/AlmanacDeviceController.php',
'AlmanacDeviceEditController' => 'applications/almanac/controller/AlmanacDeviceEditController.php', 'AlmanacDeviceEditController' => 'applications/almanac/controller/AlmanacDeviceEditController.php',
'AlmanacDeviceEditEngine' => 'applications/almanac/editor/AlmanacDeviceEditEngine.php',
'AlmanacDeviceEditor' => 'applications/almanac/editor/AlmanacDeviceEditor.php', 'AlmanacDeviceEditor' => 'applications/almanac/editor/AlmanacDeviceEditor.php',
'AlmanacDeviceListController' => 'applications/almanac/controller/AlmanacDeviceListController.php', 'AlmanacDeviceListController' => 'applications/almanac/controller/AlmanacDeviceListController.php',
'AlmanacDeviceNameNgrams' => 'applications/almanac/storage/AlmanacDeviceNameNgrams.php', 'AlmanacDeviceNameNgrams' => 'applications/almanac/storage/AlmanacDeviceNameNgrams.php',
@ -106,6 +107,7 @@ phutil_register_library_map(array(
'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php', 'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php',
'AlmanacServiceDatasource' => 'applications/almanac/typeahead/AlmanacServiceDatasource.php', 'AlmanacServiceDatasource' => 'applications/almanac/typeahead/AlmanacServiceDatasource.php',
'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php', 'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php',
'AlmanacServiceEditEngine' => 'applications/almanac/editor/AlmanacServiceEditEngine.php',
'AlmanacServiceEditor' => 'applications/almanac/editor/AlmanacServiceEditor.php', 'AlmanacServiceEditor' => 'applications/almanac/editor/AlmanacServiceEditor.php',
'AlmanacServiceListController' => 'applications/almanac/controller/AlmanacServiceListController.php', 'AlmanacServiceListController' => 'applications/almanac/controller/AlmanacServiceListController.php',
'AlmanacServiceNameNgrams' => 'applications/almanac/storage/AlmanacServiceNameNgrams.php', 'AlmanacServiceNameNgrams' => 'applications/almanac/storage/AlmanacServiceNameNgrams.php',
@ -867,15 +869,16 @@ phutil_register_library_map(array(
'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php', 'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
'DrydockBlueprintController' => 'applications/drydock/controller/DrydockBlueprintController.php', 'DrydockBlueprintController' => 'applications/drydock/controller/DrydockBlueprintController.php',
'DrydockBlueprintCoreCustomField' => 'applications/drydock/customfield/DrydockBlueprintCoreCustomField.php', 'DrydockBlueprintCoreCustomField' => 'applications/drydock/customfield/DrydockBlueprintCoreCustomField.php',
'DrydockBlueprintCreateController' => 'applications/drydock/controller/DrydockBlueprintCreateController.php',
'DrydockBlueprintCustomField' => 'applications/drydock/customfield/DrydockBlueprintCustomField.php', 'DrydockBlueprintCustomField' => 'applications/drydock/customfield/DrydockBlueprintCustomField.php',
'DrydockBlueprintDatasource' => 'applications/drydock/typeahead/DrydockBlueprintDatasource.php', 'DrydockBlueprintDatasource' => 'applications/drydock/typeahead/DrydockBlueprintDatasource.php',
'DrydockBlueprintDisableController' => 'applications/drydock/controller/DrydockBlueprintDisableController.php', 'DrydockBlueprintDisableController' => 'applications/drydock/controller/DrydockBlueprintDisableController.php',
'DrydockBlueprintEditController' => 'applications/drydock/controller/DrydockBlueprintEditController.php', 'DrydockBlueprintEditController' => 'applications/drydock/controller/DrydockBlueprintEditController.php',
'DrydockBlueprintEditEngine' => 'applications/drydock/editor/DrydockBlueprintEditEngine.php',
'DrydockBlueprintEditor' => 'applications/drydock/editor/DrydockBlueprintEditor.php', 'DrydockBlueprintEditor' => 'applications/drydock/editor/DrydockBlueprintEditor.php',
'DrydockBlueprintImplementation' => 'applications/drydock/blueprint/DrydockBlueprintImplementation.php', 'DrydockBlueprintImplementation' => 'applications/drydock/blueprint/DrydockBlueprintImplementation.php',
'DrydockBlueprintImplementationTestCase' => 'applications/drydock/blueprint/__tests__/DrydockBlueprintImplementationTestCase.php', 'DrydockBlueprintImplementationTestCase' => 'applications/drydock/blueprint/__tests__/DrydockBlueprintImplementationTestCase.php',
'DrydockBlueprintListController' => 'applications/drydock/controller/DrydockBlueprintListController.php', 'DrydockBlueprintListController' => 'applications/drydock/controller/DrydockBlueprintListController.php',
'DrydockBlueprintNameNgrams' => 'applications/drydock/storage/DrydockBlueprintNameNgrams.php',
'DrydockBlueprintPHIDType' => 'applications/drydock/phid/DrydockBlueprintPHIDType.php', 'DrydockBlueprintPHIDType' => 'applications/drydock/phid/DrydockBlueprintPHIDType.php',
'DrydockBlueprintQuery' => 'applications/drydock/query/DrydockBlueprintQuery.php', 'DrydockBlueprintQuery' => 'applications/drydock/query/DrydockBlueprintQuery.php',
'DrydockBlueprintSearchEngine' => 'applications/drydock/query/DrydockBlueprintSearchEngine.php', 'DrydockBlueprintSearchEngine' => 'applications/drydock/query/DrydockBlueprintSearchEngine.php',
@ -938,6 +941,7 @@ phutil_register_library_map(array(
'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php', 'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php',
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php', 'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
'DrydockRepositoryOperation' => 'applications/drydock/storage/DrydockRepositoryOperation.php', 'DrydockRepositoryOperation' => 'applications/drydock/storage/DrydockRepositoryOperation.php',
'DrydockRepositoryOperationController' => 'applications/drydock/controller/DrydockRepositoryOperationController.php',
'DrydockRepositoryOperationDismissController' => 'applications/drydock/controller/DrydockRepositoryOperationDismissController.php', 'DrydockRepositoryOperationDismissController' => 'applications/drydock/controller/DrydockRepositoryOperationDismissController.php',
'DrydockRepositoryOperationListController' => 'applications/drydock/controller/DrydockRepositoryOperationListController.php', 'DrydockRepositoryOperationListController' => 'applications/drydock/controller/DrydockRepositoryOperationListController.php',
'DrydockRepositoryOperationPHIDType' => 'applications/drydock/phid/DrydockRepositoryOperationPHIDType.php', 'DrydockRepositoryOperationPHIDType' => 'applications/drydock/phid/DrydockRepositoryOperationPHIDType.php',
@ -965,6 +969,7 @@ phutil_register_library_map(array(
'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php', 'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php',
'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php', 'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php',
'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php', 'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php',
'DrydockSchemaSpec' => 'applications/drydock/storage/DrydockSchemaSpec.php',
'DrydockSlotLock' => 'applications/drydock/storage/DrydockSlotLock.php', 'DrydockSlotLock' => 'applications/drydock/storage/DrydockSlotLock.php',
'DrydockSlotLockException' => 'applications/drydock/exception/DrydockSlotLockException.php', 'DrydockSlotLockException' => 'applications/drydock/exception/DrydockSlotLockException.php',
'DrydockSlotLockFailureLogType' => 'applications/drydock/logtype/DrydockSlotLockFailureLogType.php', 'DrydockSlotLockFailureLogType' => 'applications/drydock/logtype/DrydockSlotLockFailureLogType.php',
@ -1044,6 +1049,8 @@ phutil_register_library_map(array(
'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php', 'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php',
'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php', 'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php',
'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php', 'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php',
'HarbormasterBuildLogChunk' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunk.php',
'HarbormasterBuildLogChunkIterator' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php',
'HarbormasterBuildLogPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php', 'HarbormasterBuildLogPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php',
'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php', 'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php',
'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php', 'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php',
@ -1053,7 +1060,9 @@ phutil_register_library_map(array(
'HarbormasterBuildPlanDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php', 'HarbormasterBuildPlanDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php',
'HarbormasterBuildPlanDefaultEditCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultEditCapability.php', 'HarbormasterBuildPlanDefaultEditCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultEditCapability.php',
'HarbormasterBuildPlanDefaultViewCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultViewCapability.php', 'HarbormasterBuildPlanDefaultViewCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultViewCapability.php',
'HarbormasterBuildPlanEditEngine' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php',
'HarbormasterBuildPlanEditor' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditor.php', 'HarbormasterBuildPlanEditor' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditor.php',
'HarbormasterBuildPlanNameNgrams' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanNameNgrams.php',
'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php', 'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php',
'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php', 'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php',
'HarbormasterBuildPlanSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php', 'HarbormasterBuildPlanSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php',
@ -1110,6 +1119,7 @@ phutil_register_library_map(array(
'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php', 'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php',
'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php', 'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php',
'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php', 'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php',
'HarbormasterManagementArchiveLogsWorkflow' => 'applications/harbormaster/management/HarbormasterManagementArchiveLogsWorkflow.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',
'HarbormasterManagementWorkflow' => 'applications/harbormaster/management/HarbormasterManagementWorkflow.php', 'HarbormasterManagementWorkflow' => 'applications/harbormaster/management/HarbormasterManagementWorkflow.php',
@ -1143,8 +1153,11 @@ phutil_register_library_map(array(
'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php', 'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php',
'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php', 'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php',
'HarbormasterURIArtifact' => 'applications/harbormaster/artifact/HarbormasterURIArtifact.php', 'HarbormasterURIArtifact' => 'applications/harbormaster/artifact/HarbormasterURIArtifact.php',
'HarbormasterUnitMessagesController' => 'applications/harbormaster/controller/HarbormasterUnitMessagesController.php', 'HarbormasterUnitMessageListController' => 'applications/harbormaster/controller/HarbormasterUnitMessageListController.php',
'HarbormasterUnitMessageViewController' => 'applications/harbormaster/controller/HarbormasterUnitMessageViewController.php',
'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php', 'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php',
'HarbormasterUnitStatus' => 'applications/harbormaster/constants/HarbormasterUnitStatus.php',
'HarbormasterUnitSummaryView' => 'applications/harbormaster/view/HarbormasterUnitSummaryView.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',
@ -1519,6 +1532,7 @@ phutil_register_library_map(array(
'PHUIHandleListView' => 'applications/phid/view/PHUIHandleListView.php', 'PHUIHandleListView' => 'applications/phid/view/PHUIHandleListView.php',
'PHUIHandleTagListView' => 'applications/phid/view/PHUIHandleTagListView.php', 'PHUIHandleTagListView' => 'applications/phid/view/PHUIHandleTagListView.php',
'PHUIHandleView' => 'applications/phid/view/PHUIHandleView.php', 'PHUIHandleView' => 'applications/phid/view/PHUIHandleView.php',
'PHUIHeadThingView' => 'view/phui/PHUIHeadThingView.php',
'PHUIHeaderView' => 'view/phui/PHUIHeaderView.php', 'PHUIHeaderView' => 'view/phui/PHUIHeaderView.php',
'PHUIHovercardUIExample' => 'applications/uiexample/examples/PHUIHovercardUIExample.php', 'PHUIHovercardUIExample' => 'applications/uiexample/examples/PHUIHovercardUIExample.php',
'PHUIHovercardView' => 'view/phui/PHUIHovercardView.php', 'PHUIHovercardView' => 'view/phui/PHUIHovercardView.php',
@ -3277,6 +3291,7 @@ phutil_register_library_map(array(
'PhabricatorStandardCustomFieldUsers' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php', 'PhabricatorStandardCustomFieldUsers' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php',
'PhabricatorStandardPageView' => 'view/page/PhabricatorStandardPageView.php', 'PhabricatorStandardPageView' => 'view/page/PhabricatorStandardPageView.php',
'PhabricatorStandardSelectCustomFieldDatasource' => 'infrastructure/customfield/datasource/PhabricatorStandardSelectCustomFieldDatasource.php', 'PhabricatorStandardSelectCustomFieldDatasource' => 'infrastructure/customfield/datasource/PhabricatorStandardSelectCustomFieldDatasource.php',
'PhabricatorStaticEditField' => 'applications/transactions/editfield/PhabricatorStaticEditField.php',
'PhabricatorStatusController' => 'applications/system/controller/PhabricatorStatusController.php', 'PhabricatorStatusController' => 'applications/system/controller/PhabricatorStatusController.php',
'PhabricatorStatusUIExample' => 'applications/uiexample/examples/PhabricatorStatusUIExample.php', 'PhabricatorStatusUIExample' => 'applications/uiexample/examples/PhabricatorStatusUIExample.php',
'PhabricatorStorageFixtureScopeGuard' => 'infrastructure/testing/fixture/PhabricatorStorageFixtureScopeGuard.php', 'PhabricatorStorageFixtureScopeGuard' => 'infrastructure/testing/fixture/PhabricatorStorageFixtureScopeGuard.php',
@ -4031,6 +4046,7 @@ phutil_register_library_map(array(
), ),
'AlmanacDeviceController' => 'AlmanacController', 'AlmanacDeviceController' => 'AlmanacController',
'AlmanacDeviceEditController' => 'AlmanacDeviceController', 'AlmanacDeviceEditController' => 'AlmanacDeviceController',
'AlmanacDeviceEditEngine' => 'PhabricatorEditEngine',
'AlmanacDeviceEditor' => 'AlmanacEditor', 'AlmanacDeviceEditor' => 'AlmanacEditor',
'AlmanacDeviceListController' => 'AlmanacDeviceController', 'AlmanacDeviceListController' => 'AlmanacDeviceController',
'AlmanacDeviceNameNgrams' => 'PhabricatorSearchNgrams', 'AlmanacDeviceNameNgrams' => 'PhabricatorSearchNgrams',
@ -4132,6 +4148,7 @@ phutil_register_library_map(array(
'AlmanacServiceController' => 'AlmanacController', 'AlmanacServiceController' => 'AlmanacController',
'AlmanacServiceDatasource' => 'PhabricatorTypeaheadDatasource', 'AlmanacServiceDatasource' => 'PhabricatorTypeaheadDatasource',
'AlmanacServiceEditController' => 'AlmanacServiceController', 'AlmanacServiceEditController' => 'AlmanacServiceController',
'AlmanacServiceEditEngine' => 'PhabricatorEditEngine',
'AlmanacServiceEditor' => 'AlmanacEditor', 'AlmanacServiceEditor' => 'AlmanacEditor',
'AlmanacServiceListController' => 'AlmanacServiceController', 'AlmanacServiceListController' => 'AlmanacServiceController',
'AlmanacServiceNameNgrams' => 'PhabricatorSearchNgrams', 'AlmanacServiceNameNgrams' => 'PhabricatorSearchNgrams',
@ -4620,7 +4637,7 @@ phutil_register_library_map(array(
'DifferentialTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'DifferentialTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
'DifferentialTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'DifferentialTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'DifferentialTransactionView' => 'PhabricatorApplicationTransactionView', 'DifferentialTransactionView' => 'PhabricatorApplicationTransactionView',
'DifferentialUnitField' => 'DifferentialHarbormasterField', 'DifferentialUnitField' => 'DifferentialCustomField',
'DifferentialUnitStatus' => 'Phobject', 'DifferentialUnitStatus' => 'Phobject',
'DifferentialUnitTestResult' => 'Phobject', 'DifferentialUnitTestResult' => 'Phobject',
'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
@ -4963,21 +4980,24 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
'PhabricatorCustomFieldInterface', 'PhabricatorCustomFieldInterface',
'PhabricatorNgramsInterface',
'PhabricatorProjectInterface',
), ),
'DrydockBlueprintController' => 'DrydockController', 'DrydockBlueprintController' => 'DrydockController',
'DrydockBlueprintCoreCustomField' => array( 'DrydockBlueprintCoreCustomField' => array(
'DrydockBlueprintCustomField', 'DrydockBlueprintCustomField',
'PhabricatorStandardCustomFieldInterface', 'PhabricatorStandardCustomFieldInterface',
), ),
'DrydockBlueprintCreateController' => 'DrydockBlueprintController',
'DrydockBlueprintCustomField' => 'PhabricatorCustomField', 'DrydockBlueprintCustomField' => 'PhabricatorCustomField',
'DrydockBlueprintDatasource' => 'PhabricatorTypeaheadDatasource', 'DrydockBlueprintDatasource' => 'PhabricatorTypeaheadDatasource',
'DrydockBlueprintDisableController' => 'DrydockBlueprintController', 'DrydockBlueprintDisableController' => 'DrydockBlueprintController',
'DrydockBlueprintEditController' => 'DrydockBlueprintController', 'DrydockBlueprintEditController' => 'DrydockBlueprintController',
'DrydockBlueprintEditEngine' => 'PhabricatorEditEngine',
'DrydockBlueprintEditor' => 'PhabricatorApplicationTransactionEditor', 'DrydockBlueprintEditor' => 'PhabricatorApplicationTransactionEditor',
'DrydockBlueprintImplementation' => 'Phobject', 'DrydockBlueprintImplementation' => 'Phobject',
'DrydockBlueprintImplementationTestCase' => 'PhabricatorTestCase', 'DrydockBlueprintImplementationTestCase' => 'PhabricatorTestCase',
'DrydockBlueprintListController' => 'DrydockBlueprintController', 'DrydockBlueprintListController' => 'DrydockBlueprintController',
'DrydockBlueprintNameNgrams' => 'PhabricatorSearchNgrams',
'DrydockBlueprintPHIDType' => 'PhabricatorPHIDType', 'DrydockBlueprintPHIDType' => 'PhabricatorPHIDType',
'DrydockBlueprintQuery' => 'DrydockQuery', 'DrydockBlueprintQuery' => 'DrydockQuery',
'DrydockBlueprintSearchEngine' => 'PhabricatorApplicationSearchEngine', 'DrydockBlueprintSearchEngine' => 'PhabricatorApplicationSearchEngine',
@ -5051,16 +5071,17 @@ phutil_register_library_map(array(
'DrydockDAO', 'DrydockDAO',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
), ),
'DrydockRepositoryOperationDismissController' => 'DrydockController', 'DrydockRepositoryOperationController' => 'DrydockController',
'DrydockRepositoryOperationListController' => 'DrydockController', 'DrydockRepositoryOperationDismissController' => 'DrydockRepositoryOperationController',
'DrydockRepositoryOperationListController' => 'DrydockRepositoryOperationController',
'DrydockRepositoryOperationPHIDType' => 'PhabricatorPHIDType', 'DrydockRepositoryOperationPHIDType' => 'PhabricatorPHIDType',
'DrydockRepositoryOperationQuery' => 'DrydockQuery', 'DrydockRepositoryOperationQuery' => 'DrydockQuery',
'DrydockRepositoryOperationSearchEngine' => 'PhabricatorApplicationSearchEngine', 'DrydockRepositoryOperationSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DrydockRepositoryOperationStatusController' => 'DrydockController', 'DrydockRepositoryOperationStatusController' => 'DrydockRepositoryOperationController',
'DrydockRepositoryOperationStatusView' => 'AphrontView', 'DrydockRepositoryOperationStatusView' => 'AphrontView',
'DrydockRepositoryOperationType' => 'Phobject', 'DrydockRepositoryOperationType' => 'Phobject',
'DrydockRepositoryOperationUpdateWorker' => 'DrydockWorker', 'DrydockRepositoryOperationUpdateWorker' => 'DrydockWorker',
'DrydockRepositoryOperationViewController' => 'DrydockController', 'DrydockRepositoryOperationViewController' => 'DrydockRepositoryOperationController',
'DrydockResource' => array( 'DrydockResource' => array(
'DrydockDAO', 'DrydockDAO',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
@ -5081,6 +5102,7 @@ phutil_register_library_map(array(
'DrydockResourceViewController' => 'DrydockResourceController', 'DrydockResourceViewController' => 'DrydockResourceController',
'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface', 'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface',
'DrydockSSHCommandInterface' => 'DrydockCommandInterface', 'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
'DrydockSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'DrydockSlotLock' => 'DrydockDAO', 'DrydockSlotLock' => 'DrydockDAO',
'DrydockSlotLockException' => 'Exception', 'DrydockSlotLockException' => 'Exception',
'DrydockSlotLockFailureLogType' => 'DrydockLogType', 'DrydockSlotLockFailureLogType' => 'DrydockLogType',
@ -5185,6 +5207,8 @@ phutil_register_library_map(array(
'HarbormasterDAO', 'HarbormasterDAO',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
), ),
'HarbormasterBuildLogChunk' => 'HarbormasterDAO',
'HarbormasterBuildLogChunkIterator' => 'PhutilBufferedIterator',
'HarbormasterBuildLogPHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildLogPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildMessage' => array( 'HarbormasterBuildMessage' => array(
@ -5198,11 +5222,15 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
'PhabricatorSubscribableInterface', 'PhabricatorSubscribableInterface',
'PhabricatorNgramsInterface',
'PhabricatorProjectInterface',
), ),
'HarbormasterBuildPlanDatasource' => 'PhabricatorTypeaheadDatasource', 'HarbormasterBuildPlanDatasource' => 'PhabricatorTypeaheadDatasource',
'HarbormasterBuildPlanDefaultEditCapability' => 'PhabricatorPolicyCapability', 'HarbormasterBuildPlanDefaultEditCapability' => 'PhabricatorPolicyCapability',
'HarbormasterBuildPlanDefaultViewCapability' => 'PhabricatorPolicyCapability', 'HarbormasterBuildPlanDefaultViewCapability' => 'PhabricatorPolicyCapability',
'HarbormasterBuildPlanEditEngine' => 'PhabricatorEditEngine',
'HarbormasterBuildPlanEditor' => 'PhabricatorApplicationTransactionEditor', 'HarbormasterBuildPlanEditor' => 'PhabricatorApplicationTransactionEditor',
'HarbormasterBuildPlanNameNgrams' => 'PhabricatorSearchNgrams',
'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildPlanSearchEngine' => 'PhabricatorApplicationSearchEngine', 'HarbormasterBuildPlanSearchEngine' => 'PhabricatorApplicationSearchEngine',
@ -5273,6 +5301,7 @@ phutil_register_library_map(array(
'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterLintMessagesController' => 'HarbormasterController', 'HarbormasterLintMessagesController' => 'HarbormasterController',
'HarbormasterLintPropertyView' => 'AphrontView', 'HarbormasterLintPropertyView' => 'AphrontView',
'HarbormasterManagementArchiveLogsWorkflow' => 'HarbormasterManagementWorkflow',
'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow', 'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow',
'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow', 'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow',
'HarbormasterManagementWorkflow' => 'PhabricatorManagementWorkflow', 'HarbormasterManagementWorkflow' => 'PhabricatorManagementWorkflow',
@ -5283,7 +5312,7 @@ phutil_register_library_map(array(
'HarbormasterPlanDisableController' => 'HarbormasterPlanController', 'HarbormasterPlanDisableController' => 'HarbormasterPlanController',
'HarbormasterPlanEditController' => 'HarbormasterPlanController', 'HarbormasterPlanEditController' => 'HarbormasterPlanController',
'HarbormasterPlanListController' => 'HarbormasterPlanController', 'HarbormasterPlanListController' => 'HarbormasterPlanController',
'HarbormasterPlanRunController' => 'HarbormasterController', 'HarbormasterPlanRunController' => 'HarbormasterPlanController',
'HarbormasterPlanViewController' => 'HarbormasterPlanController', 'HarbormasterPlanViewController' => 'HarbormasterPlanController',
'HarbormasterPrototypeBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterPrototypeBuildStepGroup' => 'HarbormasterBuildStepGroup',
'HarbormasterPublishFragmentBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterPublishFragmentBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
@ -5296,18 +5325,21 @@ phutil_register_library_map(array(
'HarbormasterScratchTable' => 'HarbormasterDAO', 'HarbormasterScratchTable' => 'HarbormasterDAO',
'HarbormasterSendMessageConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 'HarbormasterSendMessageConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
'HarbormasterSleepBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterSleepBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterStepAddController' => 'HarbormasterController', 'HarbormasterStepAddController' => 'HarbormasterPlanController',
'HarbormasterStepDeleteController' => 'HarbormasterController', 'HarbormasterStepDeleteController' => 'HarbormasterPlanController',
'HarbormasterStepEditController' => 'HarbormasterController', 'HarbormasterStepEditController' => 'HarbormasterPlanController',
'HarbormasterStepViewController' => 'HarbormasterController', 'HarbormasterStepViewController' => 'HarbormasterPlanController',
'HarbormasterTargetEngine' => 'Phobject', 'HarbormasterTargetEngine' => 'Phobject',
'HarbormasterTargetWorker' => 'HarbormasterWorker', 'HarbormasterTargetWorker' => 'HarbormasterWorker',
'HarbormasterTestBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterTestBuildStepGroup' => 'HarbormasterBuildStepGroup',
'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation', 'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation',
'HarbormasterUIEventListener' => 'PhabricatorEventListener', 'HarbormasterUIEventListener' => 'PhabricatorEventListener',
'HarbormasterURIArtifact' => 'HarbormasterArtifact', 'HarbormasterURIArtifact' => 'HarbormasterArtifact',
'HarbormasterUnitMessagesController' => 'HarbormasterController', 'HarbormasterUnitMessageListController' => 'HarbormasterController',
'HarbormasterUnitMessageViewController' => 'HarbormasterController',
'HarbormasterUnitPropertyView' => 'AphrontView', 'HarbormasterUnitPropertyView' => 'AphrontView',
'HarbormasterUnitStatus' => 'Phobject',
'HarbormasterUnitSummaryView' => 'AphrontView',
'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterWorker' => 'PhabricatorWorker', 'HarbormasterWorker' => 'PhabricatorWorker',
@ -5746,6 +5778,7 @@ phutil_register_library_map(array(
'PHUIHandleListView' => 'AphrontTagView', 'PHUIHandleListView' => 'AphrontTagView',
'PHUIHandleTagListView' => 'AphrontTagView', 'PHUIHandleTagListView' => 'AphrontTagView',
'PHUIHandleView' => 'AphrontView', 'PHUIHandleView' => 'AphrontView',
'PHUIHeadThingView' => 'AphrontTagView',
'PHUIHeaderView' => 'AphrontTagView', 'PHUIHeaderView' => 'AphrontTagView',
'PHUIHovercardUIExample' => 'PhabricatorUIExample', 'PHUIHovercardUIExample' => 'PhabricatorUIExample',
'PHUIHovercardView' => 'AphrontTagView', 'PHUIHovercardView' => 'AphrontTagView',
@ -6103,7 +6136,7 @@ phutil_register_library_map(array(
'PhabricatorBadgesMailReceiver' => 'PhabricatorObjectMailReceiver', 'PhabricatorBadgesMailReceiver' => 'PhabricatorObjectMailReceiver',
'PhabricatorBadgesPHIDType' => 'PhabricatorPHIDType', 'PhabricatorBadgesPHIDType' => 'PhabricatorPHIDType',
'PhabricatorBadgesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorBadgesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorBadgesRecipientsListView' => 'AphrontTagView', 'PhabricatorBadgesRecipientsListView' => 'AphrontView',
'PhabricatorBadgesRemoveRecipientsController' => 'PhabricatorBadgesController', 'PhabricatorBadgesRemoveRecipientsController' => 'PhabricatorBadgesController',
'PhabricatorBadgesReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhabricatorBadgesReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhabricatorBadgesSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorBadgesSchemaSpec' => 'PhabricatorConfigSchemaSpec',
@ -6368,7 +6401,7 @@ phutil_register_library_map(array(
'PhabricatorCountdownTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorCountdownTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorCountdownTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhabricatorCountdownTransactionComment' => 'PhabricatorApplicationTransactionComment',
'PhabricatorCountdownTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorCountdownTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorCountdownView' => 'AphrontTagView', 'PhabricatorCountdownView' => 'AphrontView',
'PhabricatorCountdownViewController' => 'PhabricatorCountdownController', 'PhabricatorCountdownViewController' => 'PhabricatorCountdownController',
'PhabricatorCredentialsUsedByObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorCredentialsUsedByObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorCursorPagedPolicyAwareQuery' => 'PhabricatorPolicyAwareQuery', 'PhabricatorCursorPagedPolicyAwareQuery' => 'PhabricatorPolicyAwareQuery',
@ -7811,6 +7844,7 @@ phutil_register_library_map(array(
'AphrontResponseProducerInterface', 'AphrontResponseProducerInterface',
), ),
'PhabricatorStandardSelectCustomFieldDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorStandardSelectCustomFieldDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorStaticEditField' => 'PhabricatorEditField',
'PhabricatorStatusController' => 'PhabricatorController', 'PhabricatorStatusController' => 'PhabricatorController',
'PhabricatorStatusUIExample' => 'PhabricatorUIExample', 'PhabricatorStatusUIExample' => 'PhabricatorUIExample',
'PhabricatorStorageFixtureScopeGuard' => 'Phobject', 'PhabricatorStorageFixtureScopeGuard' => 'Phobject',

View file

@ -35,22 +35,18 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
); );
} }
public function isPrototype() {
return true;
}
public function getRoutes() { public function getRoutes() {
return array( return array(
'/almanac/' => array( '/almanac/' => array(
'' => 'AlmanacConsoleController', '' => 'AlmanacConsoleController',
'(?P<objectType>service)/' => array( '(?P<objectType>service)/' => array(
$this->getQueryRoutePattern() => 'AlmanacServiceListController', $this->getQueryRoutePattern() => 'AlmanacServiceListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacServiceEditController', $this->getEditRoutePattern('edit/') => 'AlmanacServiceEditController',
'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController', 'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController',
), ),
'(?P<objectType>device)/' => array( '(?P<objectType>device)/' => array(
$this->getQueryRoutePattern() => 'AlmanacDeviceListController', $this->getQueryRoutePattern() => 'AlmanacDeviceListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController', $this->getEditRoutePattern('edit/') => 'AlmanacDeviceEditController',
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController', 'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
), ),
'interface/' => array( 'interface/' => array(

View file

@ -158,16 +158,16 @@ abstract class AlmanacController
->setIcon('fa-plus'); ->setIcon('fa-plus');
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader(pht('Properties')) ->setHeader(pht('PROPERTIES'))
->addActionLink($add_button); ->addActionLink($add_button);
return id(new PHUIObjectBoxView()) return id(new PHUIObjectBoxView())
->setHeader($header) ->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table); ->setTable($table);
} }
protected function addClusterMessage( protected function addClusterMessage(
PHUIObjectBoxView $box,
$positive, $positive,
$negative) { $negative) {
@ -194,14 +194,13 @@ abstract class AlmanacController
$icon = id(new PHUIIconView()) $icon = id(new PHUIIconView())
->setIcon('fa-sitemap'); ->setIcon('fa-sitemap');
$error_view = id(new PHUIInfoView()) return id(new PHUIInfoView())
->setSeverity($severity) ->setSeverity($severity)
->setErrors( ->setErrors(
array( array(
array($icon, ' ', $message, ' ', $doc_link), array($icon, ' ', $message, ' ', $doc_link),
)); ));
$box->setInfoView($error_view);
} }
protected function getPropertyDeleteURI($object) { protected function getPropertyDeleteURI($object) {

View file

@ -2,6 +2,11 @@
abstract class AlmanacDeviceController extends AlmanacController { abstract class AlmanacDeviceController extends AlmanacController {
public function buildApplicationMenu() {
return $this->newApplicationMenu()
->setSearchEngine(new AlmanacDeviceSearchEngine());
}
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();

View file

@ -4,160 +4,9 @@ final class AlmanacDeviceEditController
extends AlmanacDeviceController { extends AlmanacDeviceController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); return id(new AlmanacDeviceEditEngine())
->setController($this)
$list_uri = $this->getApplicationURI('device/'); ->buildResponse();
$id = $request->getURIData('id');
if ($id) {
$device = id(new AlmanacDeviceQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$device) {
return new Aphront404Response();
}
$is_new = false;
$device_uri = $device->getURI();
$cancel_uri = $device_uri;
$title = pht('Edit Device');
$save_button = pht('Save Changes');
} else {
$this->requireApplicationCapability(
AlmanacCreateDevicesCapability::CAPABILITY);
$device = AlmanacDevice::initializeNewDevice();
$is_new = true;
$cancel_uri = $list_uri;
$title = pht('Create Device');
$save_button = pht('Create Device');
}
$v_name = $device->getName();
$e_name = true;
$validation_exception = null;
if ($is_new) {
$v_projects = array();
} else {
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$device->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
}
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$v_projects = $request->getArr('projects');
$type_name = AlmanacDeviceTransaction::TYPE_NAME;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$xactions = array();
$xactions[] = id(new AlmanacDeviceTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new AlmanacDeviceTransaction())
->setTransactionType($type_view)
->setNewValue($v_view);
$xactions[] = id(new AlmanacDeviceTransaction())
->setTransactionType($type_edit)
->setNewValue($v_edit);
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new AlmanacDeviceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$editor = id(new AlmanacDeviceEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($device, $xactions);
$device_uri = $device->getURI();
return id(new AphrontRedirectResponse())->setURI($device_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$device->setViewPolicy($v_view);
$device->setEditPolicy($v_edit);
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($device)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($device)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($device)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue($save_button));
$box = id(new PHUIObjectBoxView())
->setValidationException($validation_exception)
->setHeaderText($title)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$crumbs->addTextCrumb(pht('Create Device'));
} else {
$crumbs->addTextCrumb($device->getName(), $device_uri);
$crumbs->addTextCrumb(pht('Edit'));
}
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$box,
));
} }
} }

View file

@ -8,45 +8,19 @@ final class AlmanacDeviceListController
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$controller = id(new PhabricatorApplicationSearchController()) return id(new AlmanacDeviceSearchEngine())
->setQueryKey($request->getURIData('queryKey')) ->setController($this)
->setSearchEngine(new AlmanacDeviceSearchEngine()) ->buildResponse();
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
} }
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();
$can_create = $this->hasApplicationCapability( id(new AlmanacDeviceEditEngine())
AlmanacCreateDevicesCapability::CAPABILITY); ->setViewer($this->getViewer())
->addActionToCrumbs($crumbs);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Device'))
->setHref($this->getApplicationURI('device/edit/'))
->setIcon('fa-plus-square')
->setDisabled(!$can_create)
->setWorkflow(!$can_create));
return $crumbs; return $crumbs;
} }
public function buildSideNavView() {
$viewer = $this->getViewer();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new AlmanacDeviceSearchEngine())
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
} }

View file

@ -23,22 +23,18 @@ final class AlmanacDeviceViewController
$title = pht('Device %s', $device->getName()); $title = pht('Device %s', $device->getName());
$property_list = $this->buildPropertyList($device); $properties = $this->buildPropertyList($device);
$action_list = $this->buildActionList($device); $actions = $this->buildActionList($device);
$property_list->setActionList($action_list);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setUser($viewer) ->setUser($viewer)
->setHeader($device->getName()) ->setHeader($device->getName())
->setPolicyObject($device); ->setPolicyObject($device)
->setHeaderIcon('fa-server');
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($property_list);
$issue = null;
if ($device->isClusterDevice()) { if ($device->isClusterDevice()) {
$this->addClusterMessage( $issue = $this->addClusterMessage(
$box,
pht('This device is bound to a cluster service.'), pht('This device is bound to a cluster service.'),
pht( pht(
'This device is bound to a cluster service. You do not have '. 'This device is bound to a cluster service. You do not have '.
@ -50,24 +46,33 @@ final class AlmanacDeviceViewController
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($device->getName()); $crumbs->addTextCrumb($device->getName());
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline( $timeline = $this->buildTransactionTimeline(
$device, $device,
new AlmanacDeviceTransactionQuery()); new AlmanacDeviceTransactionQuery());
$timeline->setShouldTerminate(true); $timeline->setShouldTerminate(true);
return $this->newPage() $view = id(new PHUITwoColumnView())
->setTitle($title) ->setHeader($header)
->setCrumbs($crumbs) ->setMainColumn(array(
->appendChild( $issue,
array(
$box,
$interfaces, $interfaces,
$this->buildAlmanacPropertiesTable($device), $this->buildAlmanacPropertiesTable($device),
$this->buildSSHKeysTable($device), $this->buildSSHKeysTable($device),
$this->buildServicesTable($device), $this->buildServicesTable($device),
$timeline, $timeline,
)); ))
->setPropertyList($properties)
->setActionList($actions);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
} }
private function buildPropertyList(AlmanacDevice $device) { private function buildPropertyList(AlmanacDevice $device) {
@ -123,7 +128,7 @@ final class AlmanacDeviceViewController
->setCanEdit($can_edit); ->setCanEdit($can_edit);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader(pht('Device Interfaces')) ->setHeader(pht('DEVICE INTERFACES'))
->addActionLink( ->addActionLink(
id(new PHUIButtonView()) id(new PHUIButtonView())
->setTag('a') ->setTag('a')
@ -135,6 +140,7 @@ final class AlmanacDeviceViewController
return id(new PHUIObjectBoxView()) return id(new PHUIObjectBoxView())
->setHeader($header) ->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table); ->setTable($table);
} }
@ -172,7 +178,7 @@ final class AlmanacDeviceViewController
$upload_uri = '/auth/sshkey/upload/?objectPHID='.$device_phid; $upload_uri = '/auth/sshkey/upload/?objectPHID='.$device_phid;
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader(pht('SSH Public Keys')) ->setHeader(pht('SSH PUBLIC KEYS'))
->addActionLink( ->addActionLink(
id(new PHUIButtonView()) id(new PHUIButtonView())
->setTag('a') ->setTag('a')
@ -196,9 +202,8 @@ final class AlmanacDeviceViewController
return id(new PHUIObjectBoxView()) return id(new PHUIObjectBoxView())
->setHeader($header) ->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table); ->setTable($table);
} }
private function buildServicesTable(AlmanacDevice $device) { private function buildServicesTable(AlmanacDevice $device) {
@ -244,7 +249,8 @@ final class AlmanacDeviceViewController
)); ));
return id(new PHUIObjectBoxView()) return id(new PHUIObjectBoxView())
->setHeaderText(pht('Bound Services')) ->setHeaderText(pht('BOUND SERVICES'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table); ->setTable($table);
} }

View file

@ -21,42 +21,49 @@ final class AlmanacNamespaceViewController
$title = pht('Namespace %s', $namespace->getName()); $title = pht('Namespace %s', $namespace->getName());
$property_list = $this->buildPropertyList($namespace); $properties = $this->buildPropertyList($namespace);
$action_list = $this->buildActionList($namespace); $actions = $this->buildActionList($namespace);
$property_list->setActionList($action_list);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setUser($viewer) ->setUser($viewer)
->setHeader($namespace->getName()) ->setHeader($namespace->getName())
->setPolicyObject($namespace); ->setPolicyObject($namespace)
->setHeaderIcon('fa-asterisk');
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($property_list);
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($namespace->getName()); $crumbs->addTextCrumb($namespace->getName());
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline( $timeline = $this->buildTransactionTimeline(
$namespace, $namespace,
new AlmanacNamespaceTransactionQuery()); new AlmanacNamespaceTransactionQuery());
$timeline->setShouldTerminate(true); $timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setMainColumn(array(
$timeline,
))
->setPropertyList($properties)
->setActionList($actions);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->appendChild( ->appendChild(
array( array(
$box, $view,
$timeline, ));
));
} }
private function buildPropertyList(AlmanacNamespace $namespace) { private function buildPropertyList(AlmanacNamespace $namespace) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView()) $properties = id(new PHUIPropertyListView())
->setUser($viewer); ->setUser($viewer)
->setObject($namespace);
$properties->invokeWillRenderEvent();
return $properties; return $properties;
} }

View file

@ -21,34 +21,38 @@ final class AlmanacNetworkViewController
$title = pht('Network %s', $network->getName()); $title = pht('Network %s', $network->getName());
$property_list = $this->buildPropertyList($network); $properties = $this->buildPropertyList($network);
$action_list = $this->buildActionList($network); $actions = $this->buildActionList($network);
$property_list->setActionList($action_list);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setUser($viewer) ->setUser($viewer)
->setHeader($network->getName()) ->setHeader($network->getName())
->setHeaderIcon('fa-globe')
->setPolicyObject($network); ->setPolicyObject($network);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($property_list);
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($network->getName()); $crumbs->addTextCrumb($network->getName());
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline( $timeline = $this->buildTransactionTimeline(
$network, $network,
new AlmanacNetworkTransactionQuery()); new AlmanacNetworkTransactionQuery());
$timeline->setShouldTerminate(true); $timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setMainColumn(array(
$timeline,
))
->setPropertyList($properties)
->setActionList($actions);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->appendChild( ->appendChild(
array( array(
$box, $view,
$timeline,
)); ));
} }
@ -56,7 +60,10 @@ final class AlmanacNetworkViewController
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView()) $properties = id(new PHUIPropertyListView())
->setUser($viewer); ->setUser($viewer)
->setObject($network);
$properties->invokeWillRenderEvent();
return $properties; return $properties;
} }

View file

@ -11,6 +11,11 @@ abstract class AlmanacServiceController extends AlmanacController {
return $crumbs; return $crumbs;
} }
public function buildApplicationMenu() {
return $this->newApplicationMenu()
->setSearchEngine(new AlmanacServiceSearchEngine());
}
protected function getPropertyDeleteURI($object) { protected function getPropertyDeleteURI($object) {
$id = $object->getID(); $id = $object->getID();
return "/almanac/service/delete/{$id}/"; return "/almanac/service/delete/{$id}/";

View file

@ -4,175 +4,28 @@ final class AlmanacServiceEditController
extends AlmanacServiceController { extends AlmanacServiceController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $engine = id(new AlmanacServiceEditEngine())
->setController($this);
$list_uri = $this->getApplicationURI('service/');
$id = $request->getURIData('id'); $id = $request->getURIData('id');
if ($id) { if (!$id) {
$service = id(new AlmanacServiceQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$service) {
return new Aphront404Response();
}
$is_new = false;
$service_uri = $service->getURI();
$cancel_uri = $service_uri;
$title = pht('Edit Service');
$save_button = pht('Save Changes');
} else {
$cancel_uri = $list_uri;
$this->requireApplicationCapability( $this->requireApplicationCapability(
AlmanacCreateServicesCapability::CAPABILITY); AlmanacCreateServicesCapability::CAPABILITY);
$list_uri = $this->getApplicationURI('service/');
$service_type = $request->getStr('serviceType'); $service_type = $request->getStr('serviceType');
$service_types = AlmanacServiceType::getAllServiceTypes();
try { if (empty($service_types[$service_type])) {
$service = AlmanacService::initializeNewService($service_type); return $this->buildServiceTypeResponse($list_uri);
} catch (Exception $ex) {
return $this->buildServiceTypeResponse($cancel_uri);
} }
if ($service->isClusterService()) { $engine
$this->requireApplicationCapability( ->addContextParameter('serviceType', $service_type)
AlmanacManageClusterServicesCapability::CAPABILITY); ->setServiceType($service_type);
}
$is_new = true;
$title = pht('Create Service');
$save_button = pht('Create Service');
} }
$v_name = $service->getName(); return $engine->buildResponse();
$e_name = true;
$validation_exception = null;
if ($is_new) {
$v_projects = array();
} else {
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$service->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
}
if ($request->isFormPost() && $request->getStr('edit')) {
$v_name = $request->getStr('name');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$v_projects = $request->getArr('projects');
$type_name = AlmanacServiceTransaction::TYPE_NAME;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$xactions = array();
$xactions[] = id(new AlmanacServiceTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new AlmanacServiceTransaction())
->setTransactionType($type_view)
->setNewValue($v_view);
$xactions[] = id(new AlmanacServiceTransaction())
->setTransactionType($type_edit)
->setNewValue($v_edit);
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new AlmanacServiceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$editor = id(new AlmanacServiceEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($service, $xactions);
$service_uri = $service->getURI();
return id(new AphrontRedirectResponse())->setURI($service_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$service->setViewPolicy($v_view);
$service->setEditPolicy($v_edit);
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($service)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('edit', true)
->addHiddenInput('serviceType', $service->getServiceType())
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($service)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($service)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue($save_button));
$box = id(new PHUIObjectBoxView())
->setValidationException($validation_exception)
->setHeaderText($title)
->appendChild($form);
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$crumbs->addTextCrumb(pht('Create Service'));
} else {
$crumbs->addTextCrumb($service->getName(), $service_uri);
$crumbs->addTextCrumb(pht('Edit'));
}
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$box,
));
} }
private function buildServiceTypeResponse($cancel_uri) { private function buildServiceTypeResponse($cancel_uri) {
@ -194,7 +47,6 @@ final class AlmanacServiceEditController
pht('You have permission to create cluster services.'), pht('You have permission to create cluster services.'),
pht('You do not have permission to create new cluster services.')); pht('You do not have permission to create new cluster services.'));
$type_control = id(new AphrontFormRadioButtonControl()) $type_control = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Service Type')) ->setLabel(pht('Service Type'))
->setName('serviceType') ->setName('serviceType')
@ -239,14 +91,10 @@ final class AlmanacServiceEditController
->setHeaderText($title) ->setHeaderText($title)
->setForm($form); ->setForm($form);
return $this->buildApplicationPage( return $this->newPage()
array( ->setTitle($title)
$crumbs, ->setCrumbs($crumbs)
$box, ->appendChild($box);
),
array(
'title' => $title,
));
} }
} }

View file

@ -8,45 +8,19 @@ final class AlmanacServiceListController
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$controller = id(new PhabricatorApplicationSearchController()) return id(new AlmanacServiceSearchEngine())
->setQueryKey($request->getURIData('queryKey')) ->setController($this)
->setSearchEngine(new AlmanacServiceSearchEngine()) ->buildResponse();
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
} }
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();
$can_create = $this->hasApplicationCapability( id(new AlmanacServiceEditEngine())
AlmanacCreateServicesCapability::CAPABILITY); ->setViewer($this->getViewer())
->addActionToCrumbs($crumbs);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Service'))
->setHref($this->getApplicationURI('service/edit/'))
->setIcon('fa-plus-square')
->setDisabled(!$can_create)
->setWorkflow(!$can_create));
return $crumbs; return $crumbs;
} }
public function buildSideNavView() {
$viewer = $this->getViewer();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new AlmanacServiceSearchEngine())
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
} }

View file

@ -23,22 +23,19 @@ final class AlmanacServiceViewController
$title = pht('Service %s', $service->getName()); $title = pht('Service %s', $service->getName());
$property_list = $this->buildPropertyList($service); $properties = $this->buildPropertyList($service);
$action_list = $this->buildActionList($service); $actions = $this->buildActionList($service);
$property_list->setActionList($action_list); $details = $this->buildPropertySection($service);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setUser($viewer) ->setUser($viewer)
->setHeader($service->getName()) ->setHeader($service->getName())
->setPolicyObject($service); ->setPolicyObject($service)
->setHeaderIcon('fa-plug');
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($property_list);
$issue = null;
if ($service->isClusterService()) { if ($service->isClusterService()) {
$this->addClusterMessage( $issue = $this->addClusterMessage(
$box,
pht('This is a cluster service.'), pht('This is a cluster service.'),
pht( pht(
'This service is a cluster service. You do not have permission to '. 'This service is a cluster service. You do not have permission to '.
@ -49,36 +46,62 @@ final class AlmanacServiceViewController
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($service->getName()); $crumbs->addTextCrumb($service->getName());
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline( $timeline = $this->buildTransactionTimeline(
$service, $service,
new AlmanacServiceTransactionQuery()); new AlmanacServiceTransactionQuery());
$timeline->setShouldTerminate(true); $timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setMainColumn(array(
$issue,
$details,
$bindings,
$this->buildAlmanacPropertiesTable($service),
$timeline,
))
->setPropertyList($properties)
->setActionList($actions);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->appendChild( ->appendChild(
array( array(
$box, $view,
$bindings, ));
$this->buildAlmanacPropertiesTable($service),
$timeline,
));
} }
private function buildPropertyList(AlmanacService $service) { private function buildPropertyList(
AlmanacService $service) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($service);
$view->invokeWillRenderEvent();
return $view;
}
private function buildPropertySection(
AlmanacService $service) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView()) $properties = id(new PHUIPropertyListView())
->setUser($viewer) ->setUser($viewer);
->setObject($service);
$properties->addProperty( $properties->addProperty(
pht('Service Type'), pht('Service Type'),
$service->getServiceImplementation()->getServiceTypeShortName()); $service->getServiceImplementation()->getServiceTypeShortName());
return $properties; return id(new PHUIObjectBoxView())
->setHeaderText(pht('DETAILS'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
} }
private function buildActionList(AlmanacService $service) { private function buildActionList(AlmanacService $service) {
@ -126,7 +149,7 @@ final class AlmanacServiceViewController
->setHideServiceColumn(true); ->setHideServiceColumn(true);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader(pht('Service Bindings')) ->setHeader(pht('SERVICE BINDINGS'))
->addActionLink( ->addActionLink(
id(new PHUIButtonView()) id(new PHUIButtonView())
->setTag('a') ->setTag('a')
@ -138,6 +161,7 @@ final class AlmanacServiceViewController
return id(new PHUIObjectBoxView()) return id(new PHUIObjectBoxView())
->setHeader($header) ->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table); ->setTable($table);
} }

View file

@ -0,0 +1,85 @@
<?php
final class AlmanacDeviceEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'almanac.device';
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Almanac Devices');
}
public function getSummaryHeader() {
return pht('Edit Almanac Device Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Almanac devices.');
}
public function getEngineApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
protected function newEditableObject() {
return AlmanacDevice::initializeNewDevice();
}
protected function newObjectQuery() {
return new AlmanacDeviceQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Device');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Device');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Device: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Device');
}
protected function getObjectCreateShortText() {
return pht('Create Device');
}
protected function getEditorURI() {
return '/almanac/device/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/almanac/device/';
}
protected function getObjectViewURI($object) {
return $object->getURI();
}
protected function getCreateNewObjectPolicy() {
return $this->getApplication()->getPolicy(
AlmanacCreateDevicesCapability::CAPABILITY);
}
protected function buildCustomEditFields($object) {
return array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the device.'))
->setTransactionType(AlmanacDeviceTransaction::TYPE_NAME)
->setIsRequired(true)
->setValue($object->getName()),
);
}
}

View file

@ -0,0 +1,97 @@
<?php
final class AlmanacServiceEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'almanac.service';
private $serviceType;
public function setServiceType($service_type) {
$this->serviceType = $service_type;
return $this;
}
public function getServiceType() {
return $this->serviceType;
}
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Almanac Services');
}
public function getSummaryHeader() {
return pht('Edit Almanac Service Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Almanac services.');
}
public function getEngineApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
protected function newEditableObject() {
$service_type = $this->getServiceType();
return AlmanacService::initializeNewService($service_type);
}
protected function newObjectQuery() {
return new AlmanacServiceQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Service');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Service');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Service: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Service');
}
protected function getObjectCreateShortText() {
return pht('Create Service');
}
protected function getEditorURI() {
return '/almanac/service/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/almanac/service/';
}
protected function getObjectViewURI($object) {
return $object->getURI();
}
protected function getCreateNewObjectPolicy() {
return $this->getApplication()->getPolicy(
AlmanacCreateServicesCapability::CAPABILITY);
}
protected function buildCustomEditFields($object) {
return array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the service.'))
->setTransactionType(AlmanacServiceTransaction::TYPE_NAME)
->setIsRequired(true)
->setValue($object->getName()),
);
}
}

View file

@ -152,4 +152,28 @@ final class AlmanacServiceEditor
return $errors; return $errors;
} }
protected function validateAllTransactions(
PhabricatorLiskDAO $object,
array $xactions) {
$errors = parent::validateAllTransactions($object, $xactions);
if ($object->isClusterService()) {
$can_manage = PhabricatorPolicyFilter::hasCapability(
$this->getActor(),
new PhabricatorAlmanacApplication(),
AlmanacManageClusterServicesCapability::CAPABILITY);
if (!$can_manage) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
null,
pht('Restricted'),
pht('You do not have permission to manage cluster services.'),
null);
}
}
return $errors;
}
} }

View file

@ -22,6 +22,7 @@ final class PhabricatorBadgesEditRecipientsController
} }
$recipient_phids = $badge->getRecipientPHIDs(); $recipient_phids = $badge->getRecipientPHIDs();
$view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
if ($request->isFormPost()) { if ($request->isFormPost()) {
$recipient_spec = array(); $recipient_spec = array();
@ -53,7 +54,7 @@ final class PhabricatorBadgesEditRecipientsController
->applyTransactions($badge, $xactions); ->applyTransactions($badge, $xactions);
return id(new AphrontRedirectResponse()) return id(new AphrontRedirectResponse())
->setURI($request->getRequestURI()); ->setURI($view_uri);
} }
$recipient_phids = array_reverse($recipient_phids); $recipient_phids = array_reverse($recipient_phids);
@ -76,44 +77,26 @@ final class PhabricatorBadgesEditRecipientsController
$title = pht('Add Recipient'); $title = pht('Add Recipient');
if ($can_edit) { if ($can_edit) {
$header_name = pht('Edit Recipients'); $header_name = pht('Edit Recipients');
$view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
$form = new AphrontFormView(); $form = new AphrontFormView();
$form $form
->setUser($viewer) ->setUser($viewer)
->setFullWidth(true)
->appendControl( ->appendControl(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setName('phids') ->setName('phids')
->setLabel(pht('Add Recipients')) ->setLabel(pht('Add Recipients'))
->setDatasource(new PhabricatorPeopleDatasource())) ->setDatasource(new PhabricatorPeopleDatasource()));
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($view_uri)
->setValue(pht('Add Recipients')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setForm($form);
} }
$recipient_list = id(new PhabricatorBadgesRecipientsListView()) $dialog = id(new AphrontDialogView())
->setBadge($badge) ->setUser($viewer)
->setHandles($handles) ->setTitle(pht('Award Badges'))
->setUser($viewer); ->appendForm($form)
->addCancelButton($view_uri)
->addSubmitButton(pht('Add Recipients'));
$badge_url = $this->getApplicationURI('view/'.$id.'/'); return $dialog;
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($badge->getName(), $badge_url);
$crumbs->addTextCrumb(pht('Recipients'));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$form_box,
$recipient_list,
));
} }
} }

View file

@ -28,8 +28,7 @@ final class PhabricatorBadgesRemoveRecipientsController
return new Aphront404Response(); return new Aphront404Response();
} }
$recipients_uri = $view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
$this->getApplicationURI('recipients/'.$badge->getID().'/');
if ($request->isFormPost()) { if ($request->isFormPost()) {
$recipient_spec = array(); $recipient_spec = array();
@ -52,7 +51,7 @@ final class PhabricatorBadgesRemoveRecipientsController
->applyTransactions($badge, $xactions); ->applyTransactions($badge, $xactions);
return id(new AphrontRedirectResponse()) return id(new AphrontRedirectResponse())
->setURI($recipients_uri); ->setURI($view_uri);
} }
$handle = id(new PhabricatorHandleQuery()) $handle = id(new PhabricatorHandleQuery())
@ -68,7 +67,7 @@ final class PhabricatorBadgesRemoveRecipientsController
'Really revoke the badge "%s" from %s?', 'Really revoke the badge "%s" from %s?',
phutil_tag('strong', array(), $badge->getName()), phutil_tag('strong', array(), $badge->getName()),
phutil_tag('strong', array(), $handle->getName()))) phutil_tag('strong', array(), $handle->getName())))
->addCancelButton($recipients_uri) ->addCancelButton($view_uri)
->addSubmitButton(pht('Revoke Badge')); ->addSubmitButton(pht('Revoke Badge'));
return $dialog; return $dialog;

View file

@ -22,6 +22,7 @@ final class PhabricatorBadgesViewController
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($badge->getName()); $crumbs->addTextCrumb($badge->getName());
$crumbs->setBorder(true);
$title = $badge->getName(); $title = $badge->getName();
if ($badge->isArchived()) { if ($badge->isArchived()) {
@ -39,15 +40,12 @@ final class PhabricatorBadgesViewController
->setHeader($badge->getName()) ->setHeader($badge->getName())
->setUser($viewer) ->setUser($viewer)
->setPolicyObject($badge) ->setPolicyObject($badge)
->setStatus($status_icon, $status_color, $status_name); ->setStatus($status_icon, $status_color, $status_name)
->setHeaderIcon('fa-trophy');
$properties = $this->buildPropertyListView($badge); $properties = $this->buildPropertyListView($badge);
$actions = $this->buildActionListView($badge); $actions = $this->buildActionListView($badge);
$properties->setActionList($actions); $details = $this->buildDetailsView($badge);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$timeline = $this->buildTransactionTimeline( $timeline = $this->buildTransactionTimeline(
$badge, $badge,
@ -64,26 +62,47 @@ final class PhabricatorBadgesViewController
$add_comment = $this->buildCommentForm($badge); $add_comment = $this->buildCommentForm($badge);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setMainColumn(array(
$recipient_list,
$timeline,
$add_comment,
))
->setPropertyList($properties)
->setActionList($actions)
->addPropertySection(pht('BADGE DETAILS'), $details);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->setPageObjectPHIDs(array($badge->getPHID())) ->setPageObjectPHIDs(array($badge->getPHID()))
->appendChild( ->appendChild(
array( array(
$box, $view,
$recipient_list,
$timeline,
$add_comment,
)); ));
} }
private function buildPropertyListView(PhabricatorBadgesBadge $badge) { private function buildPropertyListView(
PhabricatorBadgesBadge $badge) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$view = id(new PHUIPropertyListView()) $view = id(new PHUIPropertyListView())
->setUser($viewer) ->setUser($viewer)
->setObject($badge); ->setObject($badge);
$view->invokeWillRenderEvent();
return $view;
}
private function buildDetailsView(
PhabricatorBadgesBadge $badge) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$quality = idx($badge->getQualityNameMap(), $badge->getQuality()); $quality = idx($badge->getQualityNameMap(), $badge->getQuality());
$view->addProperty( $view->addProperty(
@ -99,8 +118,6 @@ final class PhabricatorBadgesViewController
pht('Flavor'), pht('Flavor'),
$badge->getFlavor()); $badge->getFlavor());
$view->invokeWillRenderEvent();
$description = $badge->getDescription(); $description = $badge->getDescription();
if (strlen($description)) { if (strlen($description)) {
$view->addSectionHeader( $view->addSectionHeader(
@ -160,9 +177,10 @@ final class PhabricatorBadgesViewController
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->setName('Manage Recipients') ->setName('Add Recipients')
->setIcon('fa-users') ->setIcon('fa-users')
->setDisabled(!$can_edit) ->setDisabled(!$can_edit)
->setWorkflow(true)
->setHref($this->getApplicationURI("/recipients/{$id}/"))); ->setHref($this->getApplicationURI("/recipients/{$id}/")));
return $view; return $view;

View file

@ -1,6 +1,6 @@
<?php <?php
final class PhabricatorBadgesRecipientsListView extends AphrontTagView { final class PhabricatorBadgesRecipientsListView extends AphrontView {
private $badge; private $badge;
private $handles; private $handles;
@ -15,7 +15,7 @@ final class PhabricatorBadgesRecipientsListView extends AphrontTagView {
return $this; return $this;
} }
protected function getTagContent() { public function render() {
$viewer = $this->user; $viewer = $this->user;

View file

@ -52,7 +52,8 @@ final class PhabricatorCalendarEventViewController
$title = 'E'.$event->getID(); $title = 'E'.$event->getID();
$page_title = $title.' '.$event->getName(); $page_title = $title.' '.$event->getName();
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title, '/E'.$event->getID()); $crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
} }
if (!$event->getIsGhostEvent()) { if (!$event->getIsGhostEvent()) {
@ -63,12 +64,9 @@ final class PhabricatorCalendarEventViewController
$header = $this->buildHeaderView($event); $header = $this->buildHeaderView($event);
$actions = $this->buildActionView($event); $actions = $this->buildActionView($event);
$properties = $this->buildPropertyView($event); $properties = $this->buildPropertyListView($event);
$details = $this->buildPropertySection($event);
$properties->setActionList($actions); $description = $this->buildDescriptionView($event);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = $is_serious $add_comment_header = $is_serious
@ -90,26 +88,32 @@ final class PhabricatorCalendarEventViewController
->setAction($comment_uri) ->setAction($comment_uri)
->setSubmitButtonName(pht('Add Comment')); ->setSubmitButtonName(pht('Add Comment'));
return $this->buildApplicationPage( $view = id(new PHUITwoColumnView())
array( ->setHeader($header)
$crumbs, ->setMainColumn($timeline)
$box, ->setPropertyList($properties)
$timeline, ->addPropertySection(pht('DETAILS'), $details)
$add_comment_form, ->addPropertySection(pht('DESCRIPTION'), $description)
), ->setActionList($actions);
array(
'title' => $page_title, return $this->newPage()
'pageObjects' => array($event->getPHID()), ->setTitle($page_title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($event->getPHID()))
->appendChild(
array(
$view,
)); ));
} }
private function buildHeaderView(PhabricatorCalendarEvent $event) { private function buildHeaderView(
$viewer = $this->getRequest()->getUser(); PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$id = $event->getID(); $id = $event->getID();
$is_cancelled = $event->getIsCancelled(); $is_cancelled = $event->getIsCancelled();
$icon = $is_cancelled ? ('fa-times') : ('fa-calendar'); $icon = $is_cancelled ? ('fa-ban') : ('fa-check');
$color = $is_cancelled ? ('grey') : ('green'); $color = $is_cancelled ? ('red') : ('bluegrey');
$status = $is_cancelled ? pht('Cancelled') : pht('Active'); $status = $is_cancelled ? pht('Cancelled') : pht('Active');
$invite_status = $event->getUserInviteStatus($viewer->getPHID()); $invite_status = $event->getUserInviteStatus($viewer->getPHID());
@ -120,7 +124,8 @@ final class PhabricatorCalendarEventViewController
->setUser($viewer) ->setUser($viewer)
->setHeader($event->getName()) ->setHeader($event->getName())
->setStatus($icon, $color, $status) ->setStatus($icon, $color, $status)
->setPolicyObject($event); ->setPolicyObject($event)
->setHeaderIcon('fa-calendar');
if ($is_invite_pending) { if ($is_invite_pending) {
$decline_button = id(new PHUIButtonView()) $decline_button = id(new PHUIButtonView())
@ -245,13 +250,26 @@ final class PhabricatorCalendarEventViewController
return $actions; return $actions;
} }
private function buildPropertyView(PhabricatorCalendarEvent $event) { private function buildPropertyListView(
$viewer = $this->getRequest()->getUser(); PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView()) $properties = id(new PHUIPropertyListView())
->setUser($viewer) ->setUser($viewer)
->setObject($event); ->setObject($event);
$properties->invokeWillRenderEvent();
return $properties;
}
private function buildPropertySection(
PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
if ($event->getIsAllDay()) { if ($event->getIsAllDay()) {
$date_start = phabricator_date($event->getDateFrom(), $viewer); $date_start = phabricator_date($event->getDateFrom(), $viewer);
$date_end = phabricator_date($event->getDateTo(), $viewer); $date_end = phabricator_date($event->getDateTo(), $viewer);
@ -362,16 +380,23 @@ final class PhabricatorCalendarEventViewController
id(new PhabricatorCalendarIconSet()) id(new PhabricatorCalendarIconSet())
->getIconLabel($event->getIcon())); ->getIconLabel($event->getIcon()));
if (strlen($event->getDescription())) {
$description = new PHUIRemarkupView($viewer, $event->getDescription());
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent($description);
}
return $properties; return $properties;
} }
private function buildDescriptionView(
PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
if (strlen($event->getDescription())) {
$description = new PHUIRemarkupView($viewer, $event->getDescription());
$properties->addTextContent($description);
return $properties;
}
return null;
}
} }

View file

@ -192,10 +192,7 @@ final class CelerityDefaultPostprocessor
'sh-disabledbackground' => '#f3f3f3', 'sh-disabledbackground' => '#f3f3f3',
// Background color for "most" themes. // Background color for "most" themes.
'page.background' => '#f1f1f4', 'page.background' => '#f8f8fb',
// Background color for "dark" themes.
'page.background.dark' => '#ebecee',
'menu.profile.text' => 'rgba(255,255,255,.8)', 'menu.profile.text' => 'rgba(255,255,255,.8)',
'menu.profile.text.selected' => 'rgba(255,255,255,1)', 'menu.profile.text.selected' => 'rgba(255,255,255,1)',

View file

@ -21,15 +21,15 @@ final class PhabricatorCountdownViewController
$countdown_view = id(new PhabricatorCountdownView()) $countdown_view = id(new PhabricatorCountdownView())
->setUser($viewer) ->setUser($viewer)
->setCountdown($countdown) ->setCountdown($countdown);
->setHeadless(true);
$id = $countdown->getID(); $id = $countdown->getID();
$title = $countdown->getTitle(); $title = $countdown->getTitle();
$crumbs = $this $crumbs = $this
->buildApplicationCrumbs() ->buildApplicationCrumbs()
->addTextCrumb("C{$id}"); ->addTextCrumb("C{$id}")
->setBorder(true);
$epoch = $countdown->getEpoch(); $epoch = $countdown->getEpoch();
if ($epoch >= PhabricatorTime::getNow()) { if ($epoch >= PhabricatorTime::getNow()) {
@ -46,22 +46,30 @@ final class PhabricatorCountdownViewController
->setHeader($title) ->setHeader($title)
->setUser($viewer) ->setUser($viewer)
->setPolicyObject($countdown) ->setPolicyObject($countdown)
->setStatus($icon, $color, $status); ->setStatus($icon, $color, $status)
->setHeaderIcon('fa-rocket');
$actions = $this->buildActionListView($countdown); $actions = $this->buildActionListView($countdown);
$properties = $this->buildPropertyListView($countdown, $actions); $properties = $this->buildPropertyListView($countdown);
$subheader = $this->buildSubheaderView($countdown);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$timeline = $this->buildTransactionTimeline( $timeline = $this->buildTransactionTimeline(
$countdown, $countdown,
new PhabricatorCountdownTransactionQuery()); new PhabricatorCountdownTransactionQuery());
$add_comment = $this->buildCommentForm($countdown); $add_comment = $this->buildCommentForm($countdown);
$content = array(
$countdown_view,
$timeline,
$add_comment,
);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setSubheader($subheader)
->setMainColumn($content)
->setPropertyList($properties)
->setActionList($actions);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
@ -72,10 +80,7 @@ final class PhabricatorCountdownViewController
)) ))
->appendChild( ->appendChild(
array( array(
$object_box, $view,
$countdown_view,
$timeline,
$add_comment,
)); ));
} }
@ -114,34 +119,40 @@ final class PhabricatorCountdownViewController
} }
private function buildPropertyListView( private function buildPropertyListView(
PhabricatorCountdown $countdown, PhabricatorCountdown $countdown) {
PhabricatorActionListView $actions) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$view = id(new PHUIPropertyListView()) $view = id(new PHUIPropertyListView())
->setUser($viewer) ->setUser($viewer)
->setObject($countdown) ->setObject($countdown);
->setActionList($actions); $view->invokeWillRenderEvent();
$view->addProperty(
pht('Author'),
$viewer->renderHandle($countdown->getAuthorPHID()));
$view->invokeWillRenderEvent();
$description = $countdown->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);
}
return $view; return $view;
} }
private function buildSubheaderView(
PhabricatorCountdown $countdown) {
$viewer = $this->getViewer();
$author = $viewer->renderHandle($countdown->getAuthorPHID())->render();
$date = phabricator_datetime($countdown->getDateCreated(), $viewer);
$author = phutil_tag('strong', array(), $author);
$person = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($countdown->getAuthorPHID()))
->needProfileImage(true)
->executeOne();
$image_uri = $person->getProfileImageURI();
$image_href = '/p/'.$person->getUsername();
$content = pht('Authored by %s on %s.', $author, $date);
return id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
}
private function buildCommentForm(PhabricatorCountdown $countdown) { private function buildCommentForm(PhabricatorCountdown $countdown) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -1,46 +1,31 @@
<?php <?php
final class PhabricatorCountdownView extends AphrontTagView { final class PhabricatorCountdownView extends AphrontView {
private $countdown; private $countdown;
private $headless;
public function setHeadless($headless) {
$this->headless = $headless;
return $this;
}
public function setCountdown(PhabricatorCountdown $countdown) { public function setCountdown(PhabricatorCountdown $countdown) {
$this->countdown = $countdown; $this->countdown = $countdown;
return $this; return $this;
} }
public function render() {
protected function getTagContent() {
$countdown = $this->countdown; $countdown = $this->countdown;
require_celerity_resource('phabricator-countdown-css'); require_celerity_resource('phabricator-countdown-css');
$header = null; $header_text = array(
if (!$this->headless) { 'C'.$countdown->getID(),
$header = phutil_tag( ' ',
'div', phutil_tag(
'a',
array( array(
'class' => 'phabricator-timer-header', 'href' => '/countdown/'.$countdown->getID(),
), ),
array( $countdown->getTitle()),
'C'.$countdown->getID(), );
' ',
phutil_tag(
'a',
array(
'href' => '/countdown/'.$countdown->getID(),
),
$countdown->getTitle()),
));
}
$header = id(new PHUIHeaderView())
->setHeader($header_text);
$ths = array( $ths = array(
phutil_tag('th', array(), pht('Days')), phutil_tag('th', array(), pht('Days')),
@ -66,12 +51,23 @@ final class PhabricatorCountdownView extends AphrontTagView {
), ),
$launch_date); $launch_date);
$description = $countdown->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($this->getUser(), $description);
$description = phutil_tag(
'div',
array(
'class' => 'countdown-description phabricator-remarkup',
),
$description);
}
$container = celerity_generate_unique_node_id(); $container = celerity_generate_unique_node_id();
$content = phutil_tag( $content = phutil_tag(
'div', 'div',
array('class' => 'phabricator-timer', 'id' => $container), array('class' => 'phabricator-timer', 'id' => $container),
array( array(
$header, $description,
phutil_tag('table', array('class' => 'phabricator-timer-table'), array( phutil_tag('table', array('class' => 'phabricator-timer-table'), array(
phutil_tag('tr', array(), $ths), phutil_tag('tr', array(), $ths),
phutil_tag('tr', array(), $dashes), phutil_tag('tr', array(), $dashes),
@ -84,7 +80,11 @@ final class PhabricatorCountdownView extends AphrontTagView {
'container' => $container, 'container' => $container,
)); ));
return $content; return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('phabricator-timer-view')
->appendChild($content);
} }
} }

View file

@ -21,6 +21,7 @@ final class PhabricatorDashboardPanelCoreCustomField
public function readValueFromObject(PhabricatorCustomFieldInterface $object) { public function readValueFromObject(PhabricatorCustomFieldInterface $object) {
$key = $this->getProxy()->getRawStandardFieldKey(); $key = $this->getProxy()->getRawStandardFieldKey();
$this->setValueFromStorage($object->getProperty($key)); $this->setValueFromStorage($object->getProperty($key));
$this->didSetValueFromStorage();
} }
public function applyApplicationTransactionInternalEffects( public function applyApplicationTransactionInternalEffects(

View file

@ -92,4 +92,130 @@ abstract class DifferentialController extends PhabricatorController {
return $toc_view; return $toc_view;
} }
protected function loadDiffProperties(array $diffs) {
$diffs = mpull($diffs, null, 'getID');
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID IN (%Ld)',
array_keys($diffs));
$properties = mgroup($properties, 'getDiffID');
foreach ($diffs as $id => $diff) {
$values = idx($properties, $id, array());
$values = mpull($values, 'getData', 'getName');
$diff->attachDiffProperties($values);
}
}
protected function loadHarbormasterData(array $diffs) {
$viewer = $this->getViewer();
$diffs = mpull($diffs, null, 'getPHID');
$buildables = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withBuildablePHIDs(array_keys($diffs))
->withManualBuildables(false)
->needBuilds(true)
->needTargets(true)
->execute();
$buildables = mpull($buildables, null, 'getBuildablePHID');
foreach ($diffs as $phid => $diff) {
$diff->attachBuildable(idx($buildables, $phid));
}
$target_map = array();
foreach ($diffs as $phid => $diff) {
$target_map[$phid] = $diff->getBuildTargetPHIDs();
}
$all_target_phids = array_mergev($target_map);
if ($all_target_phids) {
$unit_messages = id(new HarbormasterBuildUnitMessage())->loadAllWhere(
'buildTargetPHID IN (%Ls)',
$all_target_phids);
$unit_messages = mgroup($unit_messages, 'getBuildTargetPHID');
} else {
$unit_messages = array();
}
foreach ($diffs as $phid => $diff) {
$target_phids = idx($target_map, $phid, array());
$messages = array_select_keys($unit_messages, $target_phids);
$messages = array_mergev($messages);
$diff->attachUnitMessages($messages);
}
// For diffs with no messages, look for legacy unit messages stored on the
// diff itself.
foreach ($diffs as $phid => $diff) {
if ($diff->getUnitMessages()) {
continue;
}
if (!$diff->hasDiffProperty('arc:unit')) {
continue;
}
$legacy_messages = $diff->getProperty('arc:unit');
if (!$legacy_messages) {
continue;
}
// 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_messages = array_slice($legacy_messages, 0, 100);
$messages = array();
foreach ($legacy_messages as $message) {
$messages[] = HarbormasterBuildUnitMessage::newFromDictionary(
new HarbormasterBuildTarget(),
$this->getModernUnitMessageDictionary($message));
}
$diff->attachUnitMessages($messages);
}
}
private function getModernUnitMessageDictionary(array $map) {
// Strip out `null` values to satisfy stricter typechecks.
foreach ($map as $key => $value) {
if ($value === null) {
unset($map[$key]);
}
}
return $map;
}
protected function getDiffTabLabels(array $diffs) {
// Make sure we're only going to render unique diffs.
$diffs = mpull($diffs, null, 'getID');
$labels = array(pht('Left'), pht('Right'));
$results = array();
foreach ($diffs as $diff) {
if (count($diffs) == 2) {
$label = array_shift($labels);
$label = pht('%s (Diff %d)', $label, $diff->getID());
} else {
$label = pht('Diff %d', $diff->getID());
}
$results[] = array(
$label,
$diff,
);
}
return $results;
}
} }

View file

@ -102,10 +102,8 @@ final class DifferentialRevisionViewController extends DifferentialController {
} }
} }
$props = id(new DifferentialDiffProperty())->loadAllWhere( $this->loadDiffProperties($diffs);
'diffID = %d', $props = $target_manual->getDiffProperties();
$target_manual->getID());
$props = mpull($props, 'getData', 'getName');
$object_phids = array_merge( $object_phids = array_merge(
$revision->getReviewers(), $revision->getReviewers(),
@ -256,23 +254,17 @@ final class DifferentialRevisionViewController extends DifferentialController {
array($diff_vs, $target->getID())); array($diff_vs, $target->getID()));
$detail_diffs = mpull($detail_diffs, null, 'getPHID'); $detail_diffs = mpull($detail_diffs, null, 'getPHID');
$buildables = id(new HarbormasterBuildableQuery()) $this->loadHarbormasterData($detail_diffs);
->setViewer($viewer)
->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(
$detail_diffs, $detail_diffs,
$revision, $revision,
$field_list); $field_list);
$unit_box = $this->buildUnitMessagesView(
$target,
$revision);
$comment_view = $this->buildTransactions( $comment_view = $this->buildTransactions(
$revision, $revision,
$diff_vs ? $diffs[$diff_vs] : $target, $diff_vs ? $diffs[$diff_vs] : $target,
@ -469,6 +461,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
$operations_box, $operations_box,
$revision_detail_box, $revision_detail_box,
$diff_detail_box, $diff_detail_box,
$unit_box,
$page_pane, $page_pane,
); );
@ -972,18 +965,9 @@ final class DifferentialRevisionViewController extends DifferentialController {
return null; return null;
} }
// Make sure we're only going to render unique diffs.
$diffs = mpull($diffs, null, 'getID');
$labels = array(pht('Left'), pht('Right'));
$property_lists = array(); $property_lists = array();
foreach ($diffs as $diff) { foreach ($this->getDiffTabLabels($diffs) as $tab) {
if (count($diffs) == 2) { list($label, $diff) = $tab;
$label = array_shift($labels);
$label = pht('%s (Diff %d)', $label, $diff->getID());
} else {
$label = pht('Diff %d', $diff->getID());
}
$property_lists[] = array( $property_lists[] = array(
$label, $label,
@ -1083,4 +1067,44 @@ final class DifferentialRevisionViewController extends DifferentialController {
->setOperation($operation); ->setOperation($operation);
} }
private function buildUnitMessagesView(
$diff,
DifferentialRevision $revision) {
$viewer = $this->getViewer();
if (!$diff->getUnitMessages()) {
return null;
}
$interesting_messages = array();
foreach ($diff->getUnitMessages() as $message) {
switch ($message->getResult()) {
case ArcanistUnitTestResult::RESULT_PASS:
case ArcanistUnitTestResult::RESULT_SKIP:
break;
default:
$interesting_messages[] = $message;
break;
}
}
if (!$interesting_messages) {
return null;
}
$excuse = null;
if ($diff->hasDiffProperty('arc:unit-excuse')) {
$excuse = $diff->getProperty('arc:unit-excuse');
}
return id(new HarbormasterUnitSummaryView())
->setUser($viewer)
->setExcuse($excuse)
->setBuildable($diff->getBuildable())
->setUnitMessages($diff->getUnitMessages())
->setLimit(5)
->setShowViewAll(true);
}
} }

View file

@ -1,7 +1,7 @@
<?php <?php
final class DifferentialUnitField final class DifferentialUnitField
extends DifferentialHarbormasterField { extends DifferentialCustomField {
public function getFieldKey() { public function getFieldKey() {
return 'differential:unit'; return 'differential:unit';
@ -31,51 +31,6 @@ final class DifferentialUnitField
return $this->getFieldName(); return $this->getFieldName();
} }
protected function getLegacyProperty() {
return 'arc:unit';
}
protected function getDiffPropertyKeys() {
return array(
'arc:unit',
'arc:unit-excuse',
);
}
protected function loadHarbormasterTargetMessages(array $target_phids) {
return id(new HarbormasterBuildUnitMessage())->loadAllWhere(
'buildTargetPHID IN (%Ls)',
$target_phids);
}
protected function newModernMessage(array $message) {
return HarbormasterBuildUnitMessage::newFromDictionary(
new HarbormasterBuildTarget(),
$this->getModernUnitMessageDictionary($message));
}
protected function newHarbormasterMessageView(array $messages) {
foreach ($messages as $key => $message) {
switch ($message->getResult()) {
case ArcanistUnitTestResult::RESULT_PASS:
case ArcanistUnitTestResult::RESULT_SKIP:
// Don't show "Pass" or "Skip" in the UI since they aren't very
// interesting. The user can click through to the full results if
// they want details.
unset($messages[$key]);
break;
}
}
if (!$messages) {
return null;
}
return id(new HarbormasterUnitPropertyView())
->setLimit(10)
->setUnitMessages($messages);
}
public function getWarningsForDetailView() { public function getWarningsForDetailView() {
$status = $this->getObject()->getActiveDiff()->getUnitStatus(); $status = $this->getObject()->getActiveDiff()->getUnitStatus();
@ -94,9 +49,7 @@ final class DifferentialUnitField
return $warnings; return $warnings;
} }
protected function renderHarbormasterStatus( public function renderDiffPropertyViewValue(DifferentialDiff $diff) {
DifferentialDiff $diff,
array $messages) {
$colors = array( $colors = array(
DifferentialUnitStatus::UNIT_NONE => 'grey', DifferentialUnitStatus::UNIT_NONE => 'grey',
@ -110,89 +63,15 @@ final class DifferentialUnitField
$message = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); $message = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
$note = array();
$groups = mgroup($messages, 'getResult');
$groups = array_select_keys(
$groups,
array(
ArcanistUnitTestResult::RESULT_FAIL,
ArcanistUnitTestResult::RESULT_BROKEN,
ArcanistUnitTestResult::RESULT_UNSOUND,
ArcanistUnitTestResult::RESULT_SKIP,
ArcanistUnitTestResult::RESULT_PASS,
)) + $groups;
foreach ($groups as $result => $group) {
$count = phutil_count($group);
switch ($result) {
case ArcanistUnitTestResult::RESULT_PASS:
$note[] = pht('%s Passed Test(s)', $count);
break;
case ArcanistUnitTestResult::RESULT_FAIL:
$note[] = pht('%s Failed Test(s)', $count);
break;
case ArcanistUnitTestResult::RESULT_SKIP:
$note[] = pht('%s Skipped Test(s)', $count);
break;
case ArcanistUnitTestResult::RESULT_BROKEN:
$note[] = pht('%s Broken Test(s)', $count);
break;
case ArcanistUnitTestResult::RESULT_UNSOUND:
$note[] = pht('%s Unsound Test(s)', $count);
break;
default:
$note[] = pht('%s Other Test(s)', $count);
break;
}
}
$buildable = $diff->getBuildable();
if ($buildable) {
$full_results = '/harbormaster/unit/'.$buildable->getID().'/';
$note[] = phutil_tag(
'a',
array(
'href' => $full_results,
),
pht('View Full Results'));
}
$excuse = $diff->getProperty('arc:unit-excuse');
if (strlen($excuse)) {
$excuse = array(
phutil_tag('strong', array(), pht('Excuse:')),
' ',
phutil_escape_html_newlines($excuse),
);
$note[] = $excuse;
}
$note = phutil_implode_html(" \xC2\xB7 ", $note);
$status = id(new PHUIStatusListView()) $status = id(new PHUIStatusListView())
->addItem( ->addItem(
id(new PHUIStatusItemView()) id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color)
->setTarget($message) ->setTarget($message));
->setNote($note));
return $status; 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;
}
} }

View file

@ -40,6 +40,8 @@ final class DifferentialDiff
private $properties = array(); private $properties = array();
private $buildable = self::ATTACHABLE; private $buildable = self::ATTACHABLE;
private $unitMessages = self::ATTACHABLE;
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
self::CONFIG_AUX_PHID => true, self::CONFIG_AUX_PHID => true,
@ -334,6 +336,20 @@ final class DifferentialDiff
return $this->assertAttachedKey($this->properties, $key); return $this->assertAttachedKey($this->properties, $key);
} }
public function hasDiffProperty($key) {
$properties = $this->getDiffProperties();
return array_key_exists($key, $properties);
}
public function attachDiffProperties(array $properties) {
$this->properties = $properties;
return $this;
}
public function getDiffProperties() {
return $this->assertAttached($this->properties);
}
public function attachBuildable(HarbormasterBuildable $buildable = null) { public function attachBuildable(HarbormasterBuildable $buildable = null) {
$this->buildable = $buildable; $this->buildable = $buildable;
return $this; return $this;
@ -391,6 +407,17 @@ final class DifferentialDiff
} }
public function attachUnitMessages(array $unit_messages) {
$this->unitMessages = $unit_messages;
return $this;
}
public function getUnitMessages() {
return $this->assertAttached($this->unitMessages);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */
@ -429,6 +456,15 @@ final class DifferentialDiff
/* -( HarbormasterBuildableInterface )------------------------------------- */ /* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildableDisplayPHID() {
$container_phid = $this->getHarbormasterContainerPHID();
if ($container_phid) {
return $container_phid;
}
return $this->getHarbormasterBuildablePHID();
}
public function getHarbormasterBuildablePHID() { public function getHarbormasterBuildablePHID() {
return $this->getPHID(); return $this->getPHID();
} }

View file

@ -432,6 +432,10 @@ final class DifferentialRevision extends DifferentialDAO
/* -( HarbormasterBuildableInterface )------------------------------------- */ /* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildableDisplayPHID() {
return $this->getHarbormasterContainerPHID();
}
public function getHarbormasterBuildablePHID() { public function getHarbormasterBuildablePHID() {
return $this->loadActiveDiff()->getPHID(); return $this->loadActiveDiff()->getPHID();
} }

View file

@ -30,10 +30,6 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
return self::GROUP_UTILITIES; return self::GROUP_UTILITIES;
} }
public function isPrototype() {
return true;
}
public function getHelpDocumentationArticles(PhabricatorUser $viewer) { public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
return array( return array(
array( array(
@ -60,8 +56,8 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
'authorizations/(?:query/(?P<queryKey>[^/]+)/)?' => 'authorizations/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockAuthorizationListController', 'DrydockAuthorizationListController',
), ),
'create/' => 'DrydockBlueprintCreateController', $this->getEditRoutePattern('edit/')
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController', => 'DrydockBlueprintEditController',
), ),
'(?P<type>resource)/' => array( '(?P<type>resource)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockResourceListController', '(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockResourceListController',

View file

@ -15,6 +15,10 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
return pht('Almanac Hosts'); return pht('Almanac Hosts');
} }
public function getBlueprintIcon() {
return 'fa-server';
}
public function getDescription() { public function getDescription() {
return pht( return pht(
'Allows Drydock to lease existing hosts defined in an Almanac service '. 'Allows Drydock to lease existing hosts defined in an Almanac service '.

View file

@ -15,6 +15,10 @@ abstract class DrydockBlueprintImplementation extends Phobject {
abstract public function getBlueprintName(); abstract public function getBlueprintName();
abstract public function getDescription(); abstract public function getDescription();
public function getBlueprintIcon() {
return 'fa-map-o';
}
public function getFieldSpecifications() { public function getFieldSpecifications() {
$fields = array(); $fields = array();

View file

@ -13,6 +13,10 @@ final class DrydockWorkingCopyBlueprintImplementation
return pht('Working Copy'); return pht('Working Copy');
} }
public function getBlueprintIcon() {
return 'fa-folder-open';
}
public function getDescription() { public function getDescription() {
return pht('Allows Drydock to check out working copies of repositories.'); return pht('Allows Drydock to check out working copies of repositories.');
} }

View file

@ -3,24 +3,18 @@
abstract class DrydockBlueprintController abstract class DrydockBlueprintController
extends DrydockController { extends DrydockController {
public function buildSideNavView() { public function buildApplicationMenu() {
$nav = new AphrontSideNavFilterView(); return $this->newApplicationMenu()
$nav->setBaseURI(new PhutilURI($this->getApplicationURI())); ->setSearchEngine(new DrydockBlueprintSearchEngine());
id(new DrydockBlueprintSearchEngine())
->setViewer($this->getRequest()->getUser())
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
} }
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb( $crumbs->addTextCrumb(
pht('Blueprints'), pht('Blueprints'),
$this->getApplicationURI('blueprint/')); $this->getApplicationURI('blueprint/'));
return $crumbs; return $crumbs;
} }

View file

@ -1,79 +0,0 @@
<?php
final class DrydockBlueprintCreateController
extends DrydockBlueprintController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$this->requireApplicationCapability(
DrydockCreateBlueprintsCapability::CAPABILITY);
$implementations =
DrydockBlueprintImplementation::getAllBlueprintImplementations();
$errors = array();
$e_blueprint = null;
if ($request->isFormPost()) {
$class = $request->getStr('blueprint-type');
if (!isset($implementations[$class])) {
$e_blueprint = pht('Required');
$errors[] = pht('You must choose a blueprint type.');
}
if (!$errors) {
$edit_uri = $this->getApplicationURI('blueprint/edit/?class='.$class);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
}
$control = id(new AphrontFormRadioButtonControl())
->setName('blueprint-type')
->setLabel(pht('Blueprint Type'))
->setError($e_blueprint);
foreach ($implementations as $implementation_name => $implementation) {
$disabled = !$implementation->isEnabled();
$control->addButton(
$implementation_name,
$implementation->getBlueprintName(),
array(
pht('Provides: %s', $implementation->getType()),
phutil_tag('br'),
phutil_tag('br'),
$implementation->getDescription(),
),
$disabled ? 'disabled' : null,
$disabled);
}
$title = pht('Create New Blueprint');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('New Blueprint'));
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild($control)
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($this->getApplicationURI('blueprint/'))
->setValue(pht('Continue')));
$box = id(new PHUIObjectBoxView())
->setFormErrors($errors)
->setHeaderText($title)
->setForm($form);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
}

View file

@ -3,168 +3,95 @@
final class DrydockBlueprintEditController extends DrydockBlueprintController { final class DrydockBlueprintEditController extends DrydockBlueprintController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $engine = id(new DrydockBlueprintEditEngine())
->setController($this);
$id = $request->getURIData('id'); $id = $request->getURIData('id');
if (!$id) {
if ($id) {
$blueprint = id(new DrydockBlueprintQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$blueprint) {
return new Aphront404Response();
}
$impl = $blueprint->getImplementation();
$cancel_uri = $this->getApplicationURI('blueprint/'.$id.'/');
} else {
$this->requireApplicationCapability( $this->requireApplicationCapability(
DrydockCreateBlueprintsCapability::CAPABILITY); DrydockCreateBlueprintsCapability::CAPABILITY);
$class = $request->getStr('class'); $type = $request->getStr('blueprintType');
$impl = DrydockBlueprintImplementation::getNamedImplementation($class); $impl = DrydockBlueprintImplementation::getNamedImplementation($type);
if (!$impl || !$impl->isEnabled()) { if (!$impl || !$impl->isEnabled()) {
return new Aphront400Response(); return $this->buildTypeSelectionResponse();
} }
$blueprint = DrydockBlueprint::initializeNewBlueprint($viewer) $engine
->setClassName($class) ->addContextParameter('blueprintType', $type)
->attachImplementation($impl); ->setBlueprintImplementation($impl);
$cancel_uri = $this->getApplicationURI('blueprint/');
} }
$field_list = PhabricatorCustomField::getObjectFields( return $engine->buildResponse();
$blueprint, }
PhabricatorCustomField::ROLE_EDIT);
$field_list private function buildTypeSelectionResponse() {
->setViewer($viewer) $request = $this->getRequest();
->readFieldsFromStorage($blueprint); $viewer = $this->getViewer();
$implementations =
DrydockBlueprintImplementation::getAllBlueprintImplementations();
$v_name = $blueprint->getBlueprintName();
$e_name = true;
$errors = array(); $errors = array();
$validation_exception = null; $e_blueprint = null;
if ($request->isFormPost()) { if ($request->isFormPost()) {
$v_view_policy = $request->getStr('viewPolicy'); $class = $request->getStr('blueprintType');
$v_edit_policy = $request->getStr('editPolicy'); if (!isset($implementations[$class])) {
$v_name = $request->getStr('name'); $e_blueprint = pht('Required');
if (!strlen($v_name)) { $errors[] = pht('You must choose a blueprint type.');
$e_name = pht('Required');
$errors[] = pht('You must name this blueprint.');
}
if (!$errors) {
$xactions = array();
$xactions = $field_list->buildFieldTransactionsFromRequest(
new DrydockBlueprintTransaction(),
$request);
$xactions[] = id(new DrydockBlueprintTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($v_view_policy);
$xactions[] = id(new DrydockBlueprintTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($v_edit_policy);
$xactions[] = id(new DrydockBlueprintTransaction())
->setTransactionType(DrydockBlueprintTransaction::TYPE_NAME)
->setNewValue($v_name);
$editor = id(new DrydockBlueprintEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($blueprint, $xactions);
$id = $blueprint->getID();
$save_uri = $this->getApplicationURI("blueprint/{$id}/");
return id(new AphrontRedirectResponse())->setURI($save_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
}
} }
} }
$policies = id(new PhabricatorPolicyQuery()) $control = id(new AphrontFormRadioButtonControl())
->setViewer($viewer) ->setName('blueprintType')
->setObject($blueprint) ->setLabel(pht('Blueprint Type'))
->execute(); ->setError($e_blueprint);
foreach ($implementations as $implementation_name => $implementation) {
$disabled = !$implementation->isEnabled();
$impl_icon = $implementation->getBlueprintIcon();
$impl_name = $implementation->getBlueprintName();
$impl_icon = id(new PHUIIconView())
->setIcon($impl_icon, 'lightgreytext');
$control->addButton(
$implementation_name,
array($impl_icon, ' ', $impl_name),
array(
pht('Provides: %s', $implementation->getType()),
phutil_tag('br'),
phutil_tag('br'),
$implementation->getDescription(),
),
$disabled ? 'disabled' : null,
$disabled);
}
$title = pht('Create New Blueprint');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('New Blueprint'));
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($viewer) ->setUser($viewer)
->addHiddenInput('class', $request->getStr('class')) ->appendChild($control)
->appendChild( ->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormSubmitControl())
->setLabel(pht('Name')) ->addCancelButton($this->getApplicationURI('blueprint/'))
->setName('name') ->setValue(pht('Continue')));
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Blueprint Type'))
->setValue($impl->getBlueprintName()))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($blueprint)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($blueprint)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies));
$field_list->appendFieldsToForm($form);
$crumbs = $this->buildApplicationCrumbs();
if ($blueprint->getID()) {
$title = pht('Edit Blueprint');
$header = pht('Edit Blueprint %d', $blueprint->getID());
$crumbs->addTextCrumb(pht('Blueprint %d', $blueprint->getID()));
$crumbs->addTextCrumb(pht('Edit'));
$submit = pht('Save Blueprint');
} else {
$title = pht('New Blueprint');
$header = pht('New Blueprint');
$crumbs->addTextCrumb(pht('New Blueprint'));
$submit = pht('Create Blueprint');
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit)
->addCancelButton($cancel_uri));
$box = id(new PHUIObjectBoxView()) $box = id(new PHUIObjectBoxView())
->setHeaderText($header)
->setValidationException($validation_exception)
->setFormErrors($errors) ->setFormErrors($errors)
->setHeaderText($title)
->setForm($form); ->setForm($form);
return $this->buildApplicationPage( return $this->newPage()
array( ->setTitle($title)
$crumbs, ->setCrumbs($crumbs)
$box, ->appendChild($box);
),
array(
'title' => $title,
));
} }
} }

View file

@ -7,29 +7,18 @@ final class DrydockBlueprintListController extends DrydockBlueprintController {
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$querykey = $request->getURIData('queryKey'); return id(new DrydockBlueprintSearchEngine())
->setController($this)
$request = $this->getRequest(); ->buildResponse();
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($querykey)
->setSearchEngine(new DrydockBlueprintSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
} }
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$can_create = $this->hasApplicationCapability(
DrydockCreateBlueprintsCapability::CAPABILITY);
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PHUIListItemView()) id(new DrydockBlueprintEditEngine())
->setName(pht('New Blueprint')) ->setViewer($this->getViewer())
->setHref($this->getApplicationURI('/blueprint/create/')) ->addActionToCrumbs($crumbs);
->setDisabled(!$can_create)
->setWorkflow(!$can_create)
->setIcon('fa-plus-square'));
return $crumbs; return $crumbs;
} }

View file

@ -127,8 +127,12 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController {
private function buildPropertyListView( private function buildPropertyListView(
DrydockBlueprint $blueprint, DrydockBlueprint $blueprint,
PhabricatorActionListView $actions) { PhabricatorActionListView $actions) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($blueprint);
$view = new PHUIPropertyListView();
$view->setActionList($actions); $view->setActionList($actions);
$view->addProperty( $view->addProperty(

View file

@ -0,0 +1,11 @@
<?php
abstract class DrydockRepositoryOperationController
extends DrydockController {
public function buildApplicationMenu() {
return $this->newApplicationMenu()
->setSearchEngine(new DrydockRepositoryOperationSearchEngine());
}
}

View file

@ -1,7 +1,7 @@
<?php <?php
final class DrydockRepositoryOperationDismissController final class DrydockRepositoryOperationDismissController
extends DrydockController { extends DrydockRepositoryOperationController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();

View file

@ -1,37 +1,16 @@
<?php <?php
final class DrydockRepositoryOperationListController final class DrydockRepositoryOperationListController
extends DrydockController { extends DrydockRepositoryOperationController {
public function shouldAllowPublic() { public function shouldAllowPublic() {
return true; return true;
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$query_key = $request->getURIData('queryKey'); return id(new DrydockRepositoryOperationSearchEngine())
->setController($this)
$engine = new DrydockRepositoryOperationSearchEngine(); ->buildResponse();
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($query_key)
->setSearchEngine($engine)
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView() {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$engine = id(new DrydockRepositoryOperationSearchEngine())
->setViewer($this->getViewer());
$engine->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
} }
} }

View file

@ -1,7 +1,7 @@
<?php <?php
final class DrydockRepositoryOperationStatusController final class DrydockRepositoryOperationStatusController
extends DrydockController { extends DrydockRepositoryOperationController {
public function shouldAllowPublic() { public function shouldAllowPublic() {
return true; return true;

View file

@ -1,7 +1,7 @@
<?php <?php
final class DrydockRepositoryOperationViewController final class DrydockRepositoryOperationViewController
extends DrydockController { extends DrydockRepositoryOperationController {
public function shouldAllowPublic() { public function shouldAllowPublic() {
return true; return true;
@ -50,16 +50,14 @@ final class DrydockRepositoryOperationViewController
->setUser($viewer) ->setUser($viewer)
->setOperation($operation); ->setOperation($operation);
return $this->buildApplicationPage( return $this->newPage()
array( ->setTitle($title)
$crumbs, ->setCrumbs($crumbs)
$object_box, ->appendChild(
$status_view, array(
), $object_box,
array( $status_view,
'title' => $title, ));
));
} }
private function buildActionListView(DrydockRepositoryOperation $operation) { private function buildActionListView(DrydockRepositoryOperation $operation) {

View file

@ -28,6 +28,7 @@ final class DrydockBlueprintCoreCustomField
public function readValueFromObject(PhabricatorCustomFieldInterface $object) { public function readValueFromObject(PhabricatorCustomFieldInterface $object) {
$key = $this->getProxy()->getRawStandardFieldKey(); $key = $this->getProxy()->getRawStandardFieldKey();
$this->setValueFromStorage($object->getDetail($key)); $this->setValueFromStorage($object->getDetail($key));
$this->didSetValueFromStorage();
} }
public function applyApplicationTransactionInternalEffects( public function applyApplicationTransactionInternalEffects(

View file

@ -0,0 +1,115 @@
<?php
final class DrydockBlueprintEditEngine
extends PhabricatorEditEngine {
private $blueprintImplementation;
const ENGINECONST = 'drydock.blueprint';
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Drydock Blueprints');
}
public function getSummaryHeader() {
return pht('Edit Drydock Blueprint Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Drydock blueprints.');
}
public function getEngineApplicationClass() {
return 'PhabricatorDrydockApplication';
}
public function setBlueprintImplementation(
DrydockBlueprintImplementation $impl) {
$this->blueprintImplementation = $impl;
return $this;
}
public function getBlueprintImplementation() {
return $this->blueprintImplementation;
}
protected function newEditableObject() {
$viewer = $this->getViewer();
$blueprint = DrydockBlueprint::initializeNewBlueprint($viewer);
$impl = $this->getBlueprintImplementation();
if ($impl) {
$blueprint
->setClassName(get_class($impl))
->attachImplementation(clone $impl);
}
return $blueprint;
}
protected function newObjectQuery() {
return new DrydockBlueprintQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Blueprint');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Blueprint');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Blueprint: %s', $object->getBlueprintName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Blueprint');
}
protected function getObjectCreateShortText() {
return pht('Create Blueprint');
}
protected function getEditorURI() {
return '/drydock/blueprint/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/drydock/blueprint/';
}
protected function getObjectViewURI($object) {
$id = $object->getID();
return "/drydock/blueprint/{$id}/";
}
protected function getCreateNewObjectPolicy() {
return $this->getApplication()->getPolicy(
DrydockCreateBlueprintsCapability::CAPABILITY);
}
protected function buildCustomEditFields($object) {
$impl = $object->getImplementation();
return array(
id(new PhabricatorStaticEditField())
->setKey('type')
->setLabel(pht('Blueprint Type'))
->setDescription(pht('Type of blueprint.'))
->setValue($impl->getBlueprintName()),
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the blueprint.'))
->setTransactionType(DrydockBlueprintTransaction::TYPE_NAME)
->setIsRequired(true)
->setValue($object->getBlueprintName()),
);
}
}

View file

@ -11,6 +11,10 @@ final class DrydockBlueprintEditor
return pht('Drydock Blueprints'); return pht('Drydock Blueprints');
} }
protected function supportsSearch() {
return true;
}
public function getTransactionTypes() { public function getTransactionTypes() {
$types = parent::getTransactionTypes(); $types = parent::getTransactionTypes();
@ -80,4 +84,36 @@ final class DrydockBlueprintEditor
return parent::applyCustomExternalTransaction($object, $xaction); return parent::applyCustomExternalTransaction($object, $xaction);
} }
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case DrydockBlueprintTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getBlueprintName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('You must choose a name for this blueprint.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
continue;
}
break;
}
return $errors;
}
} }

View file

@ -39,6 +39,12 @@ final class DrydockBlueprintQuery extends DrydockQuery {
return $this; return $this;
} }
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
new DrydockBlueprintNameNgrams(),
$ngrams);
}
public function newResultObject() { public function newResultObject() {
return new DrydockBlueprint(); return new DrydockBlueprint();
} }

View file

@ -18,6 +18,10 @@ final class DrydockBlueprintSearchEngine
protected function buildQueryFromParameters(array $map) { protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery(); $query = $this->newQuery();
if ($map['match'] !== null) {
$query->withNameNgrams($map['match']);
}
if ($map['isDisabled'] !== null) { if ($map['isDisabled'] !== null) {
$query->withDisabled($map['isDisabled']); $query->withDisabled($map['isDisabled']);
} }
@ -27,6 +31,10 @@ final class DrydockBlueprintSearchEngine
protected function buildCustomSearchFields() { protected function buildCustomSearchFields() {
return array( return array(
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('match')
->setDescription(pht('Search for blueprints by name substring.')),
id(new PhabricatorSearchThreeStateField()) id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Disabled')) ->setLabel(pht('Disabled'))
->setKey('isDisabled') ->setKey('isDisabled')
@ -69,15 +77,29 @@ final class DrydockBlueprintSearchEngine
assert_instances_of($blueprints, 'DrydockBlueprint'); assert_instances_of($blueprints, 'DrydockBlueprint');
$viewer = $this->requireViewer(); $viewer = $this->requireViewer();
if ($blueprints) {
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($blueprints, 'getPHID'))
->withEdgeTypes(
array(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
));
$edge_query->execute();
}
$view = new PHUIObjectItemListView(); $view = new PHUIObjectItemListView();
foreach ($blueprints as $blueprint) { foreach ($blueprints as $blueprint) {
$impl = $blueprint->getImplementation();
$item = id(new PHUIObjectItemView()) $item = id(new PHUIObjectItemView())
->setHeader($blueprint->getBlueprintName()) ->setHeader($blueprint->getBlueprintName())
->setHref($this->getApplicationURI('/blueprint/'.$blueprint->getID())) ->setHref($blueprint->getURI())
->setObjectName(pht('Blueprint %d', $blueprint->getID())); ->setObjectName(pht('Blueprint %d', $blueprint->getID()));
if (!$blueprint->getImplementation()->isEnabled()) { if (!$impl->isEnabled()) {
$item->setDisabled(true); $item->setDisabled(true);
$item->addIcon('fa-chain-broken grey', pht('Implementation')); $item->addIcon('fa-chain-broken grey', pht('Implementation'));
} }
@ -87,7 +109,24 @@ final class DrydockBlueprintSearchEngine
$item->addIcon('fa-ban grey', pht('Disabled')); $item->addIcon('fa-ban grey', pht('Disabled'));
} }
$item->addAttribute($blueprint->getImplementation()->getBlueprintName()); $impl_icon = $impl->getBlueprintIcon();
$impl_name = $impl->getBlueprintName();
$impl_icon = id(new PHUIIconView())
->setIcon($impl_icon, 'lightgreytext');
$item->addAttribute(array($impl_icon, ' ', $impl_name));
$phid = $blueprint->getPHID();
$project_phids = $edge_query->getDestinationPHIDs(array($phid));
if ($project_phids) {
$project_handles = $viewer->loadHandles($project_phids);
$item->addAttribute(
id(new PHUIHandleTagListView())
->setLimit(4)
->setSlim(true)
->setHandles($project_handles));
}
$view->addItem($item); $view->addItem($item);
} }

View file

@ -70,7 +70,8 @@ final class DrydockRepositoryOperationSearchEngine
$icon = DrydockRepositoryOperation::getOperationStateIcon($state); $icon = DrydockRepositoryOperation::getOperationStateIcon($state);
$name = DrydockRepositoryOperation::getOperationStateName($state); $name = DrydockRepositoryOperation::getOperationStateName($state);
$item->addIcon($icon, $name); $item->setStatusIcon($icon, $name);
$item->addByline( $item->addByline(
array( array(
pht('Via:'), pht('Via:'),
@ -78,13 +79,14 @@ final class DrydockRepositoryOperationSearchEngine
$viewer->renderHandle($operation->getAuthorPHID()), $viewer->renderHandle($operation->getAuthorPHID()),
)); ));
$item->addAttribute( $object_phid = $operation->getObjectPHID();
$viewer->renderHandle( $repository_phid = $operation->getRepositoryPHID();
$operation->getObjectPHID()));
$item->addAttribute( $item->addAttribute($viewer->renderHandle($object_phid));
$viewer->renderHandle(
$operation->getRepositoryPHID())); if ($repository_phid !== $object_phid) {
$item->addAttribute($viewer->renderHandle($repository_phid));
}
$view->addItem($item); $view->addItem($item);
} }

View file

@ -8,7 +8,9 @@ final class DrydockBlueprint extends DrydockDAO
implements implements
PhabricatorApplicationTransactionInterface, PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface, PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface { PhabricatorCustomFieldInterface,
PhabricatorNgramsInterface,
PhabricatorProjectInterface {
protected $className; protected $className;
protected $blueprintName; protected $blueprintName;
@ -118,6 +120,11 @@ final class DrydockBlueprint extends DrydockDAO
return $log->save(); return $log->save();
} }
public function getURI() {
$id = $this->getID();
return "/drydock/blueprint/{$id}/";
}
/* -( Allocating Resources )----------------------------------------------- */ /* -( Allocating Resources )----------------------------------------------- */
@ -343,4 +350,14 @@ final class DrydockBlueprint extends DrydockDAO
} }
/* -( PhabricatorNgramInterface )------------------------------------------ */
public function newNgrams() {
return array(
id(new DrydockBlueprintNameNgrams())
->setValue($this->getBlueprintName()),
);
}
} }

View file

@ -0,0 +1,18 @@
<?php
final class DrydockBlueprintNameNgrams
extends PhabricatorSearchNgrams {
public function getNgramKey() {
return 'blueprintname';
}
public function getColumnName() {
return 'blueprintName';
}
public function getApplicationName() {
return 'drydock';
}
}

View file

@ -0,0 +1,9 @@
<?php
final class DrydockSchemaSpec extends PhabricatorConfigSchemaSpec {
public function buildSchemata() {
$this->buildEdgeSchemata(new DrydockBlueprint());
}
}

View file

@ -26,11 +26,20 @@ final class DrydockBlueprintDatasource
->execute(); ->execute();
$results = array(); $results = array();
foreach ($handles as $handle) { foreach ($blueprints as $blueprint) {
$results[] = id(new PhabricatorTypeaheadResult()) $handle = $handles[$blueprint->getPHID()];
->setName($handle->getName())
$result = id(new PhabricatorTypeaheadResult())
->setName($handle->getFullName())
->setPHID($handle->getPHID()); ->setPHID($handle->getPHID());
if ($blueprint->getIsDisabled()) {
$result->setClosed(pht('Disabled'));
}
$results[] = $result;
} }
return $results; return $results;
} }
} }

View file

@ -7,7 +7,6 @@ final class FundInitiativeViewController
return true; return true;
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
$id = $request->getURIData('id'); $id = $request->getURIData('id');
@ -22,6 +21,7 @@ final class FundInitiativeViewController
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($initiative->getMonogram()); $crumbs->addTextCrumb($initiative->getMonogram());
$crumbs->setBorder(true);
$title = pht( $title = pht(
'%s %s', '%s %s',
@ -43,32 +43,32 @@ final class FundInitiativeViewController
->setHeader($initiative->getName()) ->setHeader($initiative->getName())
->setUser($viewer) ->setUser($viewer)
->setPolicyObject($initiative) ->setPolicyObject($initiative)
->setStatus($status_icon, $status_color, $status_name); ->setStatus($status_icon, $status_color, $status_name)
->setHeaderIcon('fa-heart');
$properties = $this->buildPropertyListView($initiative); $properties = $this->buildPropertyListView($initiative);
$actions = $this->buildActionListView($initiative); $actions = $this->buildActionListView($initiative);
$properties->setActionList($actions); $details = $this->buildPropertySectionView($initiative);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$timeline = $this->buildTransactionTimeline( $timeline = $this->buildTransactionTimeline(
$initiative, $initiative,
new FundInitiativeTransactionQuery()); new FundInitiativeTransactionQuery());
$timeline $timeline->setShouldTerminate(true);
->setShouldTerminate(true);
return $this->buildApplicationPage( $view = id(new PHUITwoColumnView())
array( ->setHeader($header)
$crumbs, ->setMainColumn($timeline)
$box, ->setPropertyList($properties)
$timeline, ->addPropertySection(pht('DETAILS'), $details)
), ->setActionList($actions);
array(
'title' => $title, return $this->newPage()
'pageObjects' => array($initiative->getPHID()), ->setTitle($title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($initiative->getPHID()))
->appendChild(
array(
$view,
)); ));
} }
@ -79,6 +79,17 @@ final class FundInitiativeViewController
->setUser($viewer) ->setUser($viewer)
->setObject($initiative); ->setObject($initiative);
$view->invokeWillRenderEvent();
return $view;
}
private function buildPropertySectionView(FundInitiative $initiative) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$owner_phid = $initiative->getOwnerPHID(); $owner_phid = $initiative->getOwnerPHID();
$merchant_phid = $initiative->getMerchantPHID(); $merchant_phid = $initiative->getMerchantPHID();
@ -94,8 +105,6 @@ final class FundInitiativeViewController
pht('Total Funding'), pht('Total Funding'),
$initiative->getTotalAsCurrency()->formatForDisplay()); $initiative->getTotalAsCurrency()->formatForDisplay());
$view->invokeWillRenderEvent();
$description = $initiative->getDescription(); $description = $initiative->getDescription();
if (strlen($description)) { if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description); $description = new PHUIRemarkupView($viewer, $description);

View file

@ -76,16 +76,17 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication {
=> 'HarbormasterBuildActionController', => 'HarbormasterBuildActionController',
), ),
'plan/' => array( 'plan/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' $this->getQueryRoutePattern() => 'HarbormasterPlanListController',
=> 'HarbormasterPlanListController', $this->getEditRoutePattern('edit/')
'edit/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanEditController', => 'HarbormasterPlanEditController',
'order/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanOrderController', 'order/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanOrderController',
'disable/(?P<id>\d+)/' => 'HarbormasterPlanDisableController', 'disable/(?P<id>\d+)/' => 'HarbormasterPlanDisableController',
'run/(?P<id>\d+)/' => 'HarbormasterPlanRunController', 'run/(?P<id>\d+)/' => 'HarbormasterPlanRunController',
'(?P<id>\d+)/' => 'HarbormasterPlanViewController', '(?P<id>\d+)/' => 'HarbormasterPlanViewController',
), ),
'unit/' => array( 'unit/' => array(
'(?P<id>\d+)/' => 'HarbormasterUnitMessagesController', '(?P<id>\d+)/' => 'HarbormasterUnitMessageListController',
'view/(?P<id>\d+)/' => 'HarbormasterUnitMessageViewController',
), ),
'lint/' => array( 'lint/' => array(
'(?P<id>\d+)/' => 'HarbormasterLintMessagesController', '(?P<id>\d+)/' => 'HarbormasterLintMessagesController',

View file

@ -0,0 +1,90 @@
<?php
final class HarbormasterUnitStatus
extends Phobject {
public static function getUnitStatusIcon($status) {
$map = self::getUnitStatusDictionary($status);
$default = 'fa-question-circle';
return idx($map, 'icon', $default);
}
public static function getUnitStatusColor($status) {
$map = self::getUnitStatusDictionary($status);
$default = 'violet';
return idx($map, 'color', $default);
}
public static function getUnitStatusLabel($status) {
$map = self::getUnitStatusDictionary($status);
$default = pht('Unknown Status ("%s")', $status);
return idx($map, 'label', $default);
}
public static function getUnitStatusSort($status) {
$map = self::getUnitStatusDictionary($status);
$default = 'N';
return idx($map, 'sort', $default);
}
private static function getUnitStatusDictionary($status) {
$map = self::getUnitStatusMap();
$default = array();
return idx($map, $status, $default);
}
public static function getUnitStatusCountLabel($status, $count) {
$count = new PhutilNumber($count);
switch ($status) {
case ArcanistUnitTestResult::RESULT_FAIL:
return pht('%s Failed Test(s)', $count);
case ArcanistUnitTestResult::RESULT_BROKEN:
return pht('%s Broken Test(s)', $count);
case ArcanistUnitTestResult::RESULT_UNSOUND:
return pht('%s Unsound Test(s)', $count);
case ArcanistUnitTestResult::RESULT_PASS:
return pht('%s Passed Test(s)', $count);
case ArcanistUnitTestResult::RESULT_SKIP:
return pht('%s Skipped Test(s)', $count);
}
return pht('%s Other Test(s)', $count);
}
private static function getUnitStatusMap() {
return array(
ArcanistUnitTestResult::RESULT_FAIL => array(
'label' => pht('Failed'),
'icon' => 'fa-times',
'color' => 'red',
'sort' => 'A',
),
ArcanistUnitTestResult::RESULT_BROKEN => array(
'label' => pht('Broken'),
'icon' => 'fa-bomb',
'color' => 'indigo',
'sort' => 'B',
),
ArcanistUnitTestResult::RESULT_UNSOUND => array(
'label' => pht('Unsound'),
'icon' => 'fa-exclamation-triangle',
'color' => 'yellow',
'sort' => 'C',
),
ArcanistUnitTestResult::RESULT_PASS => array(
'label' => pht('Passed'),
'icon' => 'fa-check',
'color' => 'green',
'sort' => 'D',
),
ArcanistUnitTestResult::RESULT_SKIP => array(
'label' => pht('Skipped'),
'icon' => 'fa-fast-forward',
'color' => 'blue',
'sort' => 'E',
),
);
}
}

View file

@ -7,34 +7,21 @@ final class HarbormasterBuildableListController extends HarbormasterController {
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$controller = id(new PhabricatorApplicationSearchController()) $items = array();
->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine(new HarbormasterBuildableSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller); $items[] = id(new PHUIListItemView())
} ->setType(PHUIListItemView::TYPE_LABEL)
->setName(pht('Build Plans'));
public function buildSideNavView($for_app = false) { $items[] = id(new PHUIListItemView())
$user = $this->getRequest()->getUser(); ->setType(PHUIListItemView::TYPE_LINK)
->setName(pht('Manage Build Plans'))
->setHref($this->getApplicationURI('plan/'));
$nav = new AphrontSideNavFilterView(); return id(new HarbormasterBuildableSearchEngine())
$nav->setBaseURI(new PhutilURI($this->getApplicationURI())); ->setController($this)
->setNavigationItems($items)
id(new HarbormasterBuildableSearchEngine()) ->buildResponse();
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->addLabel(pht('Build Plans'));
$nav->addFilter('plan/', pht('Manage Build Plans'));
$nav->selectFilter(null);
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNavView(true)->getMenu();
} }
} }

View file

@ -9,8 +9,6 @@ final class HarbormasterBuildableViewController
$buildable = id(new HarbormasterBuildableQuery()) $buildable = id(new HarbormasterBuildableQuery())
->setViewer($viewer) ->setViewer($viewer)
->withIDs(array($request->getURIData('id'))) ->withIDs(array($request->getURIData('id')))
->needBuildableHandles(true)
->needContainerHandles(true)
->executeOne(); ->executeOne();
if (!$buildable) { if (!$buildable) {
return new Aphront404Response(); return new Aphront404Response();
@ -167,15 +165,18 @@ final class HarbormasterBuildableViewController
->setActionList($actions); ->setActionList($actions);
$box->addPropertyList($properties); $box->addPropertyList($properties);
if ($buildable->getContainerHandle() !== null) { $container_phid = $buildable->getContainerPHID();
$buildable_phid = $buildable->getBuildablePHID();
if ($container_phid) {
$properties->addProperty( $properties->addProperty(
pht('Container'), pht('Container'),
$buildable->getContainerHandle()->renderLink()); $viewer->renderHandle($container_phid));
} }
$properties->addProperty( $properties->addProperty(
pht('Buildable'), pht('Buildable'),
$buildable->getBuildableHandle()->renderLink()); $viewer->renderHandle($buildable_phid));
$properties->addProperty( $properties->addProperty(
pht('Origin'), pht('Origin'),
@ -335,25 +336,11 @@ final class HarbormasterBuildableViewController
} }
if ($unit_data) { if ($unit_data) {
$unit_table = id(new HarbormasterUnitPropertyView()) $unit = id(new HarbormasterUnitSummaryView())
->setUser($viewer) ->setBuildable($buildable)
->setLimit(25) ->setUnitMessages($unit_data)
->setUnitMessages($unit_data); ->setShowViewAll(true)
->setLimit(5);
$unit_href = $this->getApplicationURI('unit/'.$buildable->getID().'/');
$unit_header = id(new PHUIHeaderView())
->setHeader(pht('Unit Tests'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($unit_href)
->setIcon('fa-list-ul')
->setText('View All'));
$unit = id(new PHUIObjectBoxView())
->setHeader($unit_header)
->setTable($unit_table);
} else { } else {
$unit = null; $unit = null;
} }

View file

@ -2,6 +2,11 @@
abstract class HarbormasterController extends PhabricatorController { abstract class HarbormasterController extends PhabricatorController {
public function buildApplicationMenu() {
return $this->newApplicationMenu()
->setSearchEngine(new HarbormasterBuildableSearchEngine());
}
protected function addBuildableCrumb( protected function addBuildableCrumb(
PHUICrumbsView $crumbs, PHUICrumbsView $crumbs,
HarbormasterBuildable $buildable) { HarbormasterBuildable $buildable) {

View file

@ -2,6 +2,11 @@
abstract class HarbormasterPlanController extends HarbormasterController { abstract class HarbormasterPlanController extends HarbormasterController {
public function buildApplicationMenu() {
return $this->newApplicationMenu()
->setSearchEngine(new HarbormasterBuildPlanSearchEngine());
}
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();

View file

@ -3,146 +3,9 @@
final class HarbormasterPlanEditController extends HarbormasterPlanController { final class HarbormasterPlanEditController extends HarbormasterPlanController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); return id(new HarbormasterBuildPlanEditEngine())
->setController($this)
$id = $request->getURIData('id'); ->buildResponse();
if ($id) {
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
} else {
$this->requireApplicationCapability(
HarbormasterCreatePlansCapability::CAPABILITY);
$plan = HarbormasterBuildPlan::initializeNewBuildPlan($viewer);
}
$e_name = true;
$v_name = $plan->getName();
$v_view = $plan->getViewPolicy();
$v_edit = $plan->getEditPolicy();
$validation_exception = null;
if ($request->isFormPost()) {
$xactions = array();
$v_name = $request->getStr('name');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$e_name = null;
$type_name = HarbormasterBuildPlanTransaction::TYPE_NAME;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$xactions[] = id(new HarbormasterBuildPlanTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new HarbormasterBuildPlanTransaction())
->setTransactionType($type_view)
->setNewValue($v_view);
$xactions[] = id(new HarbormasterBuildPlanTransaction())
->setTransactionType($type_edit)
->setNewValue($v_edit);
$editor = id(new HarbormasterBuildPlanEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
try {
$editor->applyTransactions($plan, $xactions);
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI('plan/'.$plan->getID().'/'));
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $validation_exception->getShortMessage(
HarbormasterBuildPlanTransaction::TYPE_NAME);
}
}
$is_new = (!$plan->getID());
if ($is_new) {
$title = pht('New Build Plan');
$cancel_uri = $this->getApplicationURI('plan/');
$save_button = pht('Create Build Plan');
} else {
$id = $plan->getID();
$title = pht('Edit Build Plan');
$cancel_uri = $this->getApplicationURI('plan/'.$plan->getID().'/');
$save_button = pht('Save Build Plan');
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($plan)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendControl(
id(new AphrontFormTextControl())
->setLabel(pht('Plan Name'))
->setName('name')
->setError($e_name)
->setValue($v_name))
->appendControl(
id(new AphrontFormPolicyControl())
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($plan)
->setPolicies($policies)
->setValue($v_view)
->setName('viewPolicy'))
->appendControl(
id(new AphrontFormPolicyControl())
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($plan)
->setPolicies($policies)
->setValue($v_edit)
->setName('editPolicy'))
->appendControl(
id(new AphrontFormSubmitControl())
->setValue($save_button)
->addCancelButton($cancel_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setValidationException($validation_exception)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$crumbs->addTextCrumb(pht('New Build Plan'));
} else {
$id = $plan->getID();
$crumbs->addTextCrumb(
pht('Plan %d', $id),
$this->getApplicationURI("plan/{$id}/"));
$crumbs->addTextCrumb(pht('Edit'));
}
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
} }
} }

View file

@ -7,53 +7,19 @@ final class HarbormasterPlanListController extends HarbormasterPlanController {
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$controller = id(new PhabricatorApplicationSearchController()) return id(new HarbormasterBuildPlanSearchEngine())
->setQueryKey($request->getURIData('queryKey')) ->setController($this)
->setSearchEngine(new HarbormasterBuildPlanSearchEngine()) ->buildResponse();
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView($for_app = false) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
if ($for_app) {
$nav->addFilter('new/', pht('New Build Plan'));
}
id(new HarbormasterBuildPlanSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNavView(true)->getMenu();
} }
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();
$can_create = $this->hasApplicationCapability( id(new HarbormasterBuildPlanEditEngine())
HarbormasterCreatePlansCapability::CAPABILITY); ->setViewer($this->getViewer())
->addActionToCrumbs($crumbs);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('New Build Plan'))
->setHref($this->getApplicationURI('plan/edit/'))
->setDisabled(!$can_create)
->setWorkflow(!$can_create)
->setIcon('fa-plus-square'));
return $crumbs; return $crumbs;
} }
} }

View file

@ -1,6 +1,6 @@
<?php <?php
final class HarbormasterPlanRunController extends HarbormasterController { final class HarbormasterPlanRunController extends HarbormasterPlanController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -187,8 +187,6 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
if ($is_deadlocking) { if ($is_deadlocking) {
$item->setStatusIcon('fa-warning red'); $item->setStatusIcon('fa-warning red');
} }
$step_list->addItem($item);
} }
$step_list->setFlush(true); $step_list->setFlush(true);

View file

@ -1,6 +1,7 @@
<?php <?php
final class HarbormasterStepAddController extends HarbormasterController { final class HarbormasterStepAddController
extends HarbormasterPlanController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -1,6 +1,7 @@
<?php <?php
final class HarbormasterStepDeleteController extends HarbormasterController { final class HarbormasterStepDeleteController
extends HarbormasterPlanController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -1,6 +1,7 @@
<?php <?php
final class HarbormasterStepEditController extends HarbormasterController { final class HarbormasterStepEditController
extends HarbormasterPlanController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -1,6 +1,7 @@
<?php <?php
final class HarbormasterStepViewController extends HarbormasterController { final class HarbormasterStepViewController
extends HarbormasterPlanController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -1,6 +1,6 @@
<?php <?php
final class HarbormasterUnitMessagesController final class HarbormasterUnitMessageListController
extends HarbormasterController { extends HarbormasterController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
@ -34,14 +34,10 @@ final class HarbormasterUnitMessagesController
$unit_data = array(); $unit_data = array();
} }
$unit_table = id(new HarbormasterUnitPropertyView()) $unit = id(new HarbormasterUnitSummaryView())
->setUser($viewer) ->setBuildable($buildable)
->setUnitMessages($unit_data); ->setUnitMessages($unit_data);
$unit = id(new PHUIObjectBoxView())
->setHeaderText(pht('Unit Tests'))
->setTable($unit_table);
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$this->addBuildableCrumb($crumbs, $buildable); $this->addBuildableCrumb($crumbs, $buildable);
$crumbs->addTextCrumb(pht('Unit Tests')); $crumbs->addTextCrumb(pht('Unit Tests'));

View file

@ -0,0 +1,122 @@
<?php
final class HarbormasterUnitMessageViewController
extends HarbormasterController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$message_id = $request->getURIData('id');
$message = id(new HarbormasterBuildUnitMessage())->load($message_id);
if (!$message) {
return new Aphront404Response();
}
$build_target = id(new HarbormasterBuildTargetQuery())
->setViewer($viewer)
->withPHIDs(array($message->getBuildTargetPHID()))
->executeOne();
if (!$build_target) {
return new Aphront404Response();
}
$build = $build_target->getBuild();
$buildable = $build->getBuildable();
$buildable_id = $buildable->getID();
$id = $message->getID();
$display_name = $message->getUnitMessageDisplayName();
$status = $message->getResult();
$status_icon = HarbormasterUnitStatus::getUnitStatusIcon($status);
$status_color = HarbormasterUnitStatus::getUnitStatusColor($status);
$status_label = HarbormasterUnitStatus::getUnitStatusLabel($status);
$header = id(new PHUIHeaderView())
->setHeader($display_name)
->setStatus($status_icon, $status_color, $status_label);
$properties = $this->buildPropertyListView($message);
$actions = $this->buildActionView($message, $build);
$properties->setActionList($actions);
$unit = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$crumbs = $this->buildApplicationCrumbs();
$this->addBuildableCrumb($crumbs, $buildable);
$crumbs->addTextCrumb(
pht('Unit Tests'),
"/harbormaster/unit/{$buildable_id}/");
$crumbs->addTextCrumb(pht('Unit %d', $id));
$title = array(
$display_name,
$buildable->getMonogram(),
);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($unit);
}
private function buildPropertyListView(
HarbormasterBuildUnitMessage $message) {
$request = $this->getRequest();
$viewer = $request->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$view->addProperty(
pht('Run At'),
phabricator_datetime($message->getDateCreated(), $viewer));
$details = $message->getUnitMessageDetails();
if (strlen($details)) {
// TODO: Use the log view here, once it gets cleaned up.
$details = phutil_tag(
'div',
array(
'class' => 'PhabricatorMonospaced',
'style' =>
'white-space: pre-wrap; '.
'color: #666666; '.
'overflow-x: auto;',
),
$details);
} else {
$details = phutil_tag('em', array(), pht('No details provided.'));
}
$view->addSectionHeader(
pht('Details'),
PHUIPropertyListView::ICON_TESTPLAN);
$view->addTextContent($details);
return $view;
}
private function buildActionView(
HarbormasterBuildUnitMessage $message,
HarbormasterBuild $build) {
$viewer = $this->getViewer();
$view = id(new PhabricatorActionListView())
->setUser($viewer);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View Build'))
->setHref($build->getURI())
->setIcon('fa-wrench'));
return $view;
}
}

View file

@ -0,0 +1,89 @@
<?php
final class HarbormasterBuildPlanEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'harbormaster.buildplan';
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Harbormaster Build Plans');
}
public function getSummaryHeader() {
return pht('Edit Harbormaster Build Plan Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Harbormaster build plans.');
}
public function getEngineApplicationClass() {
return 'PhabricatorHarbormasterApplication';
}
protected function newEditableObject() {
$viewer = $this->getViewer();
return HarbormasterBuildPlan::initializeNewBuildPlan($viewer);
}
protected function newObjectQuery() {
return new HarbormasterBuildPlanQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Build Plan');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Build Plan');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Build Plan: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Build Plan');
}
protected function getObjectCreateShortText() {
return pht('Create Build Plan');
}
protected function getEditorURI() {
return '/harbormaster/plan/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/harbormaster/plan/';
}
protected function getObjectViewURI($object) {
$id = $object->getID();
return "/harbormaster/plan/{$id}/";
}
protected function getCreateNewObjectPolicy() {
return $this->getApplication()->getPolicy(
HarbormasterCreatePlansCapability::CAPABILITY);
}
protected function buildCustomEditFields($object) {
return array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setIsRequired(true)
->setTransactionType(HarbormasterBuildPlanTransaction::TYPE_NAME)
->setDescription(pht('The build plan name.'))
->setConduitDescription(pht('Rename the plan.'))
->setConduitTypeDescription(pht('New plan name.'))
->setValue($object->getName()),
);
}
}

View file

@ -11,6 +11,10 @@ final class HarbormasterBuildPlanEditor
return pht('Harbormaster Build Plans'); return pht('Harbormaster Build Plans');
} }
protected function supportsSearch() {
return true;
}
public function getTransactionTypes() { public function getTransactionTypes() {
$types = parent::getTransactionTypes(); $types = parent::getTransactionTypes();
$types[] = HarbormasterBuildPlanTransaction::TYPE_NAME; $types[] = HarbormasterBuildPlanTransaction::TYPE_NAME;
@ -90,7 +94,7 @@ final class HarbormasterBuildPlanEditor
$error = new PhabricatorApplicationTransactionValidationError( $error = new PhabricatorApplicationTransactionValidationError(
$type, $type,
pht('Required'), pht('Required'),
pht('Plan name is required.'), pht('You must choose a name for your build plan.'),
last($xactions)); last($xactions));
$error->setIsMissingFieldError(true); $error->setIsMissingFieldError(true);

View file

@ -2,11 +2,23 @@
interface HarbormasterBuildableInterface { interface HarbormasterBuildableInterface {
/**
* Get the object PHID which best identifies this buildable to humans.
*
* This object is the primary object associated with the buildable in the
* UI. The most human-readable object for a buildable varies: for example,
* for diffs the container (the revision) is more meaningful than the
* buildable (the diff), but for commits the buildable (the commit) is more
* meaningful than the container (the repository).
*
* @return phid Related object PHID most meaningful for human viewers.
*/
public function getHarbormasterBuildableDisplayPHID();
public function getHarbormasterBuildablePHID(); public function getHarbormasterBuildablePHID();
public function getHarbormasterContainerPHID(); public function getHarbormasterContainerPHID();
public function getBuildVariables(); public function getBuildVariables();
public function getAvailableBuildVariables(); public function getAvailableBuildVariables();
} }

View file

@ -0,0 +1,150 @@
<?php
final class HarbormasterManagementArchiveLogsWorkflow
extends HarbormasterManagementWorkflow {
protected function didConstruct() {
$this
->setName('archive-logs')
->setExamples('**archive-logs** [__options__] --mode __mode__')
->setSynopsis(pht('Compress, decompress, store or destroy build logs.'))
->setArguments(
array(
array(
'name' => 'mode',
'param' => 'mode',
'help' => pht(
'Use "plain" to remove encoding, or "compress" to compress '.
'logs.'),
),
array(
'name' => 'details',
'help' => pht(
'Show more details about operations as they are performed. '.
'Slow! But also very reassuring!'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$viewer = $this->getViewer();
$mode = $args->getArg('mode');
if (!$mode) {
throw new PhutilArgumentUsageException(
pht('Choose an archival mode with --mode.'));
}
$valid_modes = array(
'plain',
'compress',
);
$valid_modes = array_fuse($valid_modes);
if (empty($valid_modes[$mode])) {
throw new PhutilArgumentUsageException(
pht(
'Unknown mode "%s". Valid modes are: %s.',
$mode,
implode(', ', $valid_modes)));
}
$log_table = new HarbormasterBuildLog();
$logs = new LiskMigrationIterator($log_table);
$show_details = $args->getArg('details');
if ($show_details) {
$total_old = 0;
$total_new = 0;
}
foreach ($logs as $log) {
echo tsprintf(
"%s\n",
pht('Processing Harbormaster build log #%d...', $log->getID()));
if ($show_details) {
$old_stats = $this->computeDetails($log);
}
switch ($mode) {
case 'plain':
$log->decompressLog();
break;
case 'compress':
$log->compressLog();
break;
}
if ($show_details) {
$new_stats = $this->computeDetails($log);
$this->printStats($old_stats, $new_stats);
$total_old += $old_stats['bytes'];
$total_new += $new_stats['bytes'];
}
}
if ($show_details) {
echo tsprintf(
"%s\n",
pht(
'Done. Total byte size of affected logs: %s -> %s.',
new PhutilNumber($total_old),
new PhutilNumber($total_new)));
}
return 0;
}
private function computeDetails(HarbormasterBuildLog $log) {
$bytes = 0;
$chunks = 0;
$hash = hash_init('sha1');
foreach ($log->newChunkIterator() as $chunk) {
$bytes += strlen($chunk->getChunk());
$chunks++;
hash_update($hash, $chunk->getChunkDisplayText());
}
return array(
'bytes' => $bytes,
'chunks' => $chunks,
'hash' => hash_final($hash),
);
}
private function printStats(array $old_stats, array $new_stats) {
echo tsprintf(
" %s\n",
pht(
'%s: %s -> %s',
pht('Stored Bytes'),
new PhutilNumber($old_stats['bytes']),
new PhutilNumber($new_stats['bytes'])));
echo tsprintf(
" %s\n",
pht(
'%s: %s -> %s',
pht('Stored Chunks'),
new PhutilNumber($old_stats['chunks']),
new PhutilNumber($new_stats['chunks'])));
echo tsprintf(
" %s\n",
pht(
'%s: %s -> %s',
pht('Data Hash'),
$old_stats['hash'],
$new_stats['hash']));
if ($old_stats['hash'] !== $new_stats['hash']) {
throw new Exception(
pht('Log data hashes differ! Something is tragically wrong!'));
}
}
}

View file

@ -21,8 +21,7 @@ final class HarbormasterBuildablePHIDType extends PhabricatorPHIDType {
array $phids) { array $phids) {
return id(new HarbormasterBuildableQuery()) return id(new HarbormasterBuildableQuery())
->withPHIDs($phids) ->withPHIDs($phids);
->needBuildableHandles(true);
} }
public function loadHandles( public function loadHandles(
@ -30,15 +29,30 @@ final class HarbormasterBuildablePHIDType extends PhabricatorPHIDType {
array $handles, array $handles,
array $objects) { array $objects) {
$viewer = $query->getViewer();
$target_phids = array();
foreach ($objects as $phid => $object) {
$target_phids[] = $object->getBuildablePHID();
}
$target_handles = $viewer->loadHandles($target_phids);
foreach ($handles as $phid => $handle) { foreach ($handles as $phid => $handle) {
$buildable = $objects[$phid]; $buildable = $objects[$phid];
$id = $buildable->getID(); $id = $buildable->getID();
$target = $buildable->getBuildableHandle()->getFullName(); $buildable_phid = $buildable->getBuildablePHID();
$handle->setURI("/B{$id}"); $target = $target_handles[$buildable_phid];
$handle->setName("B{$id}"); $target_name = $target->getFullName();
$handle->setFullName("B{$id}: ".$target);
$uri = $buildable->getURI();
$monogram = $buildable->getMonogram();
$handle
->setURI($uri)
->setName($monogram)
->setFullName("{$monogram}: {$target_name}");
} }
} }

View file

@ -35,6 +35,12 @@ final class HarbormasterBuildPlanQuery
return $this; return $this;
} }
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
new HarbormasterBuildPlanNameNgrams(),
$ngrams);
}
public function needBuildSteps($need) { public function needBuildSteps($need) {
$this->needBuildSteps = $need; $this->needBuildSteps = $need;
return $this; return $this;
@ -74,41 +80,45 @@ final class HarbormasterBuildPlanQuery
if ($this->ids !== null) { if ($this->ids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'id IN (%Ld)', 'plan.id IN (%Ld)',
$this->ids); $this->ids);
} }
if ($this->phids !== null) { if ($this->phids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'phid IN (%Ls)', 'plan.phid IN (%Ls)',
$this->phids); $this->phids);
} }
if ($this->statuses !== null) { if ($this->statuses !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'planStatus IN (%Ls)', 'plan.planStatus IN (%Ls)',
$this->statuses); $this->statuses);
} }
if (strlen($this->datasourceQuery)) { if (strlen($this->datasourceQuery)) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'name LIKE %>', 'plan.name LIKE %>',
$this->datasourceQuery); $this->datasourceQuery);
} }
if ($this->planAutoKeys !== null) { if ($this->planAutoKeys !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'planAutoKey IN (%Ls)', 'plan.planAutoKey IN (%Ls)',
$this->planAutoKeys); $this->planAutoKeys);
} }
return $where; return $where;
} }
protected function getPrimaryTableAlias() {
return 'plan';
}
public function getQueryApplicationClass() { public function getQueryApplicationClass() {
return 'PhabricatorHarbormasterApplication'; return 'PhabricatorHarbormasterApplication';
} }

View file

@ -17,6 +17,10 @@ final class HarbormasterBuildPlanSearchEngine
protected function buildCustomSearchFields() { protected function buildCustomSearchFields() {
return array( return array(
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('match')
->setDescription(pht('Search for namespaces by name substring.')),
id(new PhabricatorSearchCheckboxesField()) id(new PhabricatorSearchCheckboxesField())
->setLabel(pht('Status')) ->setLabel(pht('Status'))
->setKey('status') ->setKey('status')
@ -32,6 +36,10 @@ final class HarbormasterBuildPlanSearchEngine
protected function buildQueryFromParameters(array $map) { protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery(); $query = $this->newQuery();
if ($map['match'] !== null) {
$query->withNameNgrams($map['match']);
}
if ($map['status']) { if ($map['status']) {
$query->withStatuses($map['status']); $query->withStatuses($map['status']);
} }
@ -76,12 +84,23 @@ final class HarbormasterBuildPlanSearchEngine
$viewer = $this->requireViewer(); $viewer = $this->requireViewer();
if ($plans) {
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($plans, 'getPHID'))
->withEdgeTypes(
array(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
));
$edge_query->execute();
}
$list = new PHUIObjectItemListView(); $list = new PHUIObjectItemListView();
foreach ($plans as $plan) { foreach ($plans as $plan) {
$id = $plan->getID(); $id = $plan->getID();
$item = id(new PHUIObjectItemView()) $item = id(new PHUIObjectItemView())
->setObjectName(pht('Plan %d', $plan->getID())) ->setObjectName(pht('Plan %d', $id))
->setHeader($plan->getName()); ->setHeader($plan->getName());
if ($plan->isDisabled()) { if ($plan->isDisabled()) {
@ -94,6 +113,17 @@ final class HarbormasterBuildPlanSearchEngine
$item->setHref($this->getApplicationURI("plan/{$id}/")); $item->setHref($this->getApplicationURI("plan/{$id}/"));
$phid = $plan->getPHID();
$project_phids = $edge_query->getDestinationPHIDs(array($phid));
$project_handles = $viewer->loadHandles($project_phids);
$item->addAttribute(
id(new PHUIHandleTagListView())
->setLimit(4)
->setNoDataString(pht('No Projects'))
->setSlim(true)
->setHandles($project_handles));
$list->addItem($item); $list->addItem($item);
} }

View file

@ -7,11 +7,10 @@ final class HarbormasterBuildableQuery
private $phids; private $phids;
private $buildablePHIDs; private $buildablePHIDs;
private $containerPHIDs; private $containerPHIDs;
private $statuses;
private $manualBuildables; private $manualBuildables;
private $needContainerObjects; private $needContainerObjects;
private $needContainerHandles;
private $needBuildableHandles;
private $needBuilds; private $needBuilds;
private $needTargets; private $needTargets;
@ -45,13 +44,8 @@ final class HarbormasterBuildableQuery
return $this; return $this;
} }
public function needContainerHandles($need) { public function withStatuses(array $statuses) {
$this->needContainerHandles = $need; $this->statuses = $statuses;
return $this;
}
public function needBuildableHandles($need) {
$this->needBuildableHandles = $need;
return $this; return $this;
} }
@ -99,60 +93,23 @@ final class HarbormasterBuildableQuery
} }
protected function didFilterPage(array $page) { protected function didFilterPage(array $page) {
if ($this->needContainerObjects || $this->needContainerHandles) { if ($this->needContainerObjects) {
$container_phids = array_filter(mpull($page, 'getContainerPHID')); $container_phids = array_filter(mpull($page, 'getContainerPHID'));
if ($this->needContainerObjects) { if ($container_phids) {
$containers = array(); $containers = id(new PhabricatorObjectQuery())
if ($container_phids) {
$containers = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($container_phids)
->setParentQuery($this)
->execute();
$containers = mpull($containers, null, 'getPHID');
}
foreach ($page as $key => $buildable) {
$container_phid = $buildable->getContainerPHID();
$buildable->attachContainerObject(idx($containers, $container_phid));
}
}
if ($this->needContainerHandles) {
$handles = array();
if ($container_phids) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs($container_phids)
->setParentQuery($this)
->execute();
}
foreach ($page as $key => $buildable) {
$container_phid = $buildable->getContainerPHID();
$buildable->attachContainerHandle(idx($handles, $container_phid));
}
}
}
if ($this->needBuildableHandles) {
$handles = array();
$handle_phids = array_filter(mpull($page, 'getBuildablePHID'));
if ($handle_phids) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getViewer()) ->setViewer($this->getViewer())
->withPHIDs($handle_phids) ->withPHIDs($container_phids)
->setParentQuery($this) ->setParentQuery($this)
->execute(); ->execute();
$containers = mpull($containers, null, 'getPHID');
} else {
$containers = array();
} }
foreach ($page as $key => $buildable) { foreach ($page as $key => $buildable) {
$handle_phid = $buildable->getBuildablePHID(); $container_phid = $buildable->getContainerPHID();
$buildable->attachBuildableHandle(idx($handles, $handle_phid)); $buildable->attachContainerObject(idx($containers, $container_phid));
} }
} }
@ -203,6 +160,13 @@ final class HarbormasterBuildableQuery
$this->containerPHIDs); $this->containerPHIDs);
} }
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
'buildableStatus in (%Ls)',
$this->statuses);
}
if ($this->manualBuildables !== null) { if ($this->manualBuildables !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,

View file

@ -11,148 +11,87 @@ final class HarbormasterBuildableSearchEngine
return 'PhabricatorHarbormasterApplication'; return 'PhabricatorHarbormasterApplication';
} }
public function buildSavedQueryFromRequest(AphrontRequest $request) { public function newQuery() {
$saved = new PhabricatorSavedQuery(); return new HarbormasterBuildableQuery();
$revisions = $this->readPHIDsFromRequest(
$request,
'revisions',
array(
DifferentialRevisionPHIDType::TYPECONST,
));
$repositories = $this->readPHIDsFromRequest(
$request,
'repositories',
array(
PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
));
$container_phids = array_merge($revisions, $repositories);
$saved->setParameter('containerPHIDs', $container_phids);
$commits = $this->readPHIDsFromRequest(
$request,
'commits',
array(
PhabricatorRepositoryCommitPHIDType::TYPECONST,
));
$diffs = $this->readListFromRequest($request, 'diffs');
if ($diffs) {
$diffs = id(new DifferentialDiffQuery())
->setViewer($this->requireViewer())
->withIDs($diffs)
->execute();
$diffs = mpull($diffs, 'getPHID', 'getPHID');
}
$buildable_phids = array_merge($commits, $diffs);
$saved->setParameter('buildablePHIDs', $buildable_phids);
$saved->setParameter(
'manual',
$this->readBoolFromRequest($request, 'manual'));
return $saved;
} }
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { protected function buildCustomSearchFields() {
$query = id(new HarbormasterBuildableQuery()) return array(
->needContainerHandles(true) id(new PhabricatorSearchStringListField())
->needBuildableHandles(true); ->setKey('objectPHIDs')
->setAliases(array('objects'))
$container_phids = $saved->getParameter('containerPHIDs', array()); ->setLabel(pht('Objects'))
if ($container_phids) { ->setPlaceholder(pht('rXabcdef, PHID-DIFF-1234, ...'))
$query->withContainerPHIDs($container_phids); ->setDescription(pht('Search for builds of particular objects.')),
} id(new PhabricatorSearchStringListField())
->setKey('containerPHIDs')
$buildable_phids = $saved->getParameter('buildablePHIDs', array()); ->setAliases(array('containers'))
->setLabel(pht('Containers'))
if ($buildable_phids) { ->setPlaceholder(pht('rXYZ, R123, D456, ...'))
$query->withBuildablePHIDs($buildable_phids); ->setDescription(
} pht('Search for builds by containing revision or repository.')),
id(new PhabricatorSearchCheckboxesField())
$manual = $saved->getParameter('manual'); ->setKey('statuses')
if ($manual !== null) { ->setLabel(pht('Statuses'))
$query->withManualBuildables($manual); ->setOptions(HarbormasterBuildable::getBuildStatusMap())
} ->setDescription(pht('Search for builds by buildable status.')),
id(new PhabricatorSearchThreeStateField())
return $query; ->setLabel(pht('Manual'))
->setKey('manual')
->setDescription(
pht('Search for only manual or automatic buildables.'))
->setOptions(
pht('(Show All)'),
pht('Show Only Manual Builds'),
pht('Show Only Automated Builds')),
);
} }
public function buildSearchForm( private function resolvePHIDs(array $names) {
AphrontFormView $form, $viewer = $this->requireViewer();
PhabricatorSavedQuery $saved_query) {
$container_phids = $saved_query->getParameter('containerPHIDs', array()); $objects = id(new PhabricatorObjectQuery())
$buildable_phids = $saved_query->getParameter('buildablePHIDs', array()); ->setViewer($viewer)
->withNames($names)
->execute();
$all_phids = array_merge($container_phids, $buildable_phids); // TODO: Instead of using string lists, we should ideally be using some
// kind of smart field with resolver logic that can help users type the
// right stuff. For now, just return a bogus value here so nothing matches
// but the form doesn't explode.
if (!$objects) {
return array('-');
}
$revision_names = array(); return mpull($objects, 'getPHID');
$diff_names = array(); }
$repository_names = array();
$commit_names = array();
if ($all_phids) { protected function buildQueryFromParameters(array $map) {
$objects = id(new PhabricatorObjectQuery()) $query = $this->newQuery();
->setViewer($this->requireViewer())
->withPHIDs($all_phids)
->execute();
foreach ($all_phids as $phid) { if ($map['objectPHIDs']) {
$object = idx($objects, $phid); $phids = $this->resolvePHIDs($map['objectPHIDs']);
if (!$object) { if ($phids) {
continue; $query->withBuildablePHIDs($phids);
}
if ($object instanceof DifferentialRevision) {
$revision_names[] = 'D'.$object->getID();
} else if ($object instanceof DifferentialDiff) {
$diff_names[] = $object->getID();
} else if ($object instanceof PhabricatorRepository) {
$repository_names[] = $object->getMonogram();
} else if ($object instanceof PhabricatorRepositoryCommit) {
$repository = $object->getRepository();
$commit_names[] = $repository->formatCommitName(
$object->getCommitIdentifier());
}
} }
} }
$form if ($map['containerPHIDs']) {
->appendChild( $phids = $this->resolvePHIDs($map['containerPHIDs']);
id(new AphrontFormTextControl()) if ($phids) {
->setLabel(pht('Differential Revisions')) $query->withContainerPHIDs($phids);
->setName('revisions') }
->setValue(implode(', ', $revision_names))) }
->appendChild(
id(new AphrontFormTextControl()) if ($map['statuses']) {
->setLabel(pht('Differential Diffs')) $query->withStatuses($map['statuses']);
->setName('diffs') }
->setValue(implode(', ', $diff_names)))
->appendChild( if ($map['manual'] !== null) {
id(new AphrontFormTextControl()) $query->withManualBuildables($map['manual']);
->setLabel(pht('Repositories')) }
->setName('repositories')
->setValue(implode(', ', $repository_names))) return $query;
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Commits'))
->setName('commits')
->setValue(implode(', ', $commit_names)))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Origin'))
->setName('manual')
->setValue($this->getBoolFromQuery($saved_query, 'manual'))
->setOptions(
array(
'' => pht('(All Origins)'),
'true' => pht('Manual Buildables'),
'false' => pht('Automatic Buildables'),
)));
} }
protected function getURI($path) { protected function getURI($path) {
@ -185,35 +124,60 @@ final class HarbormasterBuildableSearchEngine
$viewer = $this->requireViewer(); $viewer = $this->requireViewer();
$phids = array();
foreach ($buildables as $buildable) {
$phids[] = $buildable->getBuildableObject()
->getHarbormasterBuildableDisplayPHID();
$phids[] = $buildable->getContainerPHID();
$phids[] = $buildable->getBuildablePHID();
}
$handles = $viewer->loadHandles($phids);
$list = new PHUIObjectItemListView(); $list = new PHUIObjectItemListView();
foreach ($buildables as $buildable) { foreach ($buildables as $buildable) {
$id = $buildable->getID(); $id = $buildable->getID();
$display_phid = $buildable->getBuildableObject()
->getHarbormasterBuildableDisplayPHID();
$container_phid = $buildable->getContainerPHID();
$buildable_phid = $buildable->getBuildablePHID();
$item = id(new PHUIObjectItemView()) $item = id(new PHUIObjectItemView())
->setHeader(pht('Buildable %d', $buildable->getID())); ->setObjectName(pht('Buildable %d', $buildable->getID()));
if ($buildable->getContainerHandle() !== null) {
$item->addAttribute($buildable->getContainerHandle()->getName()); if ($display_phid) {
} $handle = $handles[$display_phid];
if ($buildable->getBuildableHandle() !== null) { $item->setHeader($handle->getFullName());
$item->addAttribute($buildable->getBuildableHandle()->getFullName());
} }
if ($id) { if ($container_phid && ($container_phid != $display_phid)) {
$item->setHref("/B{$id}"); $handle = $handles[$container_phid];
$item->addAttribute($handle->getName());
} }
if ($buildable_phid && ($buildable_phid != $display_phid)) {
$handle = $handles[$buildable_phid];
$item->addAttribute($handle->getFullName());
}
$item->setHref($buildable->getURI());
if ($buildable->getIsManualBuildable()) { if ($buildable->getIsManualBuildable()) {
$item->addIcon('fa-wrench grey', pht('Manual')); $item->addIcon('fa-wrench grey', pht('Manual'));
} }
$item->setStatusIcon('fa-wrench '. $status = $buildable->getBuildableStatus();
HarbormasterBuildable::getBuildableStatusColor(
$buildable->getBuildableStatus())); $status_icon = HarbormasterBuildable::getBuildableStatusIcon($status);
$item->addByline(HarbormasterBuildable::getBuildableStatusName( $status_color = HarbormasterBuildable::getBuildableStatusColor($status);
$buildable->getBuildableStatus())); $status_label = HarbormasterBuildable::getBuildableStatusName($status);
$item->setStatusIcon("{$status_icon} {$status_color}", $status_label);
$list->addItem($item); $list->addItem($item);
} }
$result = new PhabricatorApplicationSearchResultView(); $result = new PhabricatorApplicationSearchResultView();

View file

@ -31,24 +31,7 @@ final class HarbormasterWaitForPreviousBuildStepImplementation
// Block until all previous builds of the same build plan have // Block until all previous builds of the same build plan have
// finished. // finished.
$plan = $build->getBuildPlan(); $plan = $build->getBuildPlan();
$existing_logs = id(new HarbormasterBuildLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withBuildTargetPHIDs(array($build_target->getPHID()))
->execute();
if ($existing_logs) {
$log = head($existing_logs);
} else {
$log = $build->createLog($build_target, 'waiting', 'blockers');
}
$blockers = $this->getBlockers($object, $plan, $build); $blockers = $this->getBlockers($object, $plan, $build);
if ($blockers) {
$log->start();
$log->append(pht("Blocked by: %s\n", implode(',', $blockers)));
$log->finalize();
}
if ($blockers) { if ($blockers) {
throw new PhabricatorWorkerYieldException(15); throw new PhabricatorWorkerYieldException(15);

View file

@ -13,8 +13,6 @@ final class HarbormasterBuildable extends HarbormasterDAO
private $buildableObject = self::ATTACHABLE; private $buildableObject = self::ATTACHABLE;
private $containerObject = self::ATTACHABLE; private $containerObject = self::ATTACHABLE;
private $buildableHandle = self::ATTACHABLE;
private $containerHandle = self::ATTACHABLE;
private $builds = self::ATTACHABLE; private $builds = self::ATTACHABLE;
const STATUS_BUILDING = 'building'; const STATUS_BUILDING = 'building';
@ -22,16 +20,16 @@ final class HarbormasterBuildable extends HarbormasterDAO
const STATUS_FAILED = 'failed'; const STATUS_FAILED = 'failed';
public static function getBuildableStatusName($status) { public static function getBuildableStatusName($status) {
switch ($status) { $map = self::getBuildStatusMap();
case self::STATUS_BUILDING: return idx($map, $status, pht('Unknown ("%s")', $status));
return pht('Building'); }
case self::STATUS_PASSED:
return pht('Passed'); public static function getBuildStatusMap() {
case self::STATUS_FAILED: return array(
return pht('Failed'); self::STATUS_BUILDING => pht('Building'),
default: self::STATUS_PASSED => pht('Passed'),
return pht('Unknown'); self::STATUS_FAILED => pht('Failed'),
} );
} }
public static function getBuildableStatusIcon($status) { public static function getBuildableStatusIcon($status) {
@ -70,6 +68,10 @@ final class HarbormasterBuildable extends HarbormasterDAO
return 'B'.$this->getID(); return 'B'.$this->getID();
} }
public function getURI() {
return '/'.$this->getMonogram();
}
/** /**
* Returns an existing buildable for the object's PHID or creates a * Returns an existing buildable for the object's PHID or creates a
* new buildable implicitly if needed. * new buildable implicitly if needed.
@ -237,24 +239,6 @@ final class HarbormasterBuildable extends HarbormasterDAO
return $this->assertAttached($this->containerObject); return $this->assertAttached($this->containerObject);
} }
public function attachContainerHandle($container_handle) {
$this->containerHandle = $container_handle;
return $this;
}
public function getContainerHandle() {
return $this->assertAttached($this->containerHandle);
}
public function attachBuildableHandle($buildable_handle) {
$this->buildableHandle = $buildable_handle;
return $this;
}
public function getBuildableHandle() {
return $this->assertAttached($this->buildableHandle);
}
public function attachBuilds(array $builds) { public function attachBuilds(array $builds) {
assert_instances_of($builds, 'HarbormasterBuild'); assert_instances_of($builds, 'HarbormasterBuild');
$this->builds = $builds; $this->builds = $builds;
@ -318,6 +302,10 @@ final class HarbormasterBuildable extends HarbormasterDAO
/* -( HarbormasterBuildableInterface )------------------------------------- */ /* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildableDisplayPHID() {
return $this->getBuildableObject()->getHarbormasterBuildableDisplayPHID();
}
public function getHarbormasterBuildablePHID() { public function getHarbormasterBuildablePHID() {
// NOTE: This is essentially just for convenience, as it allows you create // NOTE: This is essentially just for convenience, as it allows you create
// a copy of a buildable by specifying `B123` without bothering to go // a copy of a buildable by specifying `B123` without bothering to go

View file

@ -21,31 +21,6 @@ final class HarbormasterSchemaSpec extends PhabricatorConfigSchemaSpec {
), ),
)); ));
$this->buildRawSchema(
id(new HarbormasterBuildable())->getApplicationName(),
'harbormaster_buildlogchunk',
array(
'id' => 'auto',
'logID' => 'id',
'encoding' => 'text32',
// T6203/NULLABILITY
// Both the type and nullability of this column are crazily wrong.
'size' => 'uint32?',
'chunk' => 'bytes',
),
array(
'PRIMARY' => array(
'columns' => array('id'),
'unique' => true,
),
'key_log' => array(
'columns' => array('logID'),
),
));
} }
} }

View file

@ -234,23 +234,6 @@ final class HarbormasterBuild extends HarbormasterDAO
return ($this->getPlanAutoKey() !== null); return ($this->getPlanAutoKey() !== null);
} }
public function createLog(
HarbormasterBuildTarget $build_target,
$log_source,
$log_type) {
$log_source = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(250)
->truncateString($log_source);
$log = HarbormasterBuildLog::initializeNewBuildLog($build_target)
->setLogSource($log_source)
->setLogType($log_type)
->save();
return $log;
}
public function retrieveVariablesFromBuild() { public function retrieveVariablesFromBuild() {
$results = array( $results = array(
'buildable.diff' => null, 'buildable.diff' => null,
@ -323,6 +306,11 @@ final class HarbormasterBuild extends HarbormasterDAO
return ($this->getBuildStatus() == self::STATUS_PAUSED); return ($this->getBuildStatus() == self::STATUS_PAUSED);
} }
public function getURI() {
$id = $this->getID();
return "/harbormaster/build/{$id}/";
}
/* -( Build Commands )----------------------------------------------------- */ /* -( Build Commands )----------------------------------------------------- */

View file

@ -1,6 +1,7 @@
<?php <?php
final class HarbormasterBuildLog extends HarbormasterDAO final class HarbormasterBuildLog
extends HarbormasterDAO
implements PhabricatorPolicyInterface { implements PhabricatorPolicyInterface {
protected $buildTargetPHID; protected $buildTargetPHID;
@ -10,18 +11,18 @@ final class HarbormasterBuildLog extends HarbormasterDAO
protected $live; protected $live;
private $buildTarget = self::ATTACHABLE; private $buildTarget = self::ATTACHABLE;
private $start; private $rope;
private $isOpen;
const CHUNK_BYTE_LIMIT = 102400; const CHUNK_BYTE_LIMIT = 102400;
/** public function __construct() {
* The log is encoded as plain text. $this->rope = new PhutilRope();
*/ }
const ENCODING_TEXT = 'text';
public function __destruct() { public function __destruct() {
if ($this->start) { if ($this->isOpen) {
$this->finalize($this->start); $this->closeBuildLog();
} }
} }
@ -34,6 +35,37 @@ final class HarbormasterBuildLog extends HarbormasterDAO
->setLive(0); ->setLive(0);
} }
public function openBuildLog() {
if ($this->isOpen) {
throw new Exception(pht('This build log is already open!'));
}
$this->isOpen = true;
return $this
->setLive(1)
->save();
}
public function closeBuildLog() {
if (!$this->isOpen) {
throw new Exception(pht('This build log is not open!'));
}
if ($this->canCompressLog()) {
$this->compressLog();
}
$start = $this->getDateCreated();
$now = PhabricatorTime::getNow();
return $this
->setDuration($now - $start)
->setLive(0)
->save();
}
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
self::CONFIG_AUX_PHID => true, self::CONFIG_AUX_PHID => true,
@ -73,122 +105,174 @@ final class HarbormasterBuildLog extends HarbormasterDAO
return pht('Build Log'); return pht('Build Log');
} }
public function start() {
if ($this->getLive()) {
throw new Exception(
pht('Live logging has already started for this log.'));
}
$this->setLive(1);
$this->save();
$this->start = PhabricatorTime::getNow();
return time();
}
public function append($content) { public function append($content) {
if (!$this->getLive()) { if (!$this->getLive()) {
throw new Exception( throw new PhutilInvalidStateException('openBuildLog');
pht('Start logging before appending data to the log.'));
}
if (strlen($content) === 0) {
return;
} }
// If the length of the content is greater than the chunk size limit, $content = (string)$content;
// then we can never fit the content in a single record. We need to
// split our content out and call append on it for as many parts as there $this->rope->append($content);
// are to the content. $this->flush();
if (strlen($content) > self::CHUNK_BYTE_LIMIT) { }
$current = $content;
while (strlen($current) > self::CHUNK_BYTE_LIMIT) { private function flush() {
$part = substr($current, 0, self::CHUNK_BYTE_LIMIT);
$current = substr($current, self::CHUNK_BYTE_LIMIT); // TODO: Maybe don't flush more than a couple of times per second. If a
$this->append($part); // caller writes a single character over and over again, we'll currently
// spend a lot of time flushing that.
$chunk_table = id(new HarbormasterBuildLogChunk())->getTableName();
$chunk_limit = self::CHUNK_BYTE_LIMIT;
$encoding_text = HarbormasterBuildLogChunk::CHUNK_ENCODING_TEXT;
$rope = $this->rope;
while (true) {
$length = $rope->getByteLength();
if (!$length) {
break;
} }
$this->append($current);
return;
}
// Retrieve the size of last chunk from the DB for this log. If the $conn_w = $this->establishConnection('w');
// chunk is over 500K, then we need to create a new log entry. $last = $this->loadLastChunkInfo();
$conn = $this->establishConnection('w');
$result = queryfx_all(
$conn,
'SELECT id, size, encoding '.
'FROM harbormaster_buildlogchunk '.
'WHERE logID = %d '.
'ORDER BY id DESC '.
'LIMIT 1',
$this->getID());
if (count($result) === 0 ||
$result[0]['size'] + strlen($content) > self::CHUNK_BYTE_LIMIT ||
$result[0]['encoding'] !== self::ENCODING_TEXT) {
// We must insert a new chunk because the data we are appending $can_append =
// won't fit into the existing one, or we don't have any existing ($last) &&
// chunk data. ($last['encoding'] == $encoding_text) &&
queryfx( ($last['size'] < $chunk_limit);
$conn, if ($can_append) {
'INSERT INTO harbormaster_buildlogchunk '. $append_id = $last['id'];
'(logID, encoding, size, chunk) '. $prefix_size = $last['size'];
'VALUES '. } else {
'(%d, %s, %d, %B)', $append_id = null;
$this->getID(), $prefix_size = 0;
self::ENCODING_TEXT, }
strlen($content),
$content); $data_limit = ($chunk_limit - $prefix_size);
} else { $append_data = $rope->getPrefixBytes($data_limit);
// We have a resulting record that we can append our content onto. $data_size = strlen($append_data);
queryfx(
$conn, if ($append_id) {
'UPDATE harbormaster_buildlogchunk '. queryfx(
'SET chunk = CONCAT(chunk, %B), size = LENGTH(CONCAT(chunk, %B))'. $conn_w,
'WHERE id = %d', 'UPDATE %T SET chunk = CONCAT(chunk, %B), size = %d WHERE id = %d',
$content, $chunk_table,
$content, $append_data,
$result[0]['id']); $prefix_size + $data_size,
$append_id);
} else {
$this->writeChunk($encoding_text, $data_size, $append_data);
}
$rope->removeBytesFromHead($data_size);
} }
} }
public function finalize($start = 0) { public function newChunkIterator() {
if (!$this->getLive()) { return id(new HarbormasterBuildLogChunkIterator($this))
// TODO: Clean up this API. ->setPageSize(32);
return; }
}
// TODO: Encode the log contents in a gzipped format. private function loadLastChunkInfo() {
$this->reload(); $chunk_table = new HarbormasterBuildLogChunk();
if ($start > 0) { $conn_w = $chunk_table->establishConnection('w');
$this->setDuration(time() - $start);
} return queryfx_one(
$this->setLive(0); $conn_w,
$this->save(); 'SELECT id, size, encoding FROM %T WHERE logID = %d
ORDER BY id DESC LIMIT 1',
$chunk_table->getTableName(),
$this->getID());
} }
public function getLogText() { public function getLogText() {
// TODO: This won't cope very well if we're pulling like a 700MB // TODO: Remove this method since it won't scale for big logs.
// log file out of the DB. We should probably implement some sort
// of optional limit parameter so that when we're rendering out only
// 25 lines in the UI, we don't wastefully read in the whole log.
// We have to read our content out of the database and stitch all of $all_chunks = $this->newChunkIterator();
// the log data back together.
$conn = $this->establishConnection('r');
$result = queryfx_all(
$conn,
'SELECT chunk '.
'FROM harbormaster_buildlogchunk '.
'WHERE logID = %d '.
'ORDER BY id ASC',
$this->getID());
$content = ''; $full_text = array();
foreach ($result as $row) { foreach ($all_chunks as $chunk) {
$content .= $row['chunk']; $full_text[] = $chunk->getChunkDisplayText();
} }
return $content;
return implode('', $full_text);
}
private function canCompressLog() {
return function_exists('gzdeflate');
}
public function compressLog() {
$this->processLog(HarbormasterBuildLogChunk::CHUNK_ENCODING_GZIP);
}
public function decompressLog() {
$this->processLog(HarbormasterBuildLogChunk::CHUNK_ENCODING_TEXT);
}
private function processLog($mode) {
$chunks = $this->newChunkIterator();
// NOTE: Because we're going to insert new chunks, we need to stop the
// iterator once it hits the final chunk which currently exists. Otherwise,
// it may start consuming chunks we just wrote and run forever.
$last = $this->loadLastChunkInfo();
if ($last) {
$chunks->setRange(null, $last['id']);
}
$byte_limit = self::CHUNK_BYTE_LIMIT;
$rope = new PhutilRope();
$this->openTransaction();
foreach ($chunks as $chunk) {
$rope->append($chunk->getChunkDisplayText());
$chunk->delete();
while ($rope->getByteLength() > $byte_limit) {
$this->writeEncodedChunk($rope, $byte_limit, $mode);
}
}
while ($rope->getByteLength()) {
$this->writeEncodedChunk($rope, $byte_limit, $mode);
}
$this->saveTransaction();
}
private function writeEncodedChunk(PhutilRope $rope, $length, $mode) {
$data = $rope->getPrefixBytes($length);
$size = strlen($data);
switch ($mode) {
case HarbormasterBuildLogChunk::CHUNK_ENCODING_TEXT:
// Do nothing.
break;
case HarbormasterBuildLogChunk::CHUNK_ENCODING_GZIP:
$data = gzdeflate($data);
if ($data === false) {
throw new Exception(pht('Failed to gzdeflate() log data!'));
}
break;
default:
throw new Exception(pht('Unknown chunk encoding "%s"!', $mode));
}
$this->writeChunk($mode, $size, $data);
$rope->removeBytesFromHead($size);
}
private function writeChunk($encoding, $raw_size, $data) {
return id(new HarbormasterBuildLogChunk())
->setLogID($this->getID())
->setEncoding($encoding)
->setSize($raw_size)
->setChunk($data)
->save();
} }

View file

@ -0,0 +1,61 @@
<?php
final class HarbormasterBuildLogChunk
extends HarbormasterDAO {
protected $logID;
protected $encoding;
protected $size;
protected $chunk;
const CHUNK_ENCODING_TEXT = 'text';
const CHUNK_ENCODING_GZIP = 'gzip';
protected function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_BINARY => array(
'chunk' => true,
),
self::CONFIG_COLUMN_SCHEMA => array(
'logID' => 'id',
'encoding' => 'text32',
// T6203/NULLABILITY
// Both the type and nullability of this column are crazily wrong.
'size' => 'uint32?',
'chunk' => 'bytes',
),
self::CONFIG_KEY_SCHEMA => array(
'key_log' => array(
'columns' => array('logID'),
),
),
) + parent::getConfiguration();
}
public function getChunkDisplayText() {
$data = $this->getChunk();
$encoding = $this->getEncoding();
switch ($encoding) {
case self::CHUNK_ENCODING_TEXT:
// Do nothing, data is already plaintext.
break;
case self::CHUNK_ENCODING_GZIP:
$data = gzinflate($data);
if ($data === false) {
throw new Exception(pht('Unable to inflate log chunk!'));
}
break;
default:
throw new Exception(
pht('Unknown log chunk encoding ("%s")!', $encoding));
}
return $data;
}
}

View file

@ -0,0 +1,49 @@
<?php
final class HarbormasterBuildLogChunkIterator
extends PhutilBufferedIterator {
private $log;
private $cursor;
private $min = 0;
private $max = PHP_INT_MAX;
public function __construct(HarbormasterBuildLog $log) {
$this->log = $log;
}
protected function didRewind() {
$this->cursor = $this->min;
}
public function key() {
return $this->current()->getID();
}
public function setRange($min, $max) {
$this->min = (int)$min;
$this->max = (int)$max;
return $this;
}
protected function loadPage() {
if ($this->cursor > $this->max) {
return array();
}
$results = id(new HarbormasterBuildLogChunk())->loadAllWhere(
'logID = %d AND id >= %d AND id <= %d ORDER BY id ASC LIMIT %d',
$this->log->getID(),
$this->cursor,
$this->max,
$this->getPageSize());
if ($results) {
$this->cursor = last($results)->getID() + 1;
}
return $results;
}
}

View file

@ -256,9 +256,8 @@ final class HarbormasterBuildTarget extends HarbormasterDAO
$log = HarbormasterBuildLog::initializeNewBuildLog($this) $log = HarbormasterBuildLog::initializeNewBuildLog($this)
->setLogSource($log_source) ->setLogSource($log_source)
->setLogType($log_type); ->setLogType($log_type)
->openBuildLog();
$log->start();
return $log; return $log;
} }

View file

@ -61,6 +61,11 @@ final class HarbormasterBuildUnitMessage
'description' => pht( 'description' => pht(
'Coverage information for this test.'), 'Coverage information for this test.'),
), ),
'details' => array(
'type' => 'optional string',
'description' => pht(
'Additional human-readable information about the failure.'),
),
); );
} }
@ -94,6 +99,11 @@ final class HarbormasterBuildUnitMessage
$obj->setProperty('coverage', $coverage); $obj->setProperty('coverage', $coverage);
} }
$details = idx($dict, 'details');
if ($details) {
$obj->setProperty('details', $details);
}
return $obj; return $obj;
} }
@ -135,19 +145,36 @@ final class HarbormasterBuildUnitMessage
return $this; return $this;
} }
public function getSortKey() { public function getUnitMessageDetails() {
// TODO: Maybe use more numeric values after T6861. return $this->getProperty('details', '');
$map = array( }
ArcanistUnitTestResult::RESULT_FAIL => 'A',
ArcanistUnitTestResult::RESULT_BROKEN => 'B',
ArcanistUnitTestResult::RESULT_UNSOUND => 'C',
ArcanistUnitTestResult::RESULT_PASS => 'Z',
);
$result = idx($map, $this->getResult(), 'N'); public function getUnitMessageDisplayName() {
$name = $this->getName();
$namespace = $this->getNamespace();
if (strlen($namespace)) {
$name = $namespace.'::'.$name;
}
$engine = $this->getEngine();
if (strlen($engine)) {
$name = $engine.' > '.$name;
}
if (!strlen($name)) {
return pht('Nameless Test (%d)', $this->getID());
}
return $name;
}
public function getSortKey() {
$status = $this->getResult();
$sort = HarbormasterUnitStatus::getUnitStatusSort($status);
$parts = array( $parts = array(
$result, $sort,
$this->getEngine(), $this->getEngine(),
$this->getNamespace(), $this->getNamespace(),
$this->getName(), $this->getName(),

View file

@ -7,7 +7,9 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
implements implements
PhabricatorApplicationTransactionInterface, PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface, PhabricatorPolicyInterface,
PhabricatorSubscribableInterface { PhabricatorSubscribableInterface,
PhabricatorNgramsInterface,
PhabricatorProjectInterface {
protected $name; protected $name;
protected $planStatus; protected $planStatus;
@ -198,4 +200,15 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
return $messages; return $messages;
} }
/* -( PhabricatorNgramInterface )------------------------------------------ */
public function newNgrams() {
return array(
id(new HarbormasterBuildPlanNameNgrams())
->setValue($this->getName()),
);
}
} }

View file

@ -0,0 +1,18 @@
<?php
final class HarbormasterBuildPlanNameNgrams
extends PhabricatorSearchNgrams {
public function getNgramKey() {
return 'buildplanname';
}
public function getColumnName() {
return 'name';
}
public function getApplicationName() {
return 'harbormaster';
}
}

View file

@ -5,6 +5,8 @@ final class HarbormasterUnitPropertyView extends AphrontView {
private $pathURIMap = array(); private $pathURIMap = array();
private $unitMessages = array(); private $unitMessages = array();
private $limit; private $limit;
private $fullResultsURI;
private $notice;
public function setPathURIMap(array $map) { public function setPathURIMap(array $map) {
$this->pathURIMap = $map; $this->pathURIMap = $map;
@ -22,18 +24,47 @@ final class HarbormasterUnitPropertyView extends AphrontView {
return $this; return $this;
} }
public function setFullResultsURI($full_results_uri) {
$this->fullResultsURI = $full_results_uri;
return $this;
}
public function setNotice($notice) {
$this->notice = $notice;
return $this;
}
public function render() { public function render() {
require_celerity_resource('harbormaster-css');
$messages = $this->unitMessages; $messages = $this->unitMessages;
$messages = msort($messages, 'getSortKey'); $messages = msort($messages, 'getSortKey');
$limit = $this->limit;
if ($this->limit) { if ($this->limit) {
$messages = array_slice($messages, 0, $this->limit); $display_messages = array_slice($messages, 0, $limit);
} else {
$display_messages = $messages;
} }
$rows = array(); $rows = array();
$any_duration = false; $any_duration = false;
foreach ($messages as $message) { foreach ($display_messages as $message) {
$result = $this->renderResult($message->getResult()); $status = $message->getResult();
$icon_icon = HarbormasterUnitStatus::getUnitStatusIcon($status);
$icon_color = HarbormasterUnitStatus::getUnitStatusColor($status);
$icon_label = HarbormasterUnitStatus::getUnitStatusLabel($status);
$result_icon = id(new PHUIIconView())
->setIcon("{$icon_icon} {$icon_color}")
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => $icon_label,
));
$duration = $message->getDuration(); $duration = $message->getDuration();
if ($duration !== null) { if ($duration !== null) {
@ -41,37 +72,78 @@ final class HarbormasterUnitPropertyView extends AphrontView {
$duration = pht('%s ms', new PhutilNumber((int)(1000 * $duration))); $duration = pht('%s ms', new PhutilNumber((int)(1000 * $duration)));
} }
$name = $message->getName(); $name = $message->getUnitMessageDisplayName();
$id = $message->getID();
$namespace = $message->getNamespace(); if ($id) {
if (strlen($namespace)) { $name = phutil_tag(
$name = $namespace.'::'.$name; 'a',
array(
'href' => "/harbormaster/unit/view/{$id}/",
),
$name);
} }
$engine = $message->getEngine(); $details = $message->getUnitMessageDetails();
if (strlen($engine)) { if (strlen($details)) {
$name = $engine.' > '.$name; $name = array(
$name,
$this->renderUnitTestDetails($details),
);
} }
$rows[] = array( $rows[] = array(
$result, $result_icon,
$duration, $duration,
$name, $name,
); );
} }
$full_uri = $this->fullResultsURI;
if ($full_uri && (count($messages) > $limit)) {
$counts = array();
$groups = mgroup($messages, 'getResult');
foreach ($groups as $status => $group) {
$counts[] = HarbormasterUnitStatus::getUnitStatusCountLabel(
$status,
count($group));
}
$link_text = pht(
'View Full Test Results (%s)',
implode(" \xC2\xB7 ", $counts));
$full_link = phutil_tag(
'a',
array(
'href' => $full_uri,
),
$link_text);
$link_icon = id(new PHUIIconView())
->setIcon('fa-ellipsis-h lightgreytext');
$rows[] = array($link_icon, null, $full_link);
}
$table = id(new AphrontTableView($rows)) $table = id(new AphrontTableView($rows))
->setHeaders( ->setHeaders(
array( array(
pht('Result'), null,
pht('Time'), pht('Time'),
pht('Test'), pht('Test'),
)) ))
->setColumnClasses( ->setColumnClasses(
array( array(
null, 'top center',
null, 'top right',
'pri wide', 'top wide',
))
->setColumnWidths(
array(
'32px',
'64px',
)) ))
->setColumnVisibility( ->setColumnVisibility(
array( array(
@ -79,22 +151,32 @@ final class HarbormasterUnitPropertyView extends AphrontView {
$any_duration, $any_duration,
)); ));
if ($this->notice) {
$table->setNotice($this->notice);
}
return $table; return $table;
} }
private function renderResult($result) { private function renderUnitTestDetails($full_details) {
$names = array( $details = id(new PhutilUTF8StringTruncator())
ArcanistUnitTestResult::RESULT_BROKEN => pht('Broken'), ->setMaximumBytes(2048)
ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'), ->truncateString($full_details);
ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'), $details = phutil_split_lines($details);
ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'),
ArcanistUnitTestResult::RESULT_PASS => pht('Passed'),
);
$result = idx($names, $result, $result);
// TODO: Add some color. $limit = 3;
if (count($details) > $limit) {
$details = array_slice($details, 0, $limit);
}
return $result; $details = implode('', $details);
return phutil_tag(
'div',
array(
'class' => 'PhabricatorMonospaced harbormaster-unit-details',
),
$details);
} }
} }

View file

@ -0,0 +1,104 @@
<?php
final class HarbormasterUnitSummaryView extends AphrontView {
private $buildable;
private $messages;
private $limit;
private $excuse;
private $showViewAll;
public function setBuildable(HarbormasterBuildable $buildable) {
$this->buildable = $buildable;
return $this;
}
public function setUnitMessages(array $messages) {
$this->messages = $messages;
return $this;
}
public function setLimit($limit) {
$this->limit = $limit;
return $this;
}
public function setExcuse($excuse) {
$this->excuse = $excuse;
return $this;
}
public function setShowViewAll($show_view_all) {
$this->showViewAll = $show_view_all;
return $this;
}
public function render() {
$messages = $this->messages;
$buildable = $this->buildable;
$id = $buildable->getID();
$full_uri = "/harbormaster/unit/{$id}/";
$messages = msort($messages, 'getSortKey');
$head_unit = head($messages);
if ($head_unit) {
$status = $head_unit->getResult();
$tag_text = HarbormasterUnitStatus::getUnitStatusLabel($status);
$tag_color = HarbormasterUnitStatus::getUnitStatusColor($status);
$tag_icon = HarbormasterUnitStatus::getUnitStatusIcon($status);
} else {
$tag_text = pht('No Unit Tests');
$tag_color = 'grey';
$tag_icon = 'fa-ban';
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Unit Tests'))
->setStatus($tag_icon, $tag_color, $tag_text);
if ($this->showViewAll) {
$view_all = id(new PHUIButtonView())
->setTag('a')
->setHref($full_uri)
->setIcon('fa-list-ul')
->setText('View All');
$header->addActionLink($view_all);
}
$box = id(new PHUIObjectBoxView())
->setHeader($header);
$table = id(new HarbormasterUnitPropertyView())
->setUnitMessages($messages);
if ($this->showViewAll) {
$table->setFullResultsURI($full_uri);
}
if ($this->limit) {
$table->setLimit($this->limit);
}
$excuse = $this->excuse;
if (strlen($excuse)) {
$excuse_icon = id(new PHUIIconView())
->setIcon('fa-commenting-o red');
$table->setNotice(
array(
$excuse_icon,
' ',
phutil_tag('strong', array(), pht('Excuse:')),
' ',
$excuse,
));
}
$box->setTable($table);
return $box;
}
}

View file

@ -90,13 +90,10 @@ final class HarbormasterTargetWorker extends HarbormasterWorker {
$target->setDateCompleted(PhabricatorTime::getNow()); $target->setDateCompleted(PhabricatorTime::getNow());
$target->save(); $target->save();
} catch (Exception $ex) { } catch (Exception $ex) {
phlog($ex);
try { try {
$log = $build->createLog($target, 'core', 'exception'); $log = $target->newLog('core', 'exception')
$start = $log->start(); ->append($ex)
$log->append((string)$ex); ->closeBuildLog();
$log->finalize($start);
} catch (Exception $log_ex) { } catch (Exception $log_ex) {
phlog($log_ex); phlog($log_ex);
} }

Some files were not shown because too many files have changed in this diff Show more