mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 08:12:40 +01:00
(stable) Promote 2016 Week 10
This commit is contained in:
commit
2c67d9c8ac
145 changed files with 3717 additions and 2425 deletions
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => 'ecdca229',
|
||||
'core.pkg.css' => 'dd1447be',
|
||||
'core.pkg.js' => '7d8faf57',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '2de124c9',
|
||||
|
@ -25,7 +25,7 @@ return array(
|
|||
'rsrc/css/aphront/notification.css' => '7f684b62',
|
||||
'rsrc/css/aphront/panel-view.css' => '8427b78d',
|
||||
'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/tooltip.css' => '1a07aea8',
|
||||
'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c',
|
||||
|
@ -52,7 +52,7 @@ return array(
|
|||
'rsrc/css/application/conpherence/update.css' => 'faf6be09',
|
||||
'rsrc/css/application/conpherence/widget-pane.css' => '775eaaba',
|
||||
'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/dashboard/dashboard.css' => 'eb458607',
|
||||
'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/files/global-drag-and-drop.css' => '5c1b47c2',
|
||||
'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.css' => '826075fa',
|
||||
'rsrc/css/application/herald/herald.css' => '46596280',
|
||||
'rsrc/css/application/maniphest/batch-editor.css' => 'b0f0b6d5',
|
||||
'rsrc/css/application/maniphest/report.css' => '9b9580b7',
|
||||
'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b',
|
||||
'rsrc/css/application/maniphest/task-summary.css' => '11cc5344',
|
||||
'rsrc/css/application/objectselector/object-selector.css' => '85ee8ce6',
|
||||
'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/phame/phame.css' => '737792d6',
|
||||
'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-transaction-detail.css' => '82100a43',
|
||||
'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-view.css' => '298b7c5b',
|
||||
'rsrc/css/application/project/project-view.css' => '9ce99f21',
|
||||
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
|
||||
'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-typeahead.css' => '667a48ae',
|
||||
'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/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/syntax.css' => '9fd11da8',
|
||||
'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-badge.css' => 'f25c3476',
|
||||
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
|
||||
'rsrc/css/phui/phui-box.css' => 'dd1294d3',
|
||||
'rsrc/css/phui/phui-button.css' => 'edf464e9',
|
||||
'rsrc/css/phui/phui-box.css' => 'c9e01148',
|
||||
'rsrc/css/phui/phui-button.css' => 'a64a8de6',
|
||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5',
|
||||
'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-form-view.css' => '4a1a0f5e',
|
||||
'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-icon-set-selector.css' => '1ab67aad',
|
||||
'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-view.css' => '6d7c3509',
|
||||
'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-pager.css' => 'bea33d23',
|
||||
'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-segment-bar-view.css' => '46342871',
|
||||
'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-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.css' => 'e6d89647',
|
||||
'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96',
|
||||
|
@ -523,7 +524,7 @@ return array(
|
|||
'aphront-list-filter-view-css' => '5d6f0526',
|
||||
'aphront-multi-column-view-css' => 'fd18389d',
|
||||
'aphront-panel-view-css' => '8427b78d',
|
||||
'aphront-table-view-css' => '6d01d468',
|
||||
'aphront-table-view-css' => '036b6cdc',
|
||||
'aphront-tokenizer-control-css' => '056da01b',
|
||||
'aphront-tooltip-css' => '1a07aea8',
|
||||
'aphront-typeahead-control-css' => 'd4f16145',
|
||||
|
@ -558,8 +559,8 @@ return array(
|
|||
'font-fontawesome' => 'c43323c5',
|
||||
'font-lato' => 'c7ccd872',
|
||||
'global-drag-and-drop-css' => '5c1b47c2',
|
||||
'harbormaster-css' => 'b0758ca5',
|
||||
'herald-css' => '826075fa',
|
||||
'harbormaster-css' => '834879db',
|
||||
'herald-css' => '46596280',
|
||||
'herald-rule-editor' => '746ca158',
|
||||
'herald-test-css' => 'a52e323e',
|
||||
'inline-comment-summary-css' => '51efda3a',
|
||||
|
@ -740,7 +741,7 @@ return array(
|
|||
'multirow-row-manager' => 'b5d57730',
|
||||
'owners-path-editor' => 'aa1733d0',
|
||||
'owners-path-editor-css' => '2f00933b',
|
||||
'paste-css' => 'a5157c48',
|
||||
'paste-css' => '1898e534',
|
||||
'path-typeahead' => 'f7fc67ec',
|
||||
'people-profile-css' => '2473d929',
|
||||
'phabricator-action-list-view-css' => 'c5eba19d',
|
||||
|
@ -748,8 +749,8 @@ return array(
|
|||
'phabricator-busy' => '59a7976a',
|
||||
'phabricator-chatlog-css' => 'd295b020',
|
||||
'phabricator-content-source-view-css' => '4b8b05d4',
|
||||
'phabricator-core-css' => '5b3563c8',
|
||||
'phabricator-countdown-css' => 'e7544472',
|
||||
'phabricator-core-css' => 'd0801452',
|
||||
'phabricator-countdown-css' => '96696f21',
|
||||
'phabricator-dashboard-css' => 'eb458607',
|
||||
'phabricator-drag-and-drop-file-upload' => '81f182b5',
|
||||
'phabricator-draggable-list' => '5a13c79f',
|
||||
|
@ -772,7 +773,7 @@ return array(
|
|||
'phabricator-search-results-css' => '7dea472c',
|
||||
'phabricator-shaped-request' => '7cbe244b',
|
||||
'phabricator-side-menu-view-css' => '3a3d9f41',
|
||||
'phabricator-slowvote-css' => 'da0afb1b',
|
||||
'phabricator-slowvote-css' => 'a94b7230',
|
||||
'phabricator-source-code-view-css' => 'cbeef983',
|
||||
'phabricator-standard-page-view' => 'e709f6d0',
|
||||
'phabricator-textareautils' => '5813016a',
|
||||
|
@ -802,8 +803,8 @@ return array(
|
|||
'phui-action-panel-css' => '91c7b835',
|
||||
'phui-badge-view-css' => 'f25c3476',
|
||||
'phui-big-info-view-css' => 'bd903741',
|
||||
'phui-box-css' => 'dd1294d3',
|
||||
'phui-button-css' => 'edf464e9',
|
||||
'phui-box-css' => 'c9e01148',
|
||||
'phui-button-css' => 'a64a8de6',
|
||||
'phui-calendar-css' => 'ccabe893',
|
||||
'phui-calendar-day-css' => 'd1cf6f93',
|
||||
'phui-calendar-list-css' => 'c1c7f338',
|
||||
|
@ -818,7 +819,8 @@ return array(
|
|||
'phui-fontkit-css' => '9cda225e',
|
||||
'phui-form-css' => 'aac1d51d',
|
||||
'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-view-css' => 'de1a2119',
|
||||
'phui-icon-set-selector-css' => '1ab67aad',
|
||||
|
@ -828,7 +830,7 @@ return array(
|
|||
'phui-info-view-css' => '6d7c3509',
|
||||
'phui-inline-comment-view-css' => '0fdb3667',
|
||||
'phui-list-view-css' => '9da2aa00',
|
||||
'phui-object-box-css' => '407eaf5a',
|
||||
'phui-object-box-css' => '91628842',
|
||||
'phui-object-item-list-view-css' => '18b2ce8e',
|
||||
'phui-pager-css' => 'bea33d23',
|
||||
'phui-pinboard-view-css' => '2495140e',
|
||||
|
@ -837,11 +839,11 @@ return array(
|
|||
'phui-remarkup-preview-css' => '1a8f2591',
|
||||
'phui-segment-bar-view-css' => '46342871',
|
||||
'phui-spacing-css' => '042804d6',
|
||||
'phui-status-list-view-css' => '888cedb8',
|
||||
'phui-status-list-view-css' => '37309046',
|
||||
'phui-tag-view-css' => '9d5d4400',
|
||||
'phui-theme-css' => '027ba77e',
|
||||
'phui-timeline-view-css' => '2efceff8',
|
||||
'phui-two-column-view-css' => 'a317616a',
|
||||
'phui-two-column-view-css' => 'd0ad8c10',
|
||||
'phui-workboard-color-css' => 'ac6fe6a7',
|
||||
'phui-workboard-view-css' => 'e6d89647',
|
||||
'phui-workcard-view-css' => '3646fb96',
|
||||
|
@ -855,9 +857,9 @@ return array(
|
|||
'policy-css' => '957ea14c',
|
||||
'policy-edit-css' => '815c66f7',
|
||||
'policy-transaction-detail-css' => '82100a43',
|
||||
'ponder-view-css' => '212495e0',
|
||||
'ponder-view-css' => 'fbd45f96',
|
||||
'project-card-view-css' => '9418c97d',
|
||||
'project-view-css' => '298b7c5b',
|
||||
'project-view-css' => '9ce99f21',
|
||||
'releeph-core' => '9b3c5733',
|
||||
'releeph-preview-branch' => 'b7a6f4a5',
|
||||
'releeph-request-differential-create-dialog' => '8d8b92cd',
|
||||
|
|
|
@ -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};
|
11
resources/sql/autopatches/20160227.harbormaster.2.plani.php
Normal file
11
resources/sql/autopatches/20160227.harbormaster.2.plani.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
$table = new HarbormasterBuildPlan();
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $plan) {
|
||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
||||
$plan->getPHID(),
|
||||
array(
|
||||
'force' => true,
|
||||
));
|
||||
}
|
7
resources/sql/autopatches/20160303.drydock.1.bluen.sql
Normal file
7
resources/sql/autopatches/20160303.drydock.1.bluen.sql
Normal 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};
|
11
resources/sql/autopatches/20160303.drydock.2.bluei.php
Normal file
11
resources/sql/autopatches/20160303.drydock.2.bluei.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
$table = new DrydockBlueprint();
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $blueprint) {
|
||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
||||
$blueprint->getPHID(),
|
||||
array(
|
||||
'force' => true,
|
||||
));
|
||||
}
|
16
resources/sql/autopatches/20160303.drydock.3.edge.sql
Normal file
16
resources/sql/autopatches/20160303.drydock.3.edge.sql
Normal 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};
|
|
@ -36,6 +36,7 @@ phutil_register_library_map(array(
|
|||
'AlmanacDevice' => 'applications/almanac/storage/AlmanacDevice.php',
|
||||
'AlmanacDeviceController' => 'applications/almanac/controller/AlmanacDeviceController.php',
|
||||
'AlmanacDeviceEditController' => 'applications/almanac/controller/AlmanacDeviceEditController.php',
|
||||
'AlmanacDeviceEditEngine' => 'applications/almanac/editor/AlmanacDeviceEditEngine.php',
|
||||
'AlmanacDeviceEditor' => 'applications/almanac/editor/AlmanacDeviceEditor.php',
|
||||
'AlmanacDeviceListController' => 'applications/almanac/controller/AlmanacDeviceListController.php',
|
||||
'AlmanacDeviceNameNgrams' => 'applications/almanac/storage/AlmanacDeviceNameNgrams.php',
|
||||
|
@ -106,6 +107,7 @@ phutil_register_library_map(array(
|
|||
'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php',
|
||||
'AlmanacServiceDatasource' => 'applications/almanac/typeahead/AlmanacServiceDatasource.php',
|
||||
'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php',
|
||||
'AlmanacServiceEditEngine' => 'applications/almanac/editor/AlmanacServiceEditEngine.php',
|
||||
'AlmanacServiceEditor' => 'applications/almanac/editor/AlmanacServiceEditor.php',
|
||||
'AlmanacServiceListController' => 'applications/almanac/controller/AlmanacServiceListController.php',
|
||||
'AlmanacServiceNameNgrams' => 'applications/almanac/storage/AlmanacServiceNameNgrams.php',
|
||||
|
@ -867,15 +869,16 @@ phutil_register_library_map(array(
|
|||
'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
|
||||
'DrydockBlueprintController' => 'applications/drydock/controller/DrydockBlueprintController.php',
|
||||
'DrydockBlueprintCoreCustomField' => 'applications/drydock/customfield/DrydockBlueprintCoreCustomField.php',
|
||||
'DrydockBlueprintCreateController' => 'applications/drydock/controller/DrydockBlueprintCreateController.php',
|
||||
'DrydockBlueprintCustomField' => 'applications/drydock/customfield/DrydockBlueprintCustomField.php',
|
||||
'DrydockBlueprintDatasource' => 'applications/drydock/typeahead/DrydockBlueprintDatasource.php',
|
||||
'DrydockBlueprintDisableController' => 'applications/drydock/controller/DrydockBlueprintDisableController.php',
|
||||
'DrydockBlueprintEditController' => 'applications/drydock/controller/DrydockBlueprintEditController.php',
|
||||
'DrydockBlueprintEditEngine' => 'applications/drydock/editor/DrydockBlueprintEditEngine.php',
|
||||
'DrydockBlueprintEditor' => 'applications/drydock/editor/DrydockBlueprintEditor.php',
|
||||
'DrydockBlueprintImplementation' => 'applications/drydock/blueprint/DrydockBlueprintImplementation.php',
|
||||
'DrydockBlueprintImplementationTestCase' => 'applications/drydock/blueprint/__tests__/DrydockBlueprintImplementationTestCase.php',
|
||||
'DrydockBlueprintListController' => 'applications/drydock/controller/DrydockBlueprintListController.php',
|
||||
'DrydockBlueprintNameNgrams' => 'applications/drydock/storage/DrydockBlueprintNameNgrams.php',
|
||||
'DrydockBlueprintPHIDType' => 'applications/drydock/phid/DrydockBlueprintPHIDType.php',
|
||||
'DrydockBlueprintQuery' => 'applications/drydock/query/DrydockBlueprintQuery.php',
|
||||
'DrydockBlueprintSearchEngine' => 'applications/drydock/query/DrydockBlueprintSearchEngine.php',
|
||||
|
@ -938,6 +941,7 @@ phutil_register_library_map(array(
|
|||
'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php',
|
||||
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
|
||||
'DrydockRepositoryOperation' => 'applications/drydock/storage/DrydockRepositoryOperation.php',
|
||||
'DrydockRepositoryOperationController' => 'applications/drydock/controller/DrydockRepositoryOperationController.php',
|
||||
'DrydockRepositoryOperationDismissController' => 'applications/drydock/controller/DrydockRepositoryOperationDismissController.php',
|
||||
'DrydockRepositoryOperationListController' => 'applications/drydock/controller/DrydockRepositoryOperationListController.php',
|
||||
'DrydockRepositoryOperationPHIDType' => 'applications/drydock/phid/DrydockRepositoryOperationPHIDType.php',
|
||||
|
@ -965,6 +969,7 @@ phutil_register_library_map(array(
|
|||
'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php',
|
||||
'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php',
|
||||
'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php',
|
||||
'DrydockSchemaSpec' => 'applications/drydock/storage/DrydockSchemaSpec.php',
|
||||
'DrydockSlotLock' => 'applications/drydock/storage/DrydockSlotLock.php',
|
||||
'DrydockSlotLockException' => 'applications/drydock/exception/DrydockSlotLockException.php',
|
||||
'DrydockSlotLockFailureLogType' => 'applications/drydock/logtype/DrydockSlotLockFailureLogType.php',
|
||||
|
@ -1044,6 +1049,8 @@ phutil_register_library_map(array(
|
|||
'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php',
|
||||
'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.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',
|
||||
'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php',
|
||||
'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php',
|
||||
|
@ -1053,7 +1060,9 @@ phutil_register_library_map(array(
|
|||
'HarbormasterBuildPlanDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php',
|
||||
'HarbormasterBuildPlanDefaultEditCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultEditCapability.php',
|
||||
'HarbormasterBuildPlanDefaultViewCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultViewCapability.php',
|
||||
'HarbormasterBuildPlanEditEngine' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php',
|
||||
'HarbormasterBuildPlanEditor' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditor.php',
|
||||
'HarbormasterBuildPlanNameNgrams' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanNameNgrams.php',
|
||||
'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php',
|
||||
'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php',
|
||||
'HarbormasterBuildPlanSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php',
|
||||
|
@ -1110,6 +1119,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php',
|
||||
'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php',
|
||||
'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php',
|
||||
'HarbormasterManagementArchiveLogsWorkflow' => 'applications/harbormaster/management/HarbormasterManagementArchiveLogsWorkflow.php',
|
||||
'HarbormasterManagementBuildWorkflow' => 'applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php',
|
||||
'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php',
|
||||
'HarbormasterManagementWorkflow' => 'applications/harbormaster/management/HarbormasterManagementWorkflow.php',
|
||||
|
@ -1143,8 +1153,11 @@ phutil_register_library_map(array(
|
|||
'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php',
|
||||
'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.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',
|
||||
'HarbormasterUnitStatus' => 'applications/harbormaster/constants/HarbormasterUnitStatus.php',
|
||||
'HarbormasterUnitSummaryView' => 'applications/harbormaster/view/HarbormasterUnitSummaryView.php',
|
||||
'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php',
|
||||
'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php',
|
||||
'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php',
|
||||
|
@ -1519,6 +1532,7 @@ phutil_register_library_map(array(
|
|||
'PHUIHandleListView' => 'applications/phid/view/PHUIHandleListView.php',
|
||||
'PHUIHandleTagListView' => 'applications/phid/view/PHUIHandleTagListView.php',
|
||||
'PHUIHandleView' => 'applications/phid/view/PHUIHandleView.php',
|
||||
'PHUIHeadThingView' => 'view/phui/PHUIHeadThingView.php',
|
||||
'PHUIHeaderView' => 'view/phui/PHUIHeaderView.php',
|
||||
'PHUIHovercardUIExample' => 'applications/uiexample/examples/PHUIHovercardUIExample.php',
|
||||
'PHUIHovercardView' => 'view/phui/PHUIHovercardView.php',
|
||||
|
@ -3277,6 +3291,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorStandardCustomFieldUsers' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php',
|
||||
'PhabricatorStandardPageView' => 'view/page/PhabricatorStandardPageView.php',
|
||||
'PhabricatorStandardSelectCustomFieldDatasource' => 'infrastructure/customfield/datasource/PhabricatorStandardSelectCustomFieldDatasource.php',
|
||||
'PhabricatorStaticEditField' => 'applications/transactions/editfield/PhabricatorStaticEditField.php',
|
||||
'PhabricatorStatusController' => 'applications/system/controller/PhabricatorStatusController.php',
|
||||
'PhabricatorStatusUIExample' => 'applications/uiexample/examples/PhabricatorStatusUIExample.php',
|
||||
'PhabricatorStorageFixtureScopeGuard' => 'infrastructure/testing/fixture/PhabricatorStorageFixtureScopeGuard.php',
|
||||
|
@ -4031,6 +4046,7 @@ phutil_register_library_map(array(
|
|||
),
|
||||
'AlmanacDeviceController' => 'AlmanacController',
|
||||
'AlmanacDeviceEditController' => 'AlmanacDeviceController',
|
||||
'AlmanacDeviceEditEngine' => 'PhabricatorEditEngine',
|
||||
'AlmanacDeviceEditor' => 'AlmanacEditor',
|
||||
'AlmanacDeviceListController' => 'AlmanacDeviceController',
|
||||
'AlmanacDeviceNameNgrams' => 'PhabricatorSearchNgrams',
|
||||
|
@ -4132,6 +4148,7 @@ phutil_register_library_map(array(
|
|||
'AlmanacServiceController' => 'AlmanacController',
|
||||
'AlmanacServiceDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'AlmanacServiceEditController' => 'AlmanacServiceController',
|
||||
'AlmanacServiceEditEngine' => 'PhabricatorEditEngine',
|
||||
'AlmanacServiceEditor' => 'AlmanacEditor',
|
||||
'AlmanacServiceListController' => 'AlmanacServiceController',
|
||||
'AlmanacServiceNameNgrams' => 'PhabricatorSearchNgrams',
|
||||
|
@ -4620,7 +4637,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'DifferentialTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'DifferentialTransactionView' => 'PhabricatorApplicationTransactionView',
|
||||
'DifferentialUnitField' => 'DifferentialHarbormasterField',
|
||||
'DifferentialUnitField' => 'DifferentialCustomField',
|
||||
'DifferentialUnitStatus' => 'Phobject',
|
||||
'DifferentialUnitTestResult' => 'Phobject',
|
||||
'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||
|
@ -4963,21 +4980,24 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorCustomFieldInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
),
|
||||
'DrydockBlueprintController' => 'DrydockController',
|
||||
'DrydockBlueprintCoreCustomField' => array(
|
||||
'DrydockBlueprintCustomField',
|
||||
'PhabricatorStandardCustomFieldInterface',
|
||||
),
|
||||
'DrydockBlueprintCreateController' => 'DrydockBlueprintController',
|
||||
'DrydockBlueprintCustomField' => 'PhabricatorCustomField',
|
||||
'DrydockBlueprintDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'DrydockBlueprintDisableController' => 'DrydockBlueprintController',
|
||||
'DrydockBlueprintEditController' => 'DrydockBlueprintController',
|
||||
'DrydockBlueprintEditEngine' => 'PhabricatorEditEngine',
|
||||
'DrydockBlueprintEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'DrydockBlueprintImplementation' => 'Phobject',
|
||||
'DrydockBlueprintImplementationTestCase' => 'PhabricatorTestCase',
|
||||
'DrydockBlueprintListController' => 'DrydockBlueprintController',
|
||||
'DrydockBlueprintNameNgrams' => 'PhabricatorSearchNgrams',
|
||||
'DrydockBlueprintPHIDType' => 'PhabricatorPHIDType',
|
||||
'DrydockBlueprintQuery' => 'DrydockQuery',
|
||||
'DrydockBlueprintSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
|
@ -5051,16 +5071,17 @@ phutil_register_library_map(array(
|
|||
'DrydockDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'DrydockRepositoryOperationDismissController' => 'DrydockController',
|
||||
'DrydockRepositoryOperationListController' => 'DrydockController',
|
||||
'DrydockRepositoryOperationController' => 'DrydockController',
|
||||
'DrydockRepositoryOperationDismissController' => 'DrydockRepositoryOperationController',
|
||||
'DrydockRepositoryOperationListController' => 'DrydockRepositoryOperationController',
|
||||
'DrydockRepositoryOperationPHIDType' => 'PhabricatorPHIDType',
|
||||
'DrydockRepositoryOperationQuery' => 'DrydockQuery',
|
||||
'DrydockRepositoryOperationSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'DrydockRepositoryOperationStatusController' => 'DrydockController',
|
||||
'DrydockRepositoryOperationStatusController' => 'DrydockRepositoryOperationController',
|
||||
'DrydockRepositoryOperationStatusView' => 'AphrontView',
|
||||
'DrydockRepositoryOperationType' => 'Phobject',
|
||||
'DrydockRepositoryOperationUpdateWorker' => 'DrydockWorker',
|
||||
'DrydockRepositoryOperationViewController' => 'DrydockController',
|
||||
'DrydockRepositoryOperationViewController' => 'DrydockRepositoryOperationController',
|
||||
'DrydockResource' => array(
|
||||
'DrydockDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
|
@ -5081,6 +5102,7 @@ phutil_register_library_map(array(
|
|||
'DrydockResourceViewController' => 'DrydockResourceController',
|
||||
'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface',
|
||||
'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
|
||||
'DrydockSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'DrydockSlotLock' => 'DrydockDAO',
|
||||
'DrydockSlotLockException' => 'Exception',
|
||||
'DrydockSlotLockFailureLogType' => 'DrydockLogType',
|
||||
|
@ -5185,6 +5207,8 @@ phutil_register_library_map(array(
|
|||
'HarbormasterDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'HarbormasterBuildLogChunk' => 'HarbormasterDAO',
|
||||
'HarbormasterBuildLogChunkIterator' => 'PhutilBufferedIterator',
|
||||
'HarbormasterBuildLogPHIDType' => 'PhabricatorPHIDType',
|
||||
'HarbormasterBuildLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'HarbormasterBuildMessage' => array(
|
||||
|
@ -5198,11 +5222,15 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
),
|
||||
'HarbormasterBuildPlanDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'HarbormasterBuildPlanDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
'HarbormasterBuildPlanDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'HarbormasterBuildPlanEditEngine' => 'PhabricatorEditEngine',
|
||||
'HarbormasterBuildPlanEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'HarbormasterBuildPlanNameNgrams' => 'PhabricatorSearchNgrams',
|
||||
'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType',
|
||||
'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'HarbormasterBuildPlanSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
|
@ -5273,6 +5301,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterLintMessagesController' => 'HarbormasterController',
|
||||
'HarbormasterLintPropertyView' => 'AphrontView',
|
||||
'HarbormasterManagementArchiveLogsWorkflow' => 'HarbormasterManagementWorkflow',
|
||||
'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow',
|
||||
'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow',
|
||||
'HarbormasterManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
|
@ -5283,7 +5312,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterPlanDisableController' => 'HarbormasterPlanController',
|
||||
'HarbormasterPlanEditController' => 'HarbormasterPlanController',
|
||||
'HarbormasterPlanListController' => 'HarbormasterPlanController',
|
||||
'HarbormasterPlanRunController' => 'HarbormasterController',
|
||||
'HarbormasterPlanRunController' => 'HarbormasterPlanController',
|
||||
'HarbormasterPlanViewController' => 'HarbormasterPlanController',
|
||||
'HarbormasterPrototypeBuildStepGroup' => 'HarbormasterBuildStepGroup',
|
||||
'HarbormasterPublishFragmentBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
|
@ -5296,18 +5325,21 @@ phutil_register_library_map(array(
|
|||
'HarbormasterScratchTable' => 'HarbormasterDAO',
|
||||
'HarbormasterSendMessageConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
|
||||
'HarbormasterSleepBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterStepAddController' => 'HarbormasterController',
|
||||
'HarbormasterStepDeleteController' => 'HarbormasterController',
|
||||
'HarbormasterStepEditController' => 'HarbormasterController',
|
||||
'HarbormasterStepViewController' => 'HarbormasterController',
|
||||
'HarbormasterStepAddController' => 'HarbormasterPlanController',
|
||||
'HarbormasterStepDeleteController' => 'HarbormasterPlanController',
|
||||
'HarbormasterStepEditController' => 'HarbormasterPlanController',
|
||||
'HarbormasterStepViewController' => 'HarbormasterPlanController',
|
||||
'HarbormasterTargetEngine' => 'Phobject',
|
||||
'HarbormasterTargetWorker' => 'HarbormasterWorker',
|
||||
'HarbormasterTestBuildStepGroup' => 'HarbormasterBuildStepGroup',
|
||||
'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterUIEventListener' => 'PhabricatorEventListener',
|
||||
'HarbormasterURIArtifact' => 'HarbormasterArtifact',
|
||||
'HarbormasterUnitMessagesController' => 'HarbormasterController',
|
||||
'HarbormasterUnitMessageListController' => 'HarbormasterController',
|
||||
'HarbormasterUnitMessageViewController' => 'HarbormasterController',
|
||||
'HarbormasterUnitPropertyView' => 'AphrontView',
|
||||
'HarbormasterUnitStatus' => 'Phobject',
|
||||
'HarbormasterUnitSummaryView' => 'AphrontView',
|
||||
'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterWorker' => 'PhabricatorWorker',
|
||||
|
@ -5746,6 +5778,7 @@ phutil_register_library_map(array(
|
|||
'PHUIHandleListView' => 'AphrontTagView',
|
||||
'PHUIHandleTagListView' => 'AphrontTagView',
|
||||
'PHUIHandleView' => 'AphrontView',
|
||||
'PHUIHeadThingView' => 'AphrontTagView',
|
||||
'PHUIHeaderView' => 'AphrontTagView',
|
||||
'PHUIHovercardUIExample' => 'PhabricatorUIExample',
|
||||
'PHUIHovercardView' => 'AphrontTagView',
|
||||
|
@ -6103,7 +6136,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBadgesMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'PhabricatorBadgesPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorBadgesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorBadgesRecipientsListView' => 'AphrontTagView',
|
||||
'PhabricatorBadgesRecipientsListView' => 'AphrontView',
|
||||
'PhabricatorBadgesRemoveRecipientsController' => 'PhabricatorBadgesController',
|
||||
'PhabricatorBadgesReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
'PhabricatorBadgesSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
|
@ -6368,7 +6401,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCountdownTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhabricatorCountdownTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
'PhabricatorCountdownTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorCountdownView' => 'AphrontTagView',
|
||||
'PhabricatorCountdownView' => 'AphrontView',
|
||||
'PhabricatorCountdownViewController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCredentialsUsedByObjectEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorCursorPagedPolicyAwareQuery' => 'PhabricatorPolicyAwareQuery',
|
||||
|
@ -7811,6 +7844,7 @@ phutil_register_library_map(array(
|
|||
'AphrontResponseProducerInterface',
|
||||
),
|
||||
'PhabricatorStandardSelectCustomFieldDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorStaticEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorStatusController' => 'PhabricatorController',
|
||||
'PhabricatorStatusUIExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorStorageFixtureScopeGuard' => 'Phobject',
|
||||
|
|
|
@ -35,22 +35,18 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
public function isPrototype() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/almanac/' => array(
|
||||
'' => 'AlmanacConsoleController',
|
||||
'(?P<objectType>service)/' => array(
|
||||
$this->getQueryRoutePattern() => 'AlmanacServiceListController',
|
||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacServiceEditController',
|
||||
$this->getEditRoutePattern('edit/') => 'AlmanacServiceEditController',
|
||||
'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController',
|
||||
),
|
||||
'(?P<objectType>device)/' => array(
|
||||
$this->getQueryRoutePattern() => 'AlmanacDeviceListController',
|
||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
|
||||
$this->getEditRoutePattern('edit/') => 'AlmanacDeviceEditController',
|
||||
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
|
||||
),
|
||||
'interface/' => array(
|
||||
|
|
|
@ -158,16 +158,16 @@ abstract class AlmanacController
|
|||
->setIcon('fa-plus');
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Properties'))
|
||||
->setHeader(pht('PROPERTIES'))
|
||||
->addActionLink($add_button);
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
}
|
||||
|
||||
protected function addClusterMessage(
|
||||
PHUIObjectBoxView $box,
|
||||
$positive,
|
||||
$negative) {
|
||||
|
||||
|
@ -194,14 +194,13 @@ abstract class AlmanacController
|
|||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-sitemap');
|
||||
|
||||
$error_view = id(new PHUIInfoView())
|
||||
return id(new PHUIInfoView())
|
||||
->setSeverity($severity)
|
||||
->setErrors(
|
||||
array(
|
||||
array($icon, ' ', $message, ' ', $doc_link),
|
||||
));
|
||||
|
||||
$box->setInfoView($error_view);
|
||||
}
|
||||
|
||||
protected function getPropertyDeleteURI($object) {
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
abstract class AlmanacDeviceController extends AlmanacController {
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->newApplicationMenu()
|
||||
->setSearchEngine(new AlmanacDeviceSearchEngine());
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
|
|
|
@ -4,160 +4,9 @@ final class AlmanacDeviceEditController
|
|||
extends AlmanacDeviceController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$list_uri = $this->getApplicationURI('device/');
|
||||
|
||||
$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,
|
||||
));
|
||||
return id(new AlmanacDeviceEditEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,45 +8,19 @@ final class AlmanacDeviceListController
|
|||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$controller = id(new PhabricatorApplicationSearchController())
|
||||
->setQueryKey($request->getURIData('queryKey'))
|
||||
->setSearchEngine(new AlmanacDeviceSearchEngine())
|
||||
->setNavigation($this->buildSideNavView());
|
||||
|
||||
return $this->delegateToController($controller);
|
||||
return id(new AlmanacDeviceSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$can_create = $this->hasApplicationCapability(
|
||||
AlmanacCreateDevicesCapability::CAPABILITY);
|
||||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Device'))
|
||||
->setHref($this->getApplicationURI('device/edit/'))
|
||||
->setIcon('fa-plus-square')
|
||||
->setDisabled(!$can_create)
|
||||
->setWorkflow(!$can_create));
|
||||
id(new AlmanacDeviceEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,22 +23,18 @@ final class AlmanacDeviceViewController
|
|||
|
||||
$title = pht('Device %s', $device->getName());
|
||||
|
||||
$property_list = $this->buildPropertyList($device);
|
||||
$action_list = $this->buildActionList($device);
|
||||
$property_list->setActionList($action_list);
|
||||
$properties = $this->buildPropertyList($device);
|
||||
$actions = $this->buildActionList($device);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setUser($viewer)
|
||||
->setHeader($device->getName())
|
||||
->setPolicyObject($device);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($property_list);
|
||||
->setPolicyObject($device)
|
||||
->setHeaderIcon('fa-server');
|
||||
|
||||
$issue = null;
|
||||
if ($device->isClusterDevice()) {
|
||||
$this->addClusterMessage(
|
||||
$box,
|
||||
$issue = $this->addClusterMessage(
|
||||
pht('This device is bound to a cluster service.'),
|
||||
pht(
|
||||
'This device is bound to a cluster service. You do not have '.
|
||||
|
@ -50,23 +46,32 @@ final class AlmanacDeviceViewController
|
|||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($device->getName());
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$device,
|
||||
new AlmanacDeviceTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild(
|
||||
array(
|
||||
$box,
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setMainColumn(array(
|
||||
$issue,
|
||||
$interfaces,
|
||||
$this->buildAlmanacPropertiesTable($device),
|
||||
$this->buildSSHKeysTable($device),
|
||||
$this->buildServicesTable($device),
|
||||
$timeline,
|
||||
))
|
||||
->setPropertyList($properties)
|
||||
->setActionList($actions);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild(
|
||||
array(
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -123,7 +128,7 @@ final class AlmanacDeviceViewController
|
|||
->setCanEdit($can_edit);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Device Interfaces'))
|
||||
->setHeader(pht('DEVICE INTERFACES'))
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
|
@ -135,6 +140,7 @@ final class AlmanacDeviceViewController
|
|||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
}
|
||||
|
||||
|
@ -172,7 +178,7 @@ final class AlmanacDeviceViewController
|
|||
$upload_uri = '/auth/sshkey/upload/?objectPHID='.$device_phid;
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('SSH Public Keys'))
|
||||
->setHeader(pht('SSH PUBLIC KEYS'))
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
|
@ -196,9 +202,8 @@ final class AlmanacDeviceViewController
|
|||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function buildServicesTable(AlmanacDevice $device) {
|
||||
|
@ -244,7 +249,8 @@ final class AlmanacDeviceViewController
|
|||
));
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Bound Services'))
|
||||
->setHeaderText(pht('BOUND SERVICES'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,34 +21,38 @@ final class AlmanacNamespaceViewController
|
|||
|
||||
$title = pht('Namespace %s', $namespace->getName());
|
||||
|
||||
$property_list = $this->buildPropertyList($namespace);
|
||||
$action_list = $this->buildActionList($namespace);
|
||||
$property_list->setActionList($action_list);
|
||||
$properties = $this->buildPropertyList($namespace);
|
||||
$actions = $this->buildActionList($namespace);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setUser($viewer)
|
||||
->setHeader($namespace->getName())
|
||||
->setPolicyObject($namespace);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($property_list);
|
||||
->setPolicyObject($namespace)
|
||||
->setHeaderIcon('fa-asterisk');
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($namespace->getName());
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$namespace,
|
||||
new AlmanacNamespaceTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setMainColumn(array(
|
||||
$timeline,
|
||||
))
|
||||
->setPropertyList($properties)
|
||||
->setActionList($actions);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild(
|
||||
array(
|
||||
$box,
|
||||
$timeline,
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -56,7 +60,10 @@ final class AlmanacNamespaceViewController
|
|||
$viewer = $this->getViewer();
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setUser($viewer);
|
||||
->setUser($viewer)
|
||||
->setObject($namespace);
|
||||
|
||||
$properties->invokeWillRenderEvent();
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
|
|
@ -21,34 +21,38 @@ final class AlmanacNetworkViewController
|
|||
|
||||
$title = pht('Network %s', $network->getName());
|
||||
|
||||
$property_list = $this->buildPropertyList($network);
|
||||
$action_list = $this->buildActionList($network);
|
||||
$property_list->setActionList($action_list);
|
||||
$properties = $this->buildPropertyList($network);
|
||||
$actions = $this->buildActionList($network);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setUser($viewer)
|
||||
->setHeader($network->getName())
|
||||
->setHeaderIcon('fa-globe')
|
||||
->setPolicyObject($network);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($property_list);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($network->getName());
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$network,
|
||||
new AlmanacNetworkTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setMainColumn(array(
|
||||
$timeline,
|
||||
))
|
||||
->setPropertyList($properties)
|
||||
->setActionList($actions);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild(
|
||||
array(
|
||||
$box,
|
||||
$timeline,
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -56,7 +60,10 @@ final class AlmanacNetworkViewController
|
|||
$viewer = $this->getViewer();
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setUser($viewer);
|
||||
->setUser($viewer)
|
||||
->setObject($network);
|
||||
|
||||
$properties->invokeWillRenderEvent();
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,11 @@ abstract class AlmanacServiceController extends AlmanacController {
|
|||
return $crumbs;
|
||||
}
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->newApplicationMenu()
|
||||
->setSearchEngine(new AlmanacServiceSearchEngine());
|
||||
}
|
||||
|
||||
protected function getPropertyDeleteURI($object) {
|
||||
$id = $object->getID();
|
||||
return "/almanac/service/delete/{$id}/";
|
||||
|
|
|
@ -4,175 +4,28 @@ final class AlmanacServiceEditController
|
|||
extends AlmanacServiceController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$list_uri = $this->getApplicationURI('service/');
|
||||
$engine = id(new AlmanacServiceEditEngine())
|
||||
->setController($this);
|
||||
|
||||
$id = $request->getURIData('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;
|
||||
|
||||
if (!$id) {
|
||||
$this->requireApplicationCapability(
|
||||
AlmanacCreateServicesCapability::CAPABILITY);
|
||||
|
||||
$list_uri = $this->getApplicationURI('service/');
|
||||
|
||||
$service_type = $request->getStr('serviceType');
|
||||
|
||||
try {
|
||||
$service = AlmanacService::initializeNewService($service_type);
|
||||
} catch (Exception $ex) {
|
||||
return $this->buildServiceTypeResponse($cancel_uri);
|
||||
$service_types = AlmanacServiceType::getAllServiceTypes();
|
||||
if (empty($service_types[$service_type])) {
|
||||
return $this->buildServiceTypeResponse($list_uri);
|
||||
}
|
||||
|
||||
if ($service->isClusterService()) {
|
||||
$this->requireApplicationCapability(
|
||||
AlmanacManageClusterServicesCapability::CAPABILITY);
|
||||
$engine
|
||||
->addContextParameter('serviceType', $service_type)
|
||||
->setServiceType($service_type);
|
||||
}
|
||||
|
||||
$is_new = true;
|
||||
|
||||
$title = pht('Create Service');
|
||||
$save_button = pht('Create Service');
|
||||
}
|
||||
|
||||
$v_name = $service->getName();
|
||||
$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,
|
||||
));
|
||||
return $engine->buildResponse();
|
||||
}
|
||||
|
||||
private function buildServiceTypeResponse($cancel_uri) {
|
||||
|
@ -194,7 +47,6 @@ final class AlmanacServiceEditController
|
|||
pht('You have permission to create cluster services.'),
|
||||
pht('You do not have permission to create new cluster services.'));
|
||||
|
||||
|
||||
$type_control = id(new AphrontFormRadioButtonControl())
|
||||
->setLabel(pht('Service Type'))
|
||||
->setName('serviceType')
|
||||
|
@ -239,14 +91,10 @@ final class AlmanacServiceEditController
|
|||
->setHeaderText($title)
|
||||
->setForm($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
));
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($box);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,45 +8,19 @@ final class AlmanacServiceListController
|
|||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$controller = id(new PhabricatorApplicationSearchController())
|
||||
->setQueryKey($request->getURIData('queryKey'))
|
||||
->setSearchEngine(new AlmanacServiceSearchEngine())
|
||||
->setNavigation($this->buildSideNavView());
|
||||
|
||||
return $this->delegateToController($controller);
|
||||
return id(new AlmanacServiceSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$can_create = $this->hasApplicationCapability(
|
||||
AlmanacCreateServicesCapability::CAPABILITY);
|
||||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Service'))
|
||||
->setHref($this->getApplicationURI('service/edit/'))
|
||||
->setIcon('fa-plus-square')
|
||||
->setDisabled(!$can_create)
|
||||
->setWorkflow(!$can_create));
|
||||
id(new AlmanacServiceEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,22 +23,19 @@ final class AlmanacServiceViewController
|
|||
|
||||
$title = pht('Service %s', $service->getName());
|
||||
|
||||
$property_list = $this->buildPropertyList($service);
|
||||
$action_list = $this->buildActionList($service);
|
||||
$property_list->setActionList($action_list);
|
||||
$properties = $this->buildPropertyList($service);
|
||||
$actions = $this->buildActionList($service);
|
||||
$details = $this->buildPropertySection($service);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setUser($viewer)
|
||||
->setHeader($service->getName())
|
||||
->setPolicyObject($service);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($property_list);
|
||||
->setPolicyObject($service)
|
||||
->setHeaderIcon('fa-plug');
|
||||
|
||||
$issue = null;
|
||||
if ($service->isClusterService()) {
|
||||
$this->addClusterMessage(
|
||||
$box,
|
||||
$issue = $this->addClusterMessage(
|
||||
pht('This is a cluster service.'),
|
||||
pht(
|
||||
'This service is a cluster service. You do not have permission to '.
|
||||
|
@ -49,36 +46,62 @@ final class AlmanacServiceViewController
|
|||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($service->getName());
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$service,
|
||||
new AlmanacServiceTransactionQuery());
|
||||
$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()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild(
|
||||
array(
|
||||
$box,
|
||||
$bindings,
|
||||
$this->buildAlmanacPropertiesTable($service),
|
||||
$timeline,
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
->setObject($service);
|
||||
->setUser($viewer);
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Service Type'),
|
||||
$service->getServiceImplementation()->getServiceTypeShortName());
|
||||
|
||||
return $properties;
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('DETAILS'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->appendChild($properties);
|
||||
}
|
||||
|
||||
private function buildActionList(AlmanacService $service) {
|
||||
|
@ -126,7 +149,7 @@ final class AlmanacServiceViewController
|
|||
->setHideServiceColumn(true);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Service Bindings'))
|
||||
->setHeader(pht('SERVICE BINDINGS'))
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
|
@ -138,6 +161,7 @@ final class AlmanacServiceViewController
|
|||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
}
|
||||
|
||||
|
|
85
src/applications/almanac/editor/AlmanacDeviceEditEngine.php
Normal file
85
src/applications/almanac/editor/AlmanacDeviceEditEngine.php
Normal 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()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
97
src/applications/almanac/editor/AlmanacServiceEditEngine.php
Normal file
97
src/applications/almanac/editor/AlmanacServiceEditEngine.php
Normal 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()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -152,4 +152,28 @@ final class AlmanacServiceEditor
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ final class PhabricatorBadgesEditRecipientsController
|
|||
}
|
||||
|
||||
$recipient_phids = $badge->getRecipientPHIDs();
|
||||
$view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$recipient_spec = array();
|
||||
|
@ -53,7 +54,7 @@ final class PhabricatorBadgesEditRecipientsController
|
|||
->applyTransactions($badge, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($request->getRequestURI());
|
||||
->setURI($view_uri);
|
||||
}
|
||||
|
||||
$recipient_phids = array_reverse($recipient_phids);
|
||||
|
@ -76,44 +77,26 @@ final class PhabricatorBadgesEditRecipientsController
|
|||
$title = pht('Add Recipient');
|
||||
if ($can_edit) {
|
||||
$header_name = pht('Edit Recipients');
|
||||
$view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
|
||||
|
||||
$form = new AphrontFormView();
|
||||
$form
|
||||
->setUser($viewer)
|
||||
->setFullWidth(true)
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setName('phids')
|
||||
->setLabel(pht('Add Recipients'))
|
||||
->setDatasource(new PhabricatorPeopleDatasource()))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->addCancelButton($view_uri)
|
||||
->setValue(pht('Add Recipients')));
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($title)
|
||||
->setForm($form);
|
||||
->setDatasource(new PhabricatorPeopleDatasource()));
|
||||
}
|
||||
|
||||
$recipient_list = id(new PhabricatorBadgesRecipientsListView())
|
||||
->setBadge($badge)
|
||||
->setHandles($handles)
|
||||
->setUser($viewer);
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($viewer)
|
||||
->setTitle(pht('Award Badges'))
|
||||
->appendForm($form)
|
||||
->addCancelButton($view_uri)
|
||||
->addSubmitButton(pht('Add Recipients'));
|
||||
|
||||
$badge_url = $this->getApplicationURI('view/'.$id.'/');
|
||||
|
||||
$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,
|
||||
));
|
||||
return $dialog;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,8 +28,7 @@ final class PhabricatorBadgesRemoveRecipientsController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$recipients_uri =
|
||||
$this->getApplicationURI('recipients/'.$badge->getID().'/');
|
||||
$view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$recipient_spec = array();
|
||||
|
@ -52,7 +51,7 @@ final class PhabricatorBadgesRemoveRecipientsController
|
|||
->applyTransactions($badge, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($recipients_uri);
|
||||
->setURI($view_uri);
|
||||
}
|
||||
|
||||
$handle = id(new PhabricatorHandleQuery())
|
||||
|
@ -68,7 +67,7 @@ final class PhabricatorBadgesRemoveRecipientsController
|
|||
'Really revoke the badge "%s" from %s?',
|
||||
phutil_tag('strong', array(), $badge->getName()),
|
||||
phutil_tag('strong', array(), $handle->getName())))
|
||||
->addCancelButton($recipients_uri)
|
||||
->addCancelButton($view_uri)
|
||||
->addSubmitButton(pht('Revoke Badge'));
|
||||
|
||||
return $dialog;
|
||||
|
|
|
@ -22,6 +22,7 @@ final class PhabricatorBadgesViewController
|
|||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($badge->getName());
|
||||
$crumbs->setBorder(true);
|
||||
$title = $badge->getName();
|
||||
|
||||
if ($badge->isArchived()) {
|
||||
|
@ -39,15 +40,12 @@ final class PhabricatorBadgesViewController
|
|||
->setHeader($badge->getName())
|
||||
->setUser($viewer)
|
||||
->setPolicyObject($badge)
|
||||
->setStatus($status_icon, $status_color, $status_name);
|
||||
->setStatus($status_icon, $status_color, $status_name)
|
||||
->setHeaderIcon('fa-trophy');
|
||||
|
||||
$properties = $this->buildPropertyListView($badge);
|
||||
$actions = $this->buildActionListView($badge);
|
||||
$properties->setActionList($actions);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($properties);
|
||||
$details = $this->buildDetailsView($badge);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$badge,
|
||||
|
@ -64,26 +62,47 @@ final class PhabricatorBadgesViewController
|
|||
|
||||
$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()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(array($badge->getPHID()))
|
||||
->appendChild(
|
||||
array(
|
||||
$box,
|
||||
$recipient_list,
|
||||
$timeline,
|
||||
$add_comment,
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
private function buildPropertyListView(PhabricatorBadgesBadge $badge) {
|
||||
private function buildPropertyListView(
|
||||
PhabricatorBadgesBadge $badge) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
->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());
|
||||
|
||||
$view->addProperty(
|
||||
|
@ -99,8 +118,6 @@ final class PhabricatorBadgesViewController
|
|||
pht('Flavor'),
|
||||
$badge->getFlavor());
|
||||
|
||||
$view->invokeWillRenderEvent();
|
||||
|
||||
$description = $badge->getDescription();
|
||||
if (strlen($description)) {
|
||||
$view->addSectionHeader(
|
||||
|
@ -160,9 +177,10 @@ final class PhabricatorBadgesViewController
|
|||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName('Manage Recipients')
|
||||
->setName('Add Recipients')
|
||||
->setIcon('fa-users')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true)
|
||||
->setHref($this->getApplicationURI("/recipients/{$id}/")));
|
||||
|
||||
return $view;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorBadgesRecipientsListView extends AphrontTagView {
|
||||
final class PhabricatorBadgesRecipientsListView extends AphrontView {
|
||||
|
||||
private $badge;
|
||||
private $handles;
|
||||
|
@ -15,7 +15,7 @@ final class PhabricatorBadgesRecipientsListView extends AphrontTagView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function getTagContent() {
|
||||
public function render() {
|
||||
|
||||
$viewer = $this->user;
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ final class PhabricatorCalendarEventViewController
|
|||
$title = 'E'.$event->getID();
|
||||
$page_title = $title.' '.$event->getName();
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($title, '/E'.$event->getID());
|
||||
$crumbs->addTextCrumb($title);
|
||||
$crumbs->setBorder(true);
|
||||
}
|
||||
|
||||
if (!$event->getIsGhostEvent()) {
|
||||
|
@ -63,12 +64,9 @@ final class PhabricatorCalendarEventViewController
|
|||
|
||||
$header = $this->buildHeaderView($event);
|
||||
$actions = $this->buildActionView($event);
|
||||
$properties = $this->buildPropertyView($event);
|
||||
|
||||
$properties->setActionList($actions);
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($properties);
|
||||
$properties = $this->buildPropertyListView($event);
|
||||
$details = $this->buildPropertySection($event);
|
||||
$description = $this->buildDescriptionView($event);
|
||||
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
$add_comment_header = $is_serious
|
||||
|
@ -90,26 +88,32 @@ final class PhabricatorCalendarEventViewController
|
|||
->setAction($comment_uri)
|
||||
->setSubmitButtonName(pht('Add Comment'));
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setMainColumn($timeline)
|
||||
->setPropertyList($properties)
|
||||
->addPropertySection(pht('DETAILS'), $details)
|
||||
->addPropertySection(pht('DESCRIPTION'), $description)
|
||||
->setActionList($actions);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($page_title)
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(array($event->getPHID()))
|
||||
->appendChild(
|
||||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
$timeline,
|
||||
$add_comment_form,
|
||||
),
|
||||
array(
|
||||
'title' => $page_title,
|
||||
'pageObjects' => array($event->getPHID()),
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
private function buildHeaderView(PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
private function buildHeaderView(
|
||||
PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $event->getID();
|
||||
|
||||
$is_cancelled = $event->getIsCancelled();
|
||||
$icon = $is_cancelled ? ('fa-times') : ('fa-calendar');
|
||||
$color = $is_cancelled ? ('grey') : ('green');
|
||||
$icon = $is_cancelled ? ('fa-ban') : ('fa-check');
|
||||
$color = $is_cancelled ? ('red') : ('bluegrey');
|
||||
$status = $is_cancelled ? pht('Cancelled') : pht('Active');
|
||||
|
||||
$invite_status = $event->getUserInviteStatus($viewer->getPHID());
|
||||
|
@ -120,7 +124,8 @@ final class PhabricatorCalendarEventViewController
|
|||
->setUser($viewer)
|
||||
->setHeader($event->getName())
|
||||
->setStatus($icon, $color, $status)
|
||||
->setPolicyObject($event);
|
||||
->setPolicyObject($event)
|
||||
->setHeaderIcon('fa-calendar');
|
||||
|
||||
if ($is_invite_pending) {
|
||||
$decline_button = id(new PHUIButtonView())
|
||||
|
@ -245,13 +250,26 @@ final class PhabricatorCalendarEventViewController
|
|||
return $actions;
|
||||
}
|
||||
|
||||
private function buildPropertyView(PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
private function buildPropertyListView(
|
||||
PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
->setObject($event);
|
||||
|
||||
$properties->invokeWillRenderEvent();
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
private function buildPropertySection(
|
||||
PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setUser($viewer);
|
||||
|
||||
if ($event->getIsAllDay()) {
|
||||
$date_start = phabricator_date($event->getDateFrom(), $viewer);
|
||||
$date_end = phabricator_date($event->getDateTo(), $viewer);
|
||||
|
@ -362,16 +380,23 @@ final class PhabricatorCalendarEventViewController
|
|||
id(new PhabricatorCalendarIconSet())
|
||||
->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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -192,10 +192,7 @@ final class CelerityDefaultPostprocessor
|
|||
'sh-disabledbackground' => '#f3f3f3',
|
||||
|
||||
// Background color for "most" themes.
|
||||
'page.background' => '#f1f1f4',
|
||||
|
||||
// Background color for "dark" themes.
|
||||
'page.background.dark' => '#ebecee',
|
||||
'page.background' => '#f8f8fb',
|
||||
|
||||
'menu.profile.text' => 'rgba(255,255,255,.8)',
|
||||
'menu.profile.text.selected' => 'rgba(255,255,255,1)',
|
||||
|
|
|
@ -21,15 +21,15 @@ final class PhabricatorCountdownViewController
|
|||
|
||||
$countdown_view = id(new PhabricatorCountdownView())
|
||||
->setUser($viewer)
|
||||
->setCountdown($countdown)
|
||||
->setHeadless(true);
|
||||
->setCountdown($countdown);
|
||||
|
||||
$id = $countdown->getID();
|
||||
$title = $countdown->getTitle();
|
||||
|
||||
$crumbs = $this
|
||||
->buildApplicationCrumbs()
|
||||
->addTextCrumb("C{$id}");
|
||||
->addTextCrumb("C{$id}")
|
||||
->setBorder(true);
|
||||
|
||||
$epoch = $countdown->getEpoch();
|
||||
if ($epoch >= PhabricatorTime::getNow()) {
|
||||
|
@ -46,22 +46,30 @@ final class PhabricatorCountdownViewController
|
|||
->setHeader($title)
|
||||
->setUser($viewer)
|
||||
->setPolicyObject($countdown)
|
||||
->setStatus($icon, $color, $status);
|
||||
->setStatus($icon, $color, $status)
|
||||
->setHeaderIcon('fa-rocket');
|
||||
|
||||
$actions = $this->buildActionListView($countdown);
|
||||
$properties = $this->buildPropertyListView($countdown, $actions);
|
||||
|
||||
$object_box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($properties);
|
||||
$properties = $this->buildPropertyListView($countdown);
|
||||
$subheader = $this->buildSubheaderView($countdown);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$countdown,
|
||||
new PhabricatorCountdownTransactionQuery());
|
||||
|
||||
$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()
|
||||
->setTitle($title)
|
||||
|
@ -72,10 +80,7 @@ final class PhabricatorCountdownViewController
|
|||
))
|
||||
->appendChild(
|
||||
array(
|
||||
$object_box,
|
||||
$countdown_view,
|
||||
$timeline,
|
||||
$add_comment,
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -114,32 +119,38 @@ final class PhabricatorCountdownViewController
|
|||
}
|
||||
|
||||
private function buildPropertyListView(
|
||||
PhabricatorCountdown $countdown,
|
||||
PhabricatorActionListView $actions) {
|
||||
|
||||
PhabricatorCountdown $countdown) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
->setObject($countdown)
|
||||
->setActionList($actions);
|
||||
|
||||
$view->addProperty(
|
||||
pht('Author'),
|
||||
$viewer->renderHandle($countdown->getAuthorPHID()));
|
||||
|
||||
->setObject($countdown);
|
||||
$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) {
|
||||
|
|
|
@ -1,35 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCountdownView extends AphrontTagView {
|
||||
final class PhabricatorCountdownView extends AphrontView {
|
||||
|
||||
private $countdown;
|
||||
private $headless;
|
||||
|
||||
|
||||
public function setHeadless($headless) {
|
||||
$this->headless = $headless;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCountdown(PhabricatorCountdown $countdown) {
|
||||
$this->countdown = $countdown;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
protected function getTagContent() {
|
||||
public function render() {
|
||||
$countdown = $this->countdown;
|
||||
|
||||
require_celerity_resource('phabricator-countdown-css');
|
||||
|
||||
$header = null;
|
||||
if (!$this->headless) {
|
||||
$header = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phabricator-timer-header',
|
||||
),
|
||||
array(
|
||||
$header_text = array(
|
||||
'C'.$countdown->getID(),
|
||||
' ',
|
||||
phutil_tag(
|
||||
|
@ -38,9 +22,10 @@ final class PhabricatorCountdownView extends AphrontTagView {
|
|||
'href' => '/countdown/'.$countdown->getID(),
|
||||
),
|
||||
$countdown->getTitle()),
|
||||
));
|
||||
}
|
||||
);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($header_text);
|
||||
|
||||
$ths = array(
|
||||
phutil_tag('th', array(), pht('Days')),
|
||||
|
@ -66,12 +51,23 @@ final class PhabricatorCountdownView extends AphrontTagView {
|
|||
),
|
||||
$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();
|
||||
$content = phutil_tag(
|
||||
'div',
|
||||
array('class' => 'phabricator-timer', 'id' => $container),
|
||||
array(
|
||||
$header,
|
||||
$description,
|
||||
phutil_tag('table', array('class' => 'phabricator-timer-table'), array(
|
||||
phutil_tag('tr', array(), $ths),
|
||||
phutil_tag('tr', array(), $dashes),
|
||||
|
@ -84,7 +80,11 @@ final class PhabricatorCountdownView extends AphrontTagView {
|
|||
'container' => $container,
|
||||
));
|
||||
|
||||
return $content;
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->addClass('phabricator-timer-view')
|
||||
->appendChild($content);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ final class PhabricatorDashboardPanelCoreCustomField
|
|||
public function readValueFromObject(PhabricatorCustomFieldInterface $object) {
|
||||
$key = $this->getProxy()->getRawStandardFieldKey();
|
||||
$this->setValueFromStorage($object->getProperty($key));
|
||||
$this->didSetValueFromStorage();
|
||||
}
|
||||
|
||||
public function applyApplicationTransactionInternalEffects(
|
||||
|
|
|
@ -92,4 +92,130 @@ abstract class DifferentialController extends PhabricatorController {
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -102,10 +102,8 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
}
|
||||
}
|
||||
|
||||
$props = id(new DifferentialDiffProperty())->loadAllWhere(
|
||||
'diffID = %d',
|
||||
$target_manual->getID());
|
||||
$props = mpull($props, 'getData', 'getName');
|
||||
$this->loadDiffProperties($diffs);
|
||||
$props = $target_manual->getDiffProperties();
|
||||
|
||||
$object_phids = array_merge(
|
||||
$revision->getReviewers(),
|
||||
|
@ -256,23 +254,17 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
array($diff_vs, $target->getID()));
|
||||
$detail_diffs = mpull($detail_diffs, null, 'getPHID');
|
||||
|
||||
$buildables = id(new HarbormasterBuildableQuery())
|
||||
->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));
|
||||
}
|
||||
$this->loadHarbormasterData($detail_diffs);
|
||||
|
||||
$diff_detail_box = $this->buildDiffDetailView(
|
||||
$detail_diffs,
|
||||
$revision,
|
||||
$field_list);
|
||||
|
||||
$unit_box = $this->buildUnitMessagesView(
|
||||
$target,
|
||||
$revision);
|
||||
|
||||
$comment_view = $this->buildTransactions(
|
||||
$revision,
|
||||
$diff_vs ? $diffs[$diff_vs] : $target,
|
||||
|
@ -469,6 +461,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
$operations_box,
|
||||
$revision_detail_box,
|
||||
$diff_detail_box,
|
||||
$unit_box,
|
||||
$page_pane,
|
||||
);
|
||||
|
||||
|
@ -972,18 +965,9 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
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();
|
||||
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());
|
||||
}
|
||||
foreach ($this->getDiffTabLabels($diffs) as $tab) {
|
||||
list($label, $diff) = $tab;
|
||||
|
||||
$property_lists[] = array(
|
||||
$label,
|
||||
|
@ -1083,4 +1067,44 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
->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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialUnitField
|
||||
extends DifferentialHarbormasterField {
|
||||
extends DifferentialCustomField {
|
||||
|
||||
public function getFieldKey() {
|
||||
return 'differential:unit';
|
||||
|
@ -31,51 +31,6 @@ final class DifferentialUnitField
|
|||
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() {
|
||||
$status = $this->getObject()->getActiveDiff()->getUnitStatus();
|
||||
|
||||
|
@ -94,9 +49,7 @@ final class DifferentialUnitField
|
|||
return $warnings;
|
||||
}
|
||||
|
||||
protected function renderHarbormasterStatus(
|
||||
DifferentialDiff $diff,
|
||||
array $messages) {
|
||||
public function renderDiffPropertyViewValue(DifferentialDiff $diff) {
|
||||
|
||||
$colors = array(
|
||||
DifferentialUnitStatus::UNIT_NONE => 'grey',
|
||||
|
@ -110,89 +63,15 @@ final class DifferentialUnitField
|
|||
|
||||
$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())
|
||||
->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color)
|
||||
->setTarget($message)
|
||||
->setNote($note));
|
||||
->setTarget($message));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ final class DifferentialDiff
|
|||
private $properties = array();
|
||||
private $buildable = self::ATTACHABLE;
|
||||
|
||||
private $unitMessages = self::ATTACHABLE;
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
|
@ -334,6 +336,20 @@ final class DifferentialDiff
|
|||
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) {
|
||||
$this->buildable = $buildable;
|
||||
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 )----------------------------------------- */
|
||||
|
||||
|
||||
|
@ -429,6 +456,15 @@ final class DifferentialDiff
|
|||
/* -( HarbormasterBuildableInterface )------------------------------------- */
|
||||
|
||||
|
||||
public function getHarbormasterBuildableDisplayPHID() {
|
||||
$container_phid = $this->getHarbormasterContainerPHID();
|
||||
if ($container_phid) {
|
||||
return $container_phid;
|
||||
}
|
||||
|
||||
return $this->getHarbormasterBuildablePHID();
|
||||
}
|
||||
|
||||
public function getHarbormasterBuildablePHID() {
|
||||
return $this->getPHID();
|
||||
}
|
||||
|
|
|
@ -432,6 +432,10 @@ final class DifferentialRevision extends DifferentialDAO
|
|||
/* -( HarbormasterBuildableInterface )------------------------------------- */
|
||||
|
||||
|
||||
public function getHarbormasterBuildableDisplayPHID() {
|
||||
return $this->getHarbormasterContainerPHID();
|
||||
}
|
||||
|
||||
public function getHarbormasterBuildablePHID() {
|
||||
return $this->loadActiveDiff()->getPHID();
|
||||
}
|
||||
|
|
|
@ -30,10 +30,6 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
|
|||
return self::GROUP_UTILITIES;
|
||||
}
|
||||
|
||||
public function isPrototype() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
|
||||
return array(
|
||||
array(
|
||||
|
@ -60,8 +56,8 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
|
|||
'authorizations/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||
'DrydockAuthorizationListController',
|
||||
),
|
||||
'create/' => 'DrydockBlueprintCreateController',
|
||||
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController',
|
||||
$this->getEditRoutePattern('edit/')
|
||||
=> 'DrydockBlueprintEditController',
|
||||
),
|
||||
'(?P<type>resource)/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockResourceListController',
|
||||
|
|
|
@ -15,6 +15,10 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
|||
return pht('Almanac Hosts');
|
||||
}
|
||||
|
||||
public function getBlueprintIcon() {
|
||||
return 'fa-server';
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return pht(
|
||||
'Allows Drydock to lease existing hosts defined in an Almanac service '.
|
||||
|
|
|
@ -15,6 +15,10 @@ abstract class DrydockBlueprintImplementation extends Phobject {
|
|||
abstract public function getBlueprintName();
|
||||
abstract public function getDescription();
|
||||
|
||||
public function getBlueprintIcon() {
|
||||
return 'fa-map-o';
|
||||
}
|
||||
|
||||
public function getFieldSpecifications() {
|
||||
$fields = array();
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ final class DrydockWorkingCopyBlueprintImplementation
|
|||
return pht('Working Copy');
|
||||
}
|
||||
|
||||
public function getBlueprintIcon() {
|
||||
return 'fa-folder-open';
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return pht('Allows Drydock to check out working copies of repositories.');
|
||||
}
|
||||
|
|
|
@ -3,24 +3,18 @@
|
|||
abstract class DrydockBlueprintController
|
||||
extends DrydockController {
|
||||
|
||||
public function buildSideNavView() {
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
id(new DrydockBlueprintSearchEngine())
|
||||
->setViewer($this->getRequest()->getUser())
|
||||
->addNavigationItems($nav->getMenu());
|
||||
|
||||
$nav->selectFilter(null);
|
||||
|
||||
return $nav;
|
||||
public function buildApplicationMenu() {
|
||||
return $this->newApplicationMenu()
|
||||
->setSearchEngine(new DrydockBlueprintSearchEngine());
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Blueprints'),
|
||||
$this->getApplicationURI('blueprint/'));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -3,168 +3,95 @@
|
|||
final class DrydockBlueprintEditController extends DrydockBlueprintController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$engine = id(new DrydockBlueprintEditEngine())
|
||||
->setController($this);
|
||||
|
||||
$id = $request->getURIData('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 {
|
||||
if (!$id) {
|
||||
$this->requireApplicationCapability(
|
||||
DrydockCreateBlueprintsCapability::CAPABILITY);
|
||||
|
||||
$class = $request->getStr('class');
|
||||
$type = $request->getStr('blueprintType');
|
||||
|
||||
$impl = DrydockBlueprintImplementation::getNamedImplementation($class);
|
||||
$impl = DrydockBlueprintImplementation::getNamedImplementation($type);
|
||||
if (!$impl || !$impl->isEnabled()) {
|
||||
return new Aphront400Response();
|
||||
return $this->buildTypeSelectionResponse();
|
||||
}
|
||||
|
||||
$blueprint = DrydockBlueprint::initializeNewBlueprint($viewer)
|
||||
->setClassName($class)
|
||||
->attachImplementation($impl);
|
||||
|
||||
$cancel_uri = $this->getApplicationURI('blueprint/');
|
||||
$engine
|
||||
->addContextParameter('blueprintType', $type)
|
||||
->setBlueprintImplementation($impl);
|
||||
}
|
||||
|
||||
$field_list = PhabricatorCustomField::getObjectFields(
|
||||
$blueprint,
|
||||
PhabricatorCustomField::ROLE_EDIT);
|
||||
$field_list
|
||||
->setViewer($viewer)
|
||||
->readFieldsFromStorage($blueprint);
|
||||
return $engine->buildResponse();
|
||||
}
|
||||
|
||||
private function buildTypeSelectionResponse() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$implementations =
|
||||
DrydockBlueprintImplementation::getAllBlueprintImplementations();
|
||||
|
||||
$v_name = $blueprint->getBlueprintName();
|
||||
$e_name = true;
|
||||
$errors = array();
|
||||
$validation_exception = null;
|
||||
$e_blueprint = null;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$v_view_policy = $request->getStr('viewPolicy');
|
||||
$v_edit_policy = $request->getStr('editPolicy');
|
||||
$v_name = $request->getStr('name');
|
||||
if (!strlen($v_name)) {
|
||||
$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;
|
||||
$class = $request->getStr('blueprintType');
|
||||
if (!isset($implementations[$class])) {
|
||||
$e_blueprint = pht('Required');
|
||||
$errors[] = pht('You must choose a blueprint type.');
|
||||
}
|
||||
}
|
||||
|
||||
$control = id(new AphrontFormRadioButtonControl())
|
||||
->setName('blueprintType')
|
||||
->setLabel(pht('Blueprint Type'))
|
||||
->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);
|
||||
}
|
||||
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
->setObject($blueprint)
|
||||
->execute();
|
||||
$title = pht('Create New Blueprint');
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(pht('New Blueprint'));
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->addHiddenInput('class', $request->getStr('class'))
|
||||
->appendChild($control)
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Name'))
|
||||
->setName('name')
|
||||
->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));
|
||||
->addCancelButton($this->getApplicationURI('blueprint/'))
|
||||
->setValue(pht('Continue')));
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($header)
|
||||
->setValidationException($validation_exception)
|
||||
->setFormErrors($errors)
|
||||
->setHeaderText($title)
|
||||
->setForm($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
));
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($box);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,29 +7,18 @@ final class DrydockBlueprintListController extends DrydockBlueprintController {
|
|||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$querykey = $request->getURIData('queryKey');
|
||||
|
||||
$request = $this->getRequest();
|
||||
$controller = id(new PhabricatorApplicationSearchController())
|
||||
->setQueryKey($querykey)
|
||||
->setSearchEngine(new DrydockBlueprintSearchEngine())
|
||||
->setNavigation($this->buildSideNavView());
|
||||
|
||||
return $this->delegateToController($controller);
|
||||
return id(new DrydockBlueprintSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$can_create = $this->hasApplicationCapability(
|
||||
DrydockCreateBlueprintsCapability::CAPABILITY);
|
||||
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('New Blueprint'))
|
||||
->setHref($this->getApplicationURI('/blueprint/create/'))
|
||||
->setDisabled(!$can_create)
|
||||
->setWorkflow(!$can_create)
|
||||
->setIcon('fa-plus-square'));
|
||||
|
||||
id(new DrydockBlueprintEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($crumbs);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,8 +127,12 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController {
|
|||
private function buildPropertyListView(
|
||||
DrydockBlueprint $blueprint,
|
||||
PhabricatorActionListView $actions) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
->setObject($blueprint);
|
||||
|
||||
$view = new PHUIPropertyListView();
|
||||
$view->setActionList($actions);
|
||||
|
||||
$view->addProperty(
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
abstract class DrydockRepositoryOperationController
|
||||
extends DrydockController {
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->newApplicationMenu()
|
||||
->setSearchEngine(new DrydockRepositoryOperationSearchEngine());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class DrydockRepositoryOperationDismissController
|
||||
extends DrydockController {
|
||||
extends DrydockRepositoryOperationController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
|
|
@ -1,37 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class DrydockRepositoryOperationListController
|
||||
extends DrydockController {
|
||||
extends DrydockRepositoryOperationController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$query_key = $request->getURIData('queryKey');
|
||||
|
||||
$engine = new DrydockRepositoryOperationSearchEngine();
|
||||
|
||||
$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;
|
||||
return id(new DrydockRepositoryOperationSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class DrydockRepositoryOperationStatusController
|
||||
extends DrydockController {
|
||||
extends DrydockRepositoryOperationController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class DrydockRepositoryOperationViewController
|
||||
extends DrydockController {
|
||||
extends DrydockRepositoryOperationController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
|
@ -50,16 +50,14 @@ final class DrydockRepositoryOperationViewController
|
|||
->setUser($viewer)
|
||||
->setOperation($operation);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild(
|
||||
array(
|
||||
$crumbs,
|
||||
$object_box,
|
||||
$status_view,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
private function buildActionListView(DrydockRepositoryOperation $operation) {
|
||||
|
|
|
@ -28,6 +28,7 @@ final class DrydockBlueprintCoreCustomField
|
|||
public function readValueFromObject(PhabricatorCustomFieldInterface $object) {
|
||||
$key = $this->getProxy()->getRawStandardFieldKey();
|
||||
$this->setValueFromStorage($object->getDetail($key));
|
||||
$this->didSetValueFromStorage();
|
||||
}
|
||||
|
||||
public function applyApplicationTransactionInternalEffects(
|
||||
|
|
115
src/applications/drydock/editor/DrydockBlueprintEditEngine.php
Normal file
115
src/applications/drydock/editor/DrydockBlueprintEditEngine.php
Normal 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()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,10 @@ final class DrydockBlueprintEditor
|
|||
return pht('Drydock Blueprints');
|
||||
}
|
||||
|
||||
protected function supportsSearch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
|
@ -80,4 +84,36 @@ final class DrydockBlueprintEditor
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,12 @@ final class DrydockBlueprintQuery extends DrydockQuery {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withNameNgrams($ngrams) {
|
||||
return $this->withNgramsConstraint(
|
||||
new DrydockBlueprintNameNgrams(),
|
||||
$ngrams);
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new DrydockBlueprint();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ final class DrydockBlueprintSearchEngine
|
|||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['match'] !== null) {
|
||||
$query->withNameNgrams($map['match']);
|
||||
}
|
||||
|
||||
if ($map['isDisabled'] !== null) {
|
||||
$query->withDisabled($map['isDisabled']);
|
||||
}
|
||||
|
@ -27,6 +31,10 @@ final class DrydockBlueprintSearchEngine
|
|||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchTextField())
|
||||
->setLabel(pht('Name Contains'))
|
||||
->setKey('match')
|
||||
->setDescription(pht('Search for blueprints by name substring.')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Disabled'))
|
||||
->setKey('isDisabled')
|
||||
|
@ -69,15 +77,29 @@ final class DrydockBlueprintSearchEngine
|
|||
assert_instances_of($blueprints, 'DrydockBlueprint');
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
if ($blueprints) {
|
||||
$edge_query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(mpull($blueprints, 'getPHID'))
|
||||
->withEdgeTypes(
|
||||
array(
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||
));
|
||||
|
||||
$edge_query->execute();
|
||||
}
|
||||
|
||||
$view = new PHUIObjectItemListView();
|
||||
|
||||
foreach ($blueprints as $blueprint) {
|
||||
$impl = $blueprint->getImplementation();
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setHeader($blueprint->getBlueprintName())
|
||||
->setHref($this->getApplicationURI('/blueprint/'.$blueprint->getID()))
|
||||
->setHref($blueprint->getURI())
|
||||
->setObjectName(pht('Blueprint %d', $blueprint->getID()));
|
||||
|
||||
if (!$blueprint->getImplementation()->isEnabled()) {
|
||||
if (!$impl->isEnabled()) {
|
||||
$item->setDisabled(true);
|
||||
$item->addIcon('fa-chain-broken grey', pht('Implementation'));
|
||||
}
|
||||
|
@ -87,7 +109,24 @@ final class DrydockBlueprintSearchEngine
|
|||
$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);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,8 @@ final class DrydockRepositoryOperationSearchEngine
|
|||
$icon = DrydockRepositoryOperation::getOperationStateIcon($state);
|
||||
$name = DrydockRepositoryOperation::getOperationStateName($state);
|
||||
|
||||
$item->addIcon($icon, $name);
|
||||
$item->setStatusIcon($icon, $name);
|
||||
|
||||
$item->addByline(
|
||||
array(
|
||||
pht('Via:'),
|
||||
|
@ -78,13 +79,14 @@ final class DrydockRepositoryOperationSearchEngine
|
|||
$viewer->renderHandle($operation->getAuthorPHID()),
|
||||
));
|
||||
|
||||
$item->addAttribute(
|
||||
$viewer->renderHandle(
|
||||
$operation->getObjectPHID()));
|
||||
$object_phid = $operation->getObjectPHID();
|
||||
$repository_phid = $operation->getRepositoryPHID();
|
||||
|
||||
$item->addAttribute(
|
||||
$viewer->renderHandle(
|
||||
$operation->getRepositoryPHID()));
|
||||
$item->addAttribute($viewer->renderHandle($object_phid));
|
||||
|
||||
if ($repository_phid !== $object_phid) {
|
||||
$item->addAttribute($viewer->renderHandle($repository_phid));
|
||||
}
|
||||
|
||||
$view->addItem($item);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ final class DrydockBlueprint extends DrydockDAO
|
|||
implements
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorCustomFieldInterface {
|
||||
PhabricatorCustomFieldInterface,
|
||||
PhabricatorNgramsInterface,
|
||||
PhabricatorProjectInterface {
|
||||
|
||||
protected $className;
|
||||
protected $blueprintName;
|
||||
|
@ -118,6 +120,11 @@ final class DrydockBlueprint extends DrydockDAO
|
|||
return $log->save();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
$id = $this->getID();
|
||||
return "/drydock/blueprint/{$id}/";
|
||||
}
|
||||
|
||||
|
||||
/* -( Allocating Resources )----------------------------------------------- */
|
||||
|
||||
|
@ -343,4 +350,14 @@ final class DrydockBlueprint extends DrydockDAO
|
|||
}
|
||||
|
||||
|
||||
/* -( PhabricatorNgramInterface )------------------------------------------ */
|
||||
|
||||
|
||||
public function newNgrams() {
|
||||
return array(
|
||||
id(new DrydockBlueprintNameNgrams())
|
||||
->setValue($this->getBlueprintName()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
9
src/applications/drydock/storage/DrydockSchemaSpec.php
Normal file
9
src/applications/drydock/storage/DrydockSchemaSpec.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
final class DrydockSchemaSpec extends PhabricatorConfigSchemaSpec {
|
||||
|
||||
public function buildSchemata() {
|
||||
$this->buildEdgeSchemata(new DrydockBlueprint());
|
||||
}
|
||||
|
||||
}
|
|
@ -26,11 +26,20 @@ final class DrydockBlueprintDatasource
|
|||
->execute();
|
||||
|
||||
$results = array();
|
||||
foreach ($handles as $handle) {
|
||||
$results[] = id(new PhabricatorTypeaheadResult())
|
||||
->setName($handle->getName())
|
||||
foreach ($blueprints as $blueprint) {
|
||||
$handle = $handles[$blueprint->getPHID()];
|
||||
|
||||
$result = id(new PhabricatorTypeaheadResult())
|
||||
->setName($handle->getFullName())
|
||||
->setPHID($handle->getPHID());
|
||||
|
||||
if ($blueprint->getIsDisabled()) {
|
||||
$result->setClosed(pht('Disabled'));
|
||||
}
|
||||
|
||||
$results[] = $result;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ final class FundInitiativeViewController
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
@ -22,6 +21,7 @@ final class FundInitiativeViewController
|
|||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($initiative->getMonogram());
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$title = pht(
|
||||
'%s %s',
|
||||
|
@ -43,32 +43,32 @@ final class FundInitiativeViewController
|
|||
->setHeader($initiative->getName())
|
||||
->setUser($viewer)
|
||||
->setPolicyObject($initiative)
|
||||
->setStatus($status_icon, $status_color, $status_name);
|
||||
->setStatus($status_icon, $status_color, $status_name)
|
||||
->setHeaderIcon('fa-heart');
|
||||
|
||||
$properties = $this->buildPropertyListView($initiative);
|
||||
$actions = $this->buildActionListView($initiative);
|
||||
$properties->setActionList($actions);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($properties);
|
||||
|
||||
$details = $this->buildPropertySectionView($initiative);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$initiative,
|
||||
new FundInitiativeTransactionQuery());
|
||||
$timeline
|
||||
->setShouldTerminate(true);
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setMainColumn($timeline)
|
||||
->setPropertyList($properties)
|
||||
->addPropertySection(pht('DETAILS'), $details)
|
||||
->setActionList($actions);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(array($initiative->getPHID()))
|
||||
->appendChild(
|
||||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
$timeline,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
'pageObjects' => array($initiative->getPHID()),
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,17 @@ final class FundInitiativeViewController
|
|||
->setUser($viewer)
|
||||
->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();
|
||||
$merchant_phid = $initiative->getMerchantPHID();
|
||||
|
||||
|
@ -94,8 +105,6 @@ final class FundInitiativeViewController
|
|||
pht('Total Funding'),
|
||||
$initiative->getTotalAsCurrency()->formatForDisplay());
|
||||
|
||||
$view->invokeWillRenderEvent();
|
||||
|
||||
$description = $initiative->getDescription();
|
||||
if (strlen($description)) {
|
||||
$description = new PHUIRemarkupView($viewer, $description);
|
||||
|
|
|
@ -76,16 +76,17 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication {
|
|||
=> 'HarbormasterBuildActionController',
|
||||
),
|
||||
'plan/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'HarbormasterPlanListController',
|
||||
'edit/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanEditController',
|
||||
$this->getQueryRoutePattern() => 'HarbormasterPlanListController',
|
||||
$this->getEditRoutePattern('edit/')
|
||||
=> 'HarbormasterPlanEditController',
|
||||
'order/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanOrderController',
|
||||
'disable/(?P<id>\d+)/' => 'HarbormasterPlanDisableController',
|
||||
'run/(?P<id>\d+)/' => 'HarbormasterPlanRunController',
|
||||
'(?P<id>\d+)/' => 'HarbormasterPlanViewController',
|
||||
),
|
||||
'unit/' => array(
|
||||
'(?P<id>\d+)/' => 'HarbormasterUnitMessagesController',
|
||||
'(?P<id>\d+)/' => 'HarbormasterUnitMessageListController',
|
||||
'view/(?P<id>\d+)/' => 'HarbormasterUnitMessageViewController',
|
||||
),
|
||||
'lint/' => array(
|
||||
'(?P<id>\d+)/' => 'HarbormasterLintMessagesController',
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -7,34 +7,21 @@ final class HarbormasterBuildableListController extends HarbormasterController {
|
|||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$controller = id(new PhabricatorApplicationSearchController())
|
||||
->setQueryKey($request->getURIData('queryKey'))
|
||||
->setSearchEngine(new HarbormasterBuildableSearchEngine())
|
||||
->setNavigation($this->buildSideNavView());
|
||||
$items = array();
|
||||
|
||||
return $this->delegateToController($controller);
|
||||
}
|
||||
$items[] = id(new PHUIListItemView())
|
||||
->setType(PHUIListItemView::TYPE_LABEL)
|
||||
->setName(pht('Build Plans'));
|
||||
|
||||
public function buildSideNavView($for_app = false) {
|
||||
$user = $this->getRequest()->getUser();
|
||||
$items[] = id(new PHUIListItemView())
|
||||
->setType(PHUIListItemView::TYPE_LINK)
|
||||
->setName(pht('Manage Build Plans'))
|
||||
->setHref($this->getApplicationURI('plan/'));
|
||||
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
id(new HarbormasterBuildableSearchEngine())
|
||||
->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();
|
||||
return id(new HarbormasterBuildableSearchEngine())
|
||||
->setController($this)
|
||||
->setNavigationItems($items)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ final class HarbormasterBuildableViewController
|
|||
$buildable = id(new HarbormasterBuildableQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->needBuildableHandles(true)
|
||||
->needContainerHandles(true)
|
||||
->executeOne();
|
||||
if (!$buildable) {
|
||||
return new Aphront404Response();
|
||||
|
@ -167,15 +165,18 @@ final class HarbormasterBuildableViewController
|
|||
->setActionList($actions);
|
||||
$box->addPropertyList($properties);
|
||||
|
||||
if ($buildable->getContainerHandle() !== null) {
|
||||
$container_phid = $buildable->getContainerPHID();
|
||||
$buildable_phid = $buildable->getBuildablePHID();
|
||||
|
||||
if ($container_phid) {
|
||||
$properties->addProperty(
|
||||
pht('Container'),
|
||||
$buildable->getContainerHandle()->renderLink());
|
||||
$viewer->renderHandle($container_phid));
|
||||
}
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Buildable'),
|
||||
$buildable->getBuildableHandle()->renderLink());
|
||||
$viewer->renderHandle($buildable_phid));
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Origin'),
|
||||
|
@ -335,25 +336,11 @@ final class HarbormasterBuildableViewController
|
|||
}
|
||||
|
||||
if ($unit_data) {
|
||||
$unit_table = id(new HarbormasterUnitPropertyView())
|
||||
->setUser($viewer)
|
||||
->setLimit(25)
|
||||
->setUnitMessages($unit_data);
|
||||
|
||||
$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);
|
||||
$unit = id(new HarbormasterUnitSummaryView())
|
||||
->setBuildable($buildable)
|
||||
->setUnitMessages($unit_data)
|
||||
->setShowViewAll(true)
|
||||
->setLimit(5);
|
||||
} else {
|
||||
$unit = null;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
abstract class HarbormasterController extends PhabricatorController {
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->newApplicationMenu()
|
||||
->setSearchEngine(new HarbormasterBuildableSearchEngine());
|
||||
}
|
||||
|
||||
protected function addBuildableCrumb(
|
||||
PHUICrumbsView $crumbs,
|
||||
HarbormasterBuildable $buildable) {
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
abstract class HarbormasterPlanController extends HarbormasterController {
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->newApplicationMenu()
|
||||
->setSearchEngine(new HarbormasterBuildPlanSearchEngine());
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
|
|
|
@ -3,146 +3,9 @@
|
|||
final class HarbormasterPlanEditController extends HarbormasterPlanController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
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,
|
||||
));
|
||||
return id(new HarbormasterBuildPlanEditEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,53 +7,19 @@ final class HarbormasterPlanListController extends HarbormasterPlanController {
|
|||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$controller = id(new PhabricatorApplicationSearchController())
|
||||
->setQueryKey($request->getURIData('queryKey'))
|
||||
->setSearchEngine(new HarbormasterBuildPlanSearchEngine())
|
||||
->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();
|
||||
return id(new HarbormasterBuildPlanSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$can_create = $this->hasApplicationCapability(
|
||||
HarbormasterCreatePlansCapability::CAPABILITY);
|
||||
|
||||
$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'));
|
||||
id(new HarbormasterBuildPlanEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($crumbs);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterPlanRunController extends HarbormasterController {
|
||||
final class HarbormasterPlanRunController extends HarbormasterPlanController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
|
|
@ -187,8 +187,6 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
|||
if ($is_deadlocking) {
|
||||
$item->setStatusIcon('fa-warning red');
|
||||
}
|
||||
|
||||
$step_list->addItem($item);
|
||||
}
|
||||
|
||||
$step_list->setFlush(true);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterStepAddController extends HarbormasterController {
|
||||
final class HarbormasterStepAddController
|
||||
extends HarbormasterPlanController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterStepDeleteController extends HarbormasterController {
|
||||
final class HarbormasterStepDeleteController
|
||||
extends HarbormasterPlanController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterStepEditController extends HarbormasterController {
|
||||
final class HarbormasterStepEditController
|
||||
extends HarbormasterPlanController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterStepViewController extends HarbormasterController {
|
||||
final class HarbormasterStepViewController
|
||||
extends HarbormasterPlanController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterUnitMessagesController
|
||||
final class HarbormasterUnitMessageListController
|
||||
extends HarbormasterController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
|
@ -34,14 +34,10 @@ final class HarbormasterUnitMessagesController
|
|||
$unit_data = array();
|
||||
}
|
||||
|
||||
$unit_table = id(new HarbormasterUnitPropertyView())
|
||||
->setUser($viewer)
|
||||
$unit = id(new HarbormasterUnitSummaryView())
|
||||
->setBuildable($buildable)
|
||||
->setUnitMessages($unit_data);
|
||||
|
||||
$unit = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Unit Tests'))
|
||||
->setTable($unit_table);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$this->addBuildableCrumb($crumbs, $buildable);
|
||||
$crumbs->addTextCrumb(pht('Unit Tests'));
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,10 @@ final class HarbormasterBuildPlanEditor
|
|||
return pht('Harbormaster Build Plans');
|
||||
}
|
||||
|
||||
protected function supportsSearch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
$types[] = HarbormasterBuildPlanTransaction::TYPE_NAME;
|
||||
|
@ -90,7 +94,7 @@ final class HarbormasterBuildPlanEditor
|
|||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Required'),
|
||||
pht('Plan name is required.'),
|
||||
pht('You must choose a name for your build plan.'),
|
||||
last($xactions));
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
|
|
|
@ -2,11 +2,23 @@
|
|||
|
||||
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 getHarbormasterContainerPHID();
|
||||
|
||||
public function getBuildVariables();
|
||||
|
||||
public function getAvailableBuildVariables();
|
||||
|
||||
}
|
||||
|
|
|
@ -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!'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -21,8 +21,7 @@ final class HarbormasterBuildablePHIDType extends PhabricatorPHIDType {
|
|||
array $phids) {
|
||||
|
||||
return id(new HarbormasterBuildableQuery())
|
||||
->withPHIDs($phids)
|
||||
->needBuildableHandles(true);
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
|
@ -30,15 +29,30 @@ final class HarbormasterBuildablePHIDType extends PhabricatorPHIDType {
|
|||
array $handles,
|
||||
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) {
|
||||
$buildable = $objects[$phid];
|
||||
|
||||
$id = $buildable->getID();
|
||||
$target = $buildable->getBuildableHandle()->getFullName();
|
||||
$buildable_phid = $buildable->getBuildablePHID();
|
||||
|
||||
$handle->setURI("/B{$id}");
|
||||
$handle->setName("B{$id}");
|
||||
$handle->setFullName("B{$id}: ".$target);
|
||||
$target = $target_handles[$buildable_phid];
|
||||
$target_name = $target->getFullName();
|
||||
|
||||
$uri = $buildable->getURI();
|
||||
$monogram = $buildable->getMonogram();
|
||||
|
||||
$handle
|
||||
->setURI($uri)
|
||||
->setName($monogram)
|
||||
->setFullName("{$monogram}: {$target_name}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,12 @@ final class HarbormasterBuildPlanQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withNameNgrams($ngrams) {
|
||||
return $this->withNgramsConstraint(
|
||||
new HarbormasterBuildPlanNameNgrams(),
|
||||
$ngrams);
|
||||
}
|
||||
|
||||
public function needBuildSteps($need) {
|
||||
$this->needBuildSteps = $need;
|
||||
return $this;
|
||||
|
@ -74,41 +80,45 @@ final class HarbormasterBuildPlanQuery
|
|||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
'plan.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
'plan.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'planStatus IN (%Ls)',
|
||||
'plan.planStatus IN (%Ls)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
if (strlen($this->datasourceQuery)) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'name LIKE %>',
|
||||
'plan.name LIKE %>',
|
||||
$this->datasourceQuery);
|
||||
}
|
||||
|
||||
if ($this->planAutoKeys !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'planAutoKey IN (%Ls)',
|
||||
'plan.planAutoKey IN (%Ls)',
|
||||
$this->planAutoKeys);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
return 'plan';
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorHarbormasterApplication';
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ final class HarbormasterBuildPlanSearchEngine
|
|||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchTextField())
|
||||
->setLabel(pht('Name Contains'))
|
||||
->setKey('match')
|
||||
->setDescription(pht('Search for namespaces by name substring.')),
|
||||
id(new PhabricatorSearchCheckboxesField())
|
||||
->setLabel(pht('Status'))
|
||||
->setKey('status')
|
||||
|
@ -32,6 +36,10 @@ final class HarbormasterBuildPlanSearchEngine
|
|||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['match'] !== null) {
|
||||
$query->withNameNgrams($map['match']);
|
||||
}
|
||||
|
||||
if ($map['status']) {
|
||||
$query->withStatuses($map['status']);
|
||||
}
|
||||
|
@ -76,12 +84,23 @@ final class HarbormasterBuildPlanSearchEngine
|
|||
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
if ($plans) {
|
||||
$edge_query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(mpull($plans, 'getPHID'))
|
||||
->withEdgeTypes(
|
||||
array(
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||
));
|
||||
|
||||
$edge_query->execute();
|
||||
}
|
||||
|
||||
$list = new PHUIObjectItemListView();
|
||||
foreach ($plans as $plan) {
|
||||
$id = $plan->getID();
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setObjectName(pht('Plan %d', $plan->getID()))
|
||||
->setObjectName(pht('Plan %d', $id))
|
||||
->setHeader($plan->getName());
|
||||
|
||||
if ($plan->isDisabled()) {
|
||||
|
@ -94,6 +113,17 @@ final class HarbormasterBuildPlanSearchEngine
|
|||
|
||||
$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);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,10 @@ final class HarbormasterBuildableQuery
|
|||
private $phids;
|
||||
private $buildablePHIDs;
|
||||
private $containerPHIDs;
|
||||
private $statuses;
|
||||
private $manualBuildables;
|
||||
|
||||
private $needContainerObjects;
|
||||
private $needContainerHandles;
|
||||
private $needBuildableHandles;
|
||||
private $needBuilds;
|
||||
private $needTargets;
|
||||
|
||||
|
@ -45,13 +44,8 @@ final class HarbormasterBuildableQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function needContainerHandles($need) {
|
||||
$this->needContainerHandles = $need;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function needBuildableHandles($need) {
|
||||
$this->needBuildableHandles = $need;
|
||||
public function withStatuses(array $statuses) {
|
||||
$this->statuses = $statuses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -99,11 +93,8 @@ final class HarbormasterBuildableQuery
|
|||
}
|
||||
|
||||
protected function didFilterPage(array $page) {
|
||||
if ($this->needContainerObjects || $this->needContainerHandles) {
|
||||
$container_phids = array_filter(mpull($page, 'getContainerPHID'));
|
||||
|
||||
if ($this->needContainerObjects) {
|
||||
$containers = array();
|
||||
$container_phids = array_filter(mpull($page, 'getContainerPHID'));
|
||||
|
||||
if ($container_phids) {
|
||||
$containers = id(new PhabricatorObjectQuery())
|
||||
|
@ -112,6 +103,8 @@ final class HarbormasterBuildableQuery
|
|||
->setParentQuery($this)
|
||||
->execute();
|
||||
$containers = mpull($containers, null, 'getPHID');
|
||||
} else {
|
||||
$containers = array();
|
||||
}
|
||||
|
||||
foreach ($page as $key => $buildable) {
|
||||
|
@ -120,42 +113,6 @@ final class HarbormasterBuildableQuery
|
|||
}
|
||||
}
|
||||
|
||||
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())
|
||||
->withPHIDs($handle_phids)
|
||||
->setParentQuery($this)
|
||||
->execute();
|
||||
}
|
||||
|
||||
foreach ($page as $key => $buildable) {
|
||||
$handle_phid = $buildable->getBuildablePHID();
|
||||
$buildable->attachBuildableHandle(idx($handles, $handle_phid));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->needBuilds || $this->needTargets) {
|
||||
$builds = id(new HarbormasterBuildQuery())
|
||||
->setViewer($this->getViewer())
|
||||
|
@ -203,6 +160,13 @@ final class HarbormasterBuildableQuery
|
|||
$this->containerPHIDs);
|
||||
}
|
||||
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'buildableStatus in (%Ls)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
if ($this->manualBuildables !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
|
|
|
@ -11,150 +11,89 @@ final class HarbormasterBuildableSearchEngine
|
|||
return 'PhabricatorHarbormasterApplication';
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
public function newQuery() {
|
||||
return new HarbormasterBuildableQuery();
|
||||
}
|
||||
|
||||
$revisions = $this->readPHIDsFromRequest(
|
||||
$request,
|
||||
'revisions',
|
||||
array(
|
||||
DifferentialRevisionPHIDType::TYPECONST,
|
||||
));
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchStringListField())
|
||||
->setKey('objectPHIDs')
|
||||
->setAliases(array('objects'))
|
||||
->setLabel(pht('Objects'))
|
||||
->setPlaceholder(pht('rXabcdef, PHID-DIFF-1234, ...'))
|
||||
->setDescription(pht('Search for builds of particular objects.')),
|
||||
id(new PhabricatorSearchStringListField())
|
||||
->setKey('containerPHIDs')
|
||||
->setAliases(array('containers'))
|
||||
->setLabel(pht('Containers'))
|
||||
->setPlaceholder(pht('rXYZ, R123, D456, ...'))
|
||||
->setDescription(
|
||||
pht('Search for builds by containing revision or repository.')),
|
||||
id(new PhabricatorSearchCheckboxesField())
|
||||
->setKey('statuses')
|
||||
->setLabel(pht('Statuses'))
|
||||
->setOptions(HarbormasterBuildable::getBuildStatusMap())
|
||||
->setDescription(pht('Search for builds by buildable status.')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->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')),
|
||||
);
|
||||
}
|
||||
|
||||
$repositories = $this->readPHIDsFromRequest(
|
||||
$request,
|
||||
'repositories',
|
||||
array(
|
||||
PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
|
||||
));
|
||||
private function resolvePHIDs(array $names) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$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)
|
||||
$objects = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withNames($names)
|
||||
->execute();
|
||||
$diffs = mpull($diffs, 'getPHID', 'getPHID');
|
||||
|
||||
// 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('-');
|
||||
}
|
||||
|
||||
$buildable_phids = array_merge($commits, $diffs);
|
||||
$saved->setParameter('buildablePHIDs', $buildable_phids);
|
||||
|
||||
$saved->setParameter(
|
||||
'manual',
|
||||
$this->readBoolFromRequest($request, 'manual'));
|
||||
|
||||
return $saved;
|
||||
return mpull($objects, 'getPHID');
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$query = id(new HarbormasterBuildableQuery())
|
||||
->needContainerHandles(true)
|
||||
->needBuildableHandles(true);
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
$container_phids = $saved->getParameter('containerPHIDs', array());
|
||||
if ($container_phids) {
|
||||
$query->withContainerPHIDs($container_phids);
|
||||
if ($map['objectPHIDs']) {
|
||||
$phids = $this->resolvePHIDs($map['objectPHIDs']);
|
||||
if ($phids) {
|
||||
$query->withBuildablePHIDs($phids);
|
||||
}
|
||||
}
|
||||
|
||||
$buildable_phids = $saved->getParameter('buildablePHIDs', array());
|
||||
|
||||
if ($buildable_phids) {
|
||||
$query->withBuildablePHIDs($buildable_phids);
|
||||
if ($map['containerPHIDs']) {
|
||||
$phids = $this->resolvePHIDs($map['containerPHIDs']);
|
||||
if ($phids) {
|
||||
$query->withContainerPHIDs($phids);
|
||||
}
|
||||
}
|
||||
|
||||
$manual = $saved->getParameter('manual');
|
||||
if ($manual !== null) {
|
||||
$query->withManualBuildables($manual);
|
||||
if ($map['statuses']) {
|
||||
$query->withStatuses($map['statuses']);
|
||||
}
|
||||
|
||||
if ($map['manual'] !== null) {
|
||||
$query->withManualBuildables($map['manual']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved_query) {
|
||||
|
||||
$container_phids = $saved_query->getParameter('containerPHIDs', array());
|
||||
$buildable_phids = $saved_query->getParameter('buildablePHIDs', array());
|
||||
|
||||
$all_phids = array_merge($container_phids, $buildable_phids);
|
||||
|
||||
$revision_names = array();
|
||||
$diff_names = array();
|
||||
$repository_names = array();
|
||||
$commit_names = array();
|
||||
|
||||
if ($all_phids) {
|
||||
$objects = id(new PhabricatorObjectQuery())
|
||||
->setViewer($this->requireViewer())
|
||||
->withPHIDs($all_phids)
|
||||
->execute();
|
||||
|
||||
foreach ($all_phids as $phid) {
|
||||
$object = idx($objects, $phid);
|
||||
if (!$object) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Differential Revisions'))
|
||||
->setName('revisions')
|
||||
->setValue(implode(', ', $revision_names)))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Differential Diffs'))
|
||||
->setName('diffs')
|
||||
->setValue(implode(', ', $diff_names)))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Repositories'))
|
||||
->setName('repositories')
|
||||
->setValue(implode(', ', $repository_names)))
|
||||
->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) {
|
||||
return '/harbormaster/'.$path;
|
||||
}
|
||||
|
@ -185,35 +124,60 @@ final class HarbormasterBuildableSearchEngine
|
|||
|
||||
$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();
|
||||
foreach ($buildables as $buildable) {
|
||||
$id = $buildable->getID();
|
||||
|
||||
$display_phid = $buildable->getBuildableObject()
|
||||
->getHarbormasterBuildableDisplayPHID();
|
||||
|
||||
$container_phid = $buildable->getContainerPHID();
|
||||
$buildable_phid = $buildable->getBuildablePHID();
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setHeader(pht('Buildable %d', $buildable->getID()));
|
||||
if ($buildable->getContainerHandle() !== null) {
|
||||
$item->addAttribute($buildable->getContainerHandle()->getName());
|
||||
}
|
||||
if ($buildable->getBuildableHandle() !== null) {
|
||||
$item->addAttribute($buildable->getBuildableHandle()->getFullName());
|
||||
->setObjectName(pht('Buildable %d', $buildable->getID()));
|
||||
|
||||
if ($display_phid) {
|
||||
$handle = $handles[$display_phid];
|
||||
$item->setHeader($handle->getFullName());
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$item->setHref("/B{$id}");
|
||||
if ($container_phid && ($container_phid != $display_phid)) {
|
||||
$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()) {
|
||||
$item->addIcon('fa-wrench grey', pht('Manual'));
|
||||
}
|
||||
|
||||
$item->setStatusIcon('fa-wrench '.
|
||||
HarbormasterBuildable::getBuildableStatusColor(
|
||||
$buildable->getBuildableStatus()));
|
||||
$item->addByline(HarbormasterBuildable::getBuildableStatusName(
|
||||
$buildable->getBuildableStatus()));
|
||||
$status = $buildable->getBuildableStatus();
|
||||
|
||||
$status_icon = HarbormasterBuildable::getBuildableStatusIcon($status);
|
||||
$status_color = HarbormasterBuildable::getBuildableStatusColor($status);
|
||||
$status_label = HarbormasterBuildable::getBuildableStatusName($status);
|
||||
|
||||
$item->setStatusIcon("{$status_icon} {$status_color}", $status_label);
|
||||
|
||||
$list->addItem($item);
|
||||
|
||||
}
|
||||
|
||||
$result = new PhabricatorApplicationSearchResultView();
|
||||
|
|
|
@ -31,24 +31,7 @@ final class HarbormasterWaitForPreviousBuildStepImplementation
|
|||
// Block until all previous builds of the same build plan have
|
||||
// finished.
|
||||
$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);
|
||||
if ($blockers) {
|
||||
$log->start();
|
||||
$log->append(pht("Blocked by: %s\n", implode(',', $blockers)));
|
||||
$log->finalize();
|
||||
}
|
||||
|
||||
if ($blockers) {
|
||||
throw new PhabricatorWorkerYieldException(15);
|
||||
|
|
|
@ -13,8 +13,6 @@ final class HarbormasterBuildable extends HarbormasterDAO
|
|||
|
||||
private $buildableObject = self::ATTACHABLE;
|
||||
private $containerObject = self::ATTACHABLE;
|
||||
private $buildableHandle = self::ATTACHABLE;
|
||||
private $containerHandle = self::ATTACHABLE;
|
||||
private $builds = self::ATTACHABLE;
|
||||
|
||||
const STATUS_BUILDING = 'building';
|
||||
|
@ -22,16 +20,16 @@ final class HarbormasterBuildable extends HarbormasterDAO
|
|||
const STATUS_FAILED = 'failed';
|
||||
|
||||
public static function getBuildableStatusName($status) {
|
||||
switch ($status) {
|
||||
case self::STATUS_BUILDING:
|
||||
return pht('Building');
|
||||
case self::STATUS_PASSED:
|
||||
return pht('Passed');
|
||||
case self::STATUS_FAILED:
|
||||
return pht('Failed');
|
||||
default:
|
||||
return pht('Unknown');
|
||||
$map = self::getBuildStatusMap();
|
||||
return idx($map, $status, pht('Unknown ("%s")', $status));
|
||||
}
|
||||
|
||||
public static function getBuildStatusMap() {
|
||||
return array(
|
||||
self::STATUS_BUILDING => pht('Building'),
|
||||
self::STATUS_PASSED => pht('Passed'),
|
||||
self::STATUS_FAILED => pht('Failed'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function getBuildableStatusIcon($status) {
|
||||
|
@ -70,6 +68,10 @@ final class HarbormasterBuildable extends HarbormasterDAO
|
|||
return 'B'.$this->getID();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an existing buildable for the object's PHID or creates a
|
||||
* new buildable implicitly if needed.
|
||||
|
@ -237,24 +239,6 @@ final class HarbormasterBuildable extends HarbormasterDAO
|
|||
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) {
|
||||
assert_instances_of($builds, 'HarbormasterBuild');
|
||||
$this->builds = $builds;
|
||||
|
@ -318,6 +302,10 @@ final class HarbormasterBuildable extends HarbormasterDAO
|
|||
/* -( HarbormasterBuildableInterface )------------------------------------- */
|
||||
|
||||
|
||||
public function getHarbormasterBuildableDisplayPHID() {
|
||||
return $this->getBuildableObject()->getHarbormasterBuildableDisplayPHID();
|
||||
}
|
||||
|
||||
public function getHarbormasterBuildablePHID() {
|
||||
// NOTE: This is essentially just for convenience, as it allows you create
|
||||
// a copy of a buildable by specifying `B123` without bothering to go
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -234,23 +234,6 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
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() {
|
||||
$results = array(
|
||||
'buildable.diff' => null,
|
||||
|
@ -323,6 +306,11 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
return ($this->getBuildStatus() == self::STATUS_PAUSED);
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
$id = $this->getID();
|
||||
return "/harbormaster/build/{$id}/";
|
||||
}
|
||||
|
||||
|
||||
/* -( Build Commands )----------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildLog extends HarbormasterDAO
|
||||
final class HarbormasterBuildLog
|
||||
extends HarbormasterDAO
|
||||
implements PhabricatorPolicyInterface {
|
||||
|
||||
protected $buildTargetPHID;
|
||||
|
@ -10,18 +11,18 @@ final class HarbormasterBuildLog extends HarbormasterDAO
|
|||
protected $live;
|
||||
|
||||
private $buildTarget = self::ATTACHABLE;
|
||||
private $start;
|
||||
private $rope;
|
||||
private $isOpen;
|
||||
|
||||
const CHUNK_BYTE_LIMIT = 102400;
|
||||
|
||||
/**
|
||||
* The log is encoded as plain text.
|
||||
*/
|
||||
const ENCODING_TEXT = 'text';
|
||||
public function __construct() {
|
||||
$this->rope = new PhutilRope();
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
if ($this->start) {
|
||||
$this->finalize($this->start);
|
||||
if ($this->isOpen) {
|
||||
$this->closeBuildLog();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +35,37 @@ final class HarbormasterBuildLog extends HarbormasterDAO
|
|||
->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() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
|
@ -73,122 +105,174 @@ final class HarbormasterBuildLog extends HarbormasterDAO
|
|||
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) {
|
||||
if (!$this->getLive()) {
|
||||
throw new Exception(
|
||||
pht('Start logging before appending data to the log.'));
|
||||
}
|
||||
if (strlen($content) === 0) {
|
||||
return;
|
||||
throw new PhutilInvalidStateException('openBuildLog');
|
||||
}
|
||||
|
||||
// If the length of the content is greater than the chunk size limit,
|
||||
// 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
|
||||
// are to the content.
|
||||
if (strlen($content) > self::CHUNK_BYTE_LIMIT) {
|
||||
$current = $content;
|
||||
while (strlen($current) > self::CHUNK_BYTE_LIMIT) {
|
||||
$part = substr($current, 0, self::CHUNK_BYTE_LIMIT);
|
||||
$current = substr($current, self::CHUNK_BYTE_LIMIT);
|
||||
$this->append($part);
|
||||
}
|
||||
$this->append($current);
|
||||
return;
|
||||
$content = (string)$content;
|
||||
|
||||
$this->rope->append($content);
|
||||
$this->flush();
|
||||
}
|
||||
|
||||
// Retrieve the size of last chunk from the DB for this log. If the
|
||||
// chunk is over 500K, then we need to create a new log entry.
|
||||
$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) {
|
||||
private function flush() {
|
||||
|
||||
// We must insert a new chunk because the data we are appending
|
||||
// won't fit into the existing one, or we don't have any existing
|
||||
// chunk data.
|
||||
queryfx(
|
||||
$conn,
|
||||
'INSERT INTO harbormaster_buildlogchunk '.
|
||||
'(logID, encoding, size, chunk) '.
|
||||
'VALUES '.
|
||||
'(%d, %s, %d, %B)',
|
||||
$this->getID(),
|
||||
self::ENCODING_TEXT,
|
||||
strlen($content),
|
||||
$content);
|
||||
// TODO: Maybe don't flush more than a couple of times per second. If a
|
||||
// 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;
|
||||
}
|
||||
|
||||
$conn_w = $this->establishConnection('w');
|
||||
$last = $this->loadLastChunkInfo();
|
||||
|
||||
$can_append =
|
||||
($last) &&
|
||||
($last['encoding'] == $encoding_text) &&
|
||||
($last['size'] < $chunk_limit);
|
||||
if ($can_append) {
|
||||
$append_id = $last['id'];
|
||||
$prefix_size = $last['size'];
|
||||
} else {
|
||||
// We have a resulting record that we can append our content onto.
|
||||
$append_id = null;
|
||||
$prefix_size = 0;
|
||||
}
|
||||
|
||||
$data_limit = ($chunk_limit - $prefix_size);
|
||||
$append_data = $rope->getPrefixBytes($data_limit);
|
||||
$data_size = strlen($append_data);
|
||||
|
||||
if ($append_id) {
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE harbormaster_buildlogchunk '.
|
||||
'SET chunk = CONCAT(chunk, %B), size = LENGTH(CONCAT(chunk, %B))'.
|
||||
'WHERE id = %d',
|
||||
$content,
|
||||
$content,
|
||||
$result[0]['id']);
|
||||
$conn_w,
|
||||
'UPDATE %T SET chunk = CONCAT(chunk, %B), size = %d WHERE id = %d',
|
||||
$chunk_table,
|
||||
$append_data,
|
||||
$prefix_size + $data_size,
|
||||
$append_id);
|
||||
} else {
|
||||
$this->writeChunk($encoding_text, $data_size, $append_data);
|
||||
}
|
||||
|
||||
$rope->removeBytesFromHead($data_size);
|
||||
}
|
||||
}
|
||||
|
||||
public function finalize($start = 0) {
|
||||
if (!$this->getLive()) {
|
||||
// TODO: Clean up this API.
|
||||
return;
|
||||
public function newChunkIterator() {
|
||||
return id(new HarbormasterBuildLogChunkIterator($this))
|
||||
->setPageSize(32);
|
||||
}
|
||||
|
||||
// TODO: Encode the log contents in a gzipped format.
|
||||
$this->reload();
|
||||
if ($start > 0) {
|
||||
$this->setDuration(time() - $start);
|
||||
}
|
||||
$this->setLive(0);
|
||||
$this->save();
|
||||
private function loadLastChunkInfo() {
|
||||
$chunk_table = new HarbormasterBuildLogChunk();
|
||||
$conn_w = $chunk_table->establishConnection('w');
|
||||
|
||||
return queryfx_one(
|
||||
$conn_w,
|
||||
'SELECT id, size, encoding FROM %T WHERE logID = %d
|
||||
ORDER BY id DESC LIMIT 1',
|
||||
$chunk_table->getTableName(),
|
||||
$this->getID());
|
||||
}
|
||||
|
||||
public function getLogText() {
|
||||
// TODO: This won't cope very well if we're pulling like a 700MB
|
||||
// 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.
|
||||
// TODO: Remove this method since it won't scale for big logs.
|
||||
|
||||
// We have to read our content out of the database and stitch all of
|
||||
// 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());
|
||||
$all_chunks = $this->newChunkIterator();
|
||||
|
||||
$content = '';
|
||||
foreach ($result as $row) {
|
||||
$content .= $row['chunk'];
|
||||
$full_text = array();
|
||||
foreach ($all_chunks as $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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -256,9 +256,8 @@ final class HarbormasterBuildTarget extends HarbormasterDAO
|
|||
|
||||
$log = HarbormasterBuildLog::initializeNewBuildLog($this)
|
||||
->setLogSource($log_source)
|
||||
->setLogType($log_type);
|
||||
|
||||
$log->start();
|
||||
->setLogType($log_type)
|
||||
->openBuildLog();
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,11 @@ final class HarbormasterBuildUnitMessage
|
|||
'description' => pht(
|
||||
'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);
|
||||
}
|
||||
|
||||
$details = idx($dict, 'details');
|
||||
if ($details) {
|
||||
$obj->setProperty('details', $details);
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
|
@ -135,19 +145,36 @@ final class HarbormasterBuildUnitMessage
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getSortKey() {
|
||||
// TODO: Maybe use more numeric values after T6861.
|
||||
$map = array(
|
||||
ArcanistUnitTestResult::RESULT_FAIL => 'A',
|
||||
ArcanistUnitTestResult::RESULT_BROKEN => 'B',
|
||||
ArcanistUnitTestResult::RESULT_UNSOUND => 'C',
|
||||
ArcanistUnitTestResult::RESULT_PASS => 'Z',
|
||||
);
|
||||
public function getUnitMessageDetails() {
|
||||
return $this->getProperty('details', '');
|
||||
}
|
||||
|
||||
$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(
|
||||
$result,
|
||||
$sort,
|
||||
$this->getEngine(),
|
||||
$this->getNamespace(),
|
||||
$this->getName(),
|
||||
|
|
|
@ -7,7 +7,9 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
|||
implements
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorSubscribableInterface {
|
||||
PhabricatorSubscribableInterface,
|
||||
PhabricatorNgramsInterface,
|
||||
PhabricatorProjectInterface {
|
||||
|
||||
protected $name;
|
||||
protected $planStatus;
|
||||
|
@ -198,4 +200,15 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
|||
return $messages;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorNgramInterface )------------------------------------------ */
|
||||
|
||||
|
||||
public function newNgrams() {
|
||||
return array(
|
||||
id(new HarbormasterBuildPlanNameNgrams())
|
||||
->setValue($this->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,8 @@ final class HarbormasterUnitPropertyView extends AphrontView {
|
|||
private $pathURIMap = array();
|
||||
private $unitMessages = array();
|
||||
private $limit;
|
||||
private $fullResultsURI;
|
||||
private $notice;
|
||||
|
||||
public function setPathURIMap(array $map) {
|
||||
$this->pathURIMap = $map;
|
||||
|
@ -22,18 +24,47 @@ final class HarbormasterUnitPropertyView extends AphrontView {
|
|||
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() {
|
||||
require_celerity_resource('harbormaster-css');
|
||||
|
||||
$messages = $this->unitMessages;
|
||||
$messages = msort($messages, 'getSortKey');
|
||||
|
||||
$limit = $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();
|
||||
$any_duration = false;
|
||||
foreach ($messages as $message) {
|
||||
$result = $this->renderResult($message->getResult());
|
||||
foreach ($display_messages as $message) {
|
||||
$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();
|
||||
if ($duration !== null) {
|
||||
|
@ -41,37 +72,78 @@ final class HarbormasterUnitPropertyView extends AphrontView {
|
|||
$duration = pht('%s ms', new PhutilNumber((int)(1000 * $duration)));
|
||||
}
|
||||
|
||||
$name = $message->getName();
|
||||
$name = $message->getUnitMessageDisplayName();
|
||||
$id = $message->getID();
|
||||
|
||||
$namespace = $message->getNamespace();
|
||||
if (strlen($namespace)) {
|
||||
$name = $namespace.'::'.$name;
|
||||
if ($id) {
|
||||
$name = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => "/harbormaster/unit/view/{$id}/",
|
||||
),
|
||||
$name);
|
||||
}
|
||||
|
||||
$engine = $message->getEngine();
|
||||
if (strlen($engine)) {
|
||||
$name = $engine.' > '.$name;
|
||||
$details = $message->getUnitMessageDetails();
|
||||
if (strlen($details)) {
|
||||
$name = array(
|
||||
$name,
|
||||
$this->renderUnitTestDetails($details),
|
||||
);
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
$result,
|
||||
$result_icon,
|
||||
$duration,
|
||||
$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))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Result'),
|
||||
null,
|
||||
pht('Time'),
|
||||
pht('Test'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
null,
|
||||
null,
|
||||
'pri wide',
|
||||
'top center',
|
||||
'top right',
|
||||
'top wide',
|
||||
))
|
||||
->setColumnWidths(
|
||||
array(
|
||||
'32px',
|
||||
'64px',
|
||||
))
|
||||
->setColumnVisibility(
|
||||
array(
|
||||
|
@ -79,22 +151,32 @@ final class HarbormasterUnitPropertyView extends AphrontView {
|
|||
$any_duration,
|
||||
));
|
||||
|
||||
if ($this->notice) {
|
||||
$table->setNotice($this->notice);
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
private function renderResult($result) {
|
||||
$names = array(
|
||||
ArcanistUnitTestResult::RESULT_BROKEN => pht('Broken'),
|
||||
ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'),
|
||||
ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'),
|
||||
ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'),
|
||||
ArcanistUnitTestResult::RESULT_PASS => pht('Passed'),
|
||||
);
|
||||
$result = idx($names, $result, $result);
|
||||
private function renderUnitTestDetails($full_details) {
|
||||
$details = id(new PhutilUTF8StringTruncator())
|
||||
->setMaximumBytes(2048)
|
||||
->truncateString($full_details);
|
||||
$details = phutil_split_lines($details);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -90,13 +90,10 @@ final class HarbormasterTargetWorker extends HarbormasterWorker {
|
|||
$target->setDateCompleted(PhabricatorTime::getNow());
|
||||
$target->save();
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
|
||||
try {
|
||||
$log = $build->createLog($target, 'core', 'exception');
|
||||
$start = $log->start();
|
||||
$log->append((string)$ex);
|
||||
$log->finalize($start);
|
||||
$log = $target->newLog('core', 'exception')
|
||||
->append($ex)
|
||||
->closeBuildLog();
|
||||
} catch (Exception $log_ex) {
|
||||
phlog($log_ex);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue