mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-02 03:32:42 +01:00
(stable) Promote 2015 Week 34
This commit is contained in:
commit
f2b2264e8d
105 changed files with 3049 additions and 1729 deletions
|
@ -7,10 +7,10 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '33799ec4',
|
||||
'core.pkg.css' => 'aced76a5',
|
||||
'core.pkg.js' => 'a590b451',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '9451634c',
|
||||
'differential.pkg.css' => '2de124c9',
|
||||
'differential.pkg.js' => 'ebef29b1',
|
||||
'diffusion.pkg.css' => '385e85b3',
|
||||
'diffusion.pkg.js' => '0115b37c',
|
||||
|
@ -51,7 +51,7 @@ return array(
|
|||
'rsrc/css/application/conpherence/notification.css' => '6cdcc253',
|
||||
'rsrc/css/application/conpherence/transaction.css' => '85d0974c',
|
||||
'rsrc/css/application/conpherence/update.css' => 'faf6be09',
|
||||
'rsrc/css/application/conpherence/widget-pane.css' => '419fd50c',
|
||||
'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/daemon/bulk-job.css' => 'df9c1d4a',
|
||||
|
@ -60,7 +60,7 @@ return array(
|
|||
'rsrc/css/application/differential/add-comment.css' => 'c47f8c40',
|
||||
'rsrc/css/application/differential/changeset-view.css' => 'b6b0d1bb',
|
||||
'rsrc/css/application/differential/core.css' => '7ac3cabc',
|
||||
'rsrc/css/application/differential/phui-inline-comment.css' => '9fadd6b8',
|
||||
'rsrc/css/application/differential/phui-inline-comment.css' => '0fdb3667',
|
||||
'rsrc/css/application/differential/revision-comment.css' => '14b8565a',
|
||||
'rsrc/css/application/differential/revision-history.css' => '0e8eb855',
|
||||
'rsrc/css/application/differential/revision-list.css' => 'f3c47d33',
|
||||
|
@ -93,18 +93,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' => '6a399881',
|
||||
'rsrc/css/application/ponder/ponder-view.css' => 'bef48f86',
|
||||
'rsrc/css/application/projects/project-icon.css' => '4e3eaa5a',
|
||||
'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' => '7c27f0f9',
|
||||
'rsrc/css/application/slowvote/slowvote.css' => '475b4bd2',
|
||||
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
||||
'rsrc/css/application/uiexample/example.css' => '528b19de',
|
||||
'rsrc/css/core/core.css' => 'a76cefc9',
|
||||
'rsrc/css/core/remarkup.css' => 'de13dcbe',
|
||||
'rsrc/css/core/remarkup.css' => '73fc4395',
|
||||
'rsrc/css/core/syntax.css' => '9fd11da8',
|
||||
'rsrc/css/core/z-index.css' => '57ddcaa2',
|
||||
'rsrc/css/diviner/diviner-shared.css' => '5a337049',
|
||||
|
@ -148,7 +148,7 @@ return array(
|
|||
'rsrc/css/phui/phui-tag-view.css' => '402691cc',
|
||||
'rsrc/css/phui/phui-text.css' => 'cf019f54',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'f1bccf73',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => 'add0a7d1',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => '39ecafb1',
|
||||
'rsrc/css/phui/phui-workboard-view.css' => '6704d68d',
|
||||
'rsrc/css/phui/phui-workpanel-view.css' => 'adec7699',
|
||||
'rsrc/css/sprite-login.css' => '1ebb9bf9',
|
||||
|
@ -346,7 +346,7 @@ return array(
|
|||
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '01774ab2',
|
||||
'rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js' => 'cf86d16a',
|
||||
'rsrc/js/application/conpherence/behavior-durable-column.js' => 'c72aa091',
|
||||
'rsrc/js/application/conpherence/behavior-menu.js' => 'd3782c93',
|
||||
'rsrc/js/application/conpherence/behavior-menu.js' => '1d45c74d',
|
||||
'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861',
|
||||
'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3',
|
||||
'rsrc/js/application/conpherence/behavior-widget-pane.js' => 'a8458711',
|
||||
|
@ -514,7 +514,7 @@ return array(
|
|||
'conpherence-thread-manager' => '01774ab2',
|
||||
'conpherence-transaction-css' => '85d0974c',
|
||||
'conpherence-update-css' => 'faf6be09',
|
||||
'conpherence-widget-pane-css' => '419fd50c',
|
||||
'conpherence-widget-pane-css' => '775eaaba',
|
||||
'differential-changeset-view-css' => 'b6b0d1bb',
|
||||
'differential-core-view-css' => '7ac3cabc',
|
||||
'differential-inline-comment-editor' => 'd4c87bf4',
|
||||
|
@ -552,7 +552,7 @@ return array(
|
|||
'javelin-behavior-choose-control' => '6153c708',
|
||||
'javelin-behavior-config-reorder-fields' => 'b6993408',
|
||||
'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a',
|
||||
'javelin-behavior-conpherence-menu' => 'd3782c93',
|
||||
'javelin-behavior-conpherence-menu' => '1d45c74d',
|
||||
'javelin-behavior-conpherence-pontificate' => '21ba5861',
|
||||
'javelin-behavior-conpherence-widget-pane' => 'a8458711',
|
||||
'javelin-behavior-countdown-timer' => 'e4cc26b3',
|
||||
|
@ -737,11 +737,11 @@ return array(
|
|||
'phabricator-object-selector-css' => '85ee8ce6',
|
||||
'phabricator-phtize' => 'd254d646',
|
||||
'phabricator-prefab' => '6920d200',
|
||||
'phabricator-remarkup-css' => 'de13dcbe',
|
||||
'phabricator-remarkup-css' => '73fc4395',
|
||||
'phabricator-search-results-css' => '7dea472c',
|
||||
'phabricator-shaped-request' => '7cbe244b',
|
||||
'phabricator-side-menu-view-css' => 'bec2458e',
|
||||
'phabricator-slowvote-css' => '7c27f0f9',
|
||||
'phabricator-slowvote-css' => '475b4bd2',
|
||||
'phabricator-source-code-view-css' => '5e0178de',
|
||||
'phabricator-standard-page-view' => '4d176b67',
|
||||
'phabricator-textareautils' => '5c93c52c',
|
||||
|
@ -788,7 +788,7 @@ return array(
|
|||
'phui-image-mask-css' => '5a8b09c8',
|
||||
'phui-info-panel-css' => '27ea50a1',
|
||||
'phui-info-view-css' => '5b16bac6',
|
||||
'phui-inline-comment-view-css' => '9fadd6b8',
|
||||
'phui-inline-comment-view-css' => '0fdb3667',
|
||||
'phui-list-view-css' => '125599df',
|
||||
'phui-object-box-css' => '407eaf5a',
|
||||
'phui-object-item-list-view-css' => '36ce366c',
|
||||
|
@ -802,7 +802,7 @@ return array(
|
|||
'phui-text-css' => 'cf019f54',
|
||||
'phui-theme-css' => '6b451f24',
|
||||
'phui-timeline-view-css' => 'f1bccf73',
|
||||
'phui-two-column-view-css' => 'add0a7d1',
|
||||
'phui-two-column-view-css' => '39ecafb1',
|
||||
'phui-workboard-view-css' => '6704d68d',
|
||||
'phui-workpanel-view-css' => 'adec7699',
|
||||
'phuix-action-list-view' => 'b5c256b8',
|
||||
|
@ -811,7 +811,7 @@ return array(
|
|||
'policy-css' => '957ea14c',
|
||||
'policy-edit-css' => '815c66f7',
|
||||
'policy-transaction-detail-css' => '82100a43',
|
||||
'ponder-view-css' => '6a399881',
|
||||
'ponder-view-css' => 'bef48f86',
|
||||
'project-icon-css' => '4e3eaa5a',
|
||||
'raphael-core' => '51ee6b43',
|
||||
'raphael-g' => '40dde778',
|
||||
|
@ -955,6 +955,20 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'1d45c74d' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-behavior-device',
|
||||
'javelin-history',
|
||||
'javelin-vector',
|
||||
'javelin-scrollbar',
|
||||
'phabricator-title',
|
||||
'phabricator-shaped-request',
|
||||
'conpherence-thread-manager',
|
||||
),
|
||||
'1def2711' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1798,20 +1812,6 @@ return array(
|
|||
'd254d646' => array(
|
||||
'javelin-util',
|
||||
),
|
||||
'd3782c93' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-behavior-device',
|
||||
'javelin-history',
|
||||
'javelin-vector',
|
||||
'javelin-scrollbar',
|
||||
'phabricator-title',
|
||||
'phabricator-shaped-request',
|
||||
'conpherence-thread-manager',
|
||||
),
|
||||
'd4505101' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildartifact
|
||||
ADD phid VARBINARY(64) NOT NULL AFTER id;
|
||||
|
||||
UPDATE {$NAMESPACE}_harbormaster.harbormaster_buildartifact
|
||||
SET phid = CONCAT('PHID-HMBA-', id) WHERE phid = '';
|
2
resources/sql/autopatches/20150815.owners.status.1.sql
Normal file
2
resources/sql/autopatches/20150815.owners.status.1.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_owners.owners_package
|
||||
ADD status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20150815.owners.status.2.sql
Normal file
2
resources/sql/autopatches/20150815.owners.status.2.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
UPDATE {$NAMESPACE}_owners.owners_package
|
||||
SET status = 'active' WHERE status = '';
|
|
@ -232,7 +232,6 @@ phutil_register_library_map(array(
|
|||
'ConpherenceDAO' => 'applications/conpherence/storage/ConpherenceDAO.php',
|
||||
'ConpherenceDurableColumnView' => 'applications/conpherence/view/ConpherenceDurableColumnView.php',
|
||||
'ConpherenceEditor' => 'applications/conpherence/editor/ConpherenceEditor.php',
|
||||
'ConpherenceFileWidgetView' => 'applications/conpherence/view/ConpherenceFileWidgetView.php',
|
||||
'ConpherenceFormDragAndDropUploadControl' => 'applications/conpherence/view/ConpherenceFormDragAndDropUploadControl.php',
|
||||
'ConpherenceFulltextQuery' => 'applications/conpherence/query/ConpherenceFulltextQuery.php',
|
||||
'ConpherenceHovercardEventListener' => 'applications/conpherence/events/ConpherenceHovercardEventListener.php',
|
||||
|
@ -287,6 +286,7 @@ phutil_register_library_map(array(
|
|||
'DarkConsolePlugin' => 'applications/console/plugin/DarkConsolePlugin.php',
|
||||
'DarkConsoleRequestPlugin' => 'applications/console/plugin/DarkConsoleRequestPlugin.php',
|
||||
'DarkConsoleServicesPlugin' => 'applications/console/plugin/DarkConsoleServicesPlugin.php',
|
||||
'DarkConsoleStartupPlugin' => 'applications/console/plugin/DarkConsoleStartupPlugin.php',
|
||||
'DarkConsoleXHProfPlugin' => 'applications/console/plugin/DarkConsoleXHProfPlugin.php',
|
||||
'DarkConsoleXHProfPluginAPI' => 'applications/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php',
|
||||
'DatabaseConfigurationProvider' => 'infrastructure/storage/configuration/DatabaseConfigurationProvider.php',
|
||||
|
@ -367,7 +367,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php',
|
||||
'DifferentialDiffRepositoryHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryHeraldField.php',
|
||||
'DifferentialDiffRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryProjectsHeraldField.php',
|
||||
'DifferentialDiffTableOfContentsView' => 'applications/differential/view/DifferentialDiffTableOfContentsView.php',
|
||||
'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php',
|
||||
'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php',
|
||||
'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php',
|
||||
|
@ -519,7 +518,6 @@ phutil_register_library_map(array(
|
|||
'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php',
|
||||
'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php',
|
||||
'DiffusionCommitBranchesHeraldField' => 'applications/diffusion/herald/DiffusionCommitBranchesHeraldField.php',
|
||||
'DiffusionCommitChangeTableView' => 'applications/diffusion/view/DiffusionCommitChangeTableView.php',
|
||||
'DiffusionCommitCommitterHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterHeraldField.php',
|
||||
'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php',
|
||||
'DiffusionCommitDiffContentAddedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentAddedHeraldField.php',
|
||||
|
@ -915,12 +913,14 @@ phutil_register_library_map(array(
|
|||
'FundSchemaSpec' => 'applications/fund/storage/FundSchemaSpec.php',
|
||||
'HarbormasterArcLintBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php',
|
||||
'HarbormasterArcUnitBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcUnitBuildStepImplementation.php',
|
||||
'HarbormasterArtifact' => 'applications/harbormaster/artifact/HarbormasterArtifact.php',
|
||||
'HarbormasterAutotargetsTestCase' => 'applications/harbormaster/__tests__/HarbormasterAutotargetsTestCase.php',
|
||||
'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php',
|
||||
'HarbormasterBuildAbortedException' => 'applications/harbormaster/exception/HarbormasterBuildAbortedException.php',
|
||||
'HarbormasterBuildActionController' => 'applications/harbormaster/controller/HarbormasterBuildActionController.php',
|
||||
'HarbormasterBuildArcanistAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildArcanistAutoplan.php',
|
||||
'HarbormasterBuildArtifact' => 'applications/harbormaster/storage/build/HarbormasterBuildArtifact.php',
|
||||
'HarbormasterBuildArtifactPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php',
|
||||
'HarbormasterBuildArtifactQuery' => 'applications/harbormaster/query/HarbormasterBuildArtifactQuery.php',
|
||||
'HarbormasterBuildAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildAutoplan.php',
|
||||
'HarbormasterBuildCommand' => 'applications/harbormaster/storage/HarbormasterBuildCommand.php',
|
||||
|
@ -980,9 +980,12 @@ phutil_register_library_map(array(
|
|||
'HarbormasterCommandBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php',
|
||||
'HarbormasterConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php',
|
||||
'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php',
|
||||
'HarbormasterCreateArtifactConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php',
|
||||
'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php',
|
||||
'HarbormasterExternalBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterExternalBuildStepGroup.php',
|
||||
'HarbormasterFileArtifact' => 'applications/harbormaster/artifact/HarbormasterFileArtifact.php',
|
||||
'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php',
|
||||
'HarbormasterHostArtifact' => 'applications/harbormaster/artifact/HarbormasterHostArtifact.php',
|
||||
'HarbormasterLeaseHostBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php',
|
||||
'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php',
|
||||
'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php',
|
||||
|
@ -1018,6 +1021,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterTestBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterTestBuildStepGroup.php',
|
||||
'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php',
|
||||
'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php',
|
||||
'HarbormasterURIArtifact' => 'applications/harbormaster/artifact/HarbormasterURIArtifact.php',
|
||||
'HarbormasterUnitMessagesController' => 'applications/harbormaster/controller/HarbormasterUnitMessagesController.php',
|
||||
'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php',
|
||||
'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php',
|
||||
|
@ -1356,6 +1360,8 @@ phutil_register_library_map(array(
|
|||
'PHUIDiffInlineCommentView' => 'infrastructure/diff/view/PHUIDiffInlineCommentView.php',
|
||||
'PHUIDiffOneUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php',
|
||||
'PHUIDiffRevealIconView' => 'infrastructure/diff/view/PHUIDiffRevealIconView.php',
|
||||
'PHUIDiffTableOfContentsItemView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php',
|
||||
'PHUIDiffTableOfContentsListView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsListView.php',
|
||||
'PHUIDiffTwoUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php',
|
||||
'PHUIDocumentExample' => 'applications/uiexample/examples/PHUIDocumentExample.php',
|
||||
'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php',
|
||||
|
@ -2241,6 +2247,9 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMacroTransactionComment' => 'applications/macro/storage/PhabricatorMacroTransactionComment.php',
|
||||
'PhabricatorMacroTransactionQuery' => 'applications/macro/query/PhabricatorMacroTransactionQuery.php',
|
||||
'PhabricatorMacroViewController' => 'applications/macro/controller/PhabricatorMacroViewController.php',
|
||||
'PhabricatorMailEmailHeraldField' => 'applications/metamta/herald/PhabricatorMailEmailHeraldField.php',
|
||||
'PhabricatorMailEmailHeraldFieldGroup' => 'applications/metamta/herald/PhabricatorMailEmailHeraldFieldGroup.php',
|
||||
'PhabricatorMailEmailSubjectHeraldField' => 'applications/metamta/herald/PhabricatorMailEmailSubjectHeraldField.php',
|
||||
'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAdapter.php',
|
||||
'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAmazonSESAdapter.php',
|
||||
'PhabricatorMailImplementationMailgunAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationMailgunAdapter.php',
|
||||
|
@ -2257,10 +2266,15 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMailManagementShowOutboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php',
|
||||
'PhabricatorMailManagementVolumeWorkflow' => 'applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php',
|
||||
'PhabricatorMailManagementWorkflow' => 'applications/metamta/management/PhabricatorMailManagementWorkflow.php',
|
||||
'PhabricatorMailOutboundMailHeraldAdapter' => 'applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php',
|
||||
'PhabricatorMailOutboundRoutingHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingHeraldAction.php',
|
||||
'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php',
|
||||
'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php',
|
||||
'PhabricatorMailOutboundStatus' => 'applications/metamta/constants/PhabricatorMailOutboundStatus.php',
|
||||
'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php',
|
||||
'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php',
|
||||
'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
|
||||
'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php',
|
||||
'PhabricatorMailSetupCheck' => 'applications/config/check/PhabricatorMailSetupCheck.php',
|
||||
'PhabricatorMailTarget' => 'applications/metamta/replyhandler/PhabricatorMailTarget.php',
|
||||
'PhabricatorMailgunConfigOptions' => 'applications/config/option/PhabricatorMailgunConfigOptions.php',
|
||||
|
@ -3392,8 +3406,10 @@ phutil_register_library_map(array(
|
|||
'PonderAnswerEditor' => 'applications/ponder/editor/PonderAnswerEditor.php',
|
||||
'PonderAnswerHasVotingUserEdgeType' => 'applications/ponder/edge/PonderAnswerHasVotingUserEdgeType.php',
|
||||
'PonderAnswerHistoryController' => 'applications/ponder/controller/PonderAnswerHistoryController.php',
|
||||
'PonderAnswerMailReceiver' => 'applications/ponder/mail/PonderAnswerMailReceiver.php',
|
||||
'PonderAnswerPHIDType' => 'applications/ponder/phid/PonderAnswerPHIDType.php',
|
||||
'PonderAnswerQuery' => 'applications/ponder/query/PonderAnswerQuery.php',
|
||||
'PonderAnswerReplyHandler' => 'applications/ponder/mail/PonderAnswerReplyHandler.php',
|
||||
'PonderAnswerSaveController' => 'applications/ponder/controller/PonderAnswerSaveController.php',
|
||||
'PonderAnswerStatus' => 'applications/ponder/constants/PonderAnswerStatus.php',
|
||||
'PonderAnswerTransaction' => 'applications/ponder/storage/PonderAnswerTransaction.php',
|
||||
|
@ -3428,7 +3444,6 @@ phutil_register_library_map(array(
|
|||
'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php',
|
||||
'PonderSchemaSpec' => 'applications/ponder/storage/PonderSchemaSpec.php',
|
||||
'PonderSearchIndexer' => 'applications/ponder/search/PonderSearchIndexer.php',
|
||||
'PonderTransactionFeedStory' => 'applications/ponder/feed/PonderTransactionFeedStory.php',
|
||||
'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php',
|
||||
'PonderVote' => 'applications/ponder/constants/PonderVote.php',
|
||||
'PonderVoteEditor' => 'applications/ponder/editor/PonderVoteEditor.php',
|
||||
|
@ -3838,7 +3853,6 @@ phutil_register_library_map(array(
|
|||
'ConpherenceDAO' => 'PhabricatorLiskDAO',
|
||||
'ConpherenceDurableColumnView' => 'AphrontTagView',
|
||||
'ConpherenceEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'ConpherenceFileWidgetView' => 'ConpherenceWidgetView',
|
||||
'ConpherenceFormDragAndDropUploadControl' => 'AphrontFormControl',
|
||||
'ConpherenceFulltextQuery' => 'PhabricatorOffsetPagedQuery',
|
||||
'ConpherenceHovercardEventListener' => 'PhabricatorEventListener',
|
||||
|
@ -3899,6 +3913,7 @@ phutil_register_library_map(array(
|
|||
'DarkConsolePlugin' => 'Phobject',
|
||||
'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleStartupPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleXHProfPluginAPI' => 'Phobject',
|
||||
'DefaultDatabaseConfigurationProvider' => array(
|
||||
|
@ -3990,7 +4005,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'DifferentialDiffRepositoryHeraldField' => 'DifferentialDiffHeraldField',
|
||||
'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField',
|
||||
'DifferentialDiffTableOfContentsView' => 'AphrontView',
|
||||
'DifferentialDiffTestCase' => 'PhutilTestCase',
|
||||
'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
|
@ -4162,7 +4176,6 @@ phutil_register_library_map(array(
|
|||
'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField',
|
||||
'DiffusionCommitBranchesController' => 'DiffusionController',
|
||||
'DiffusionCommitBranchesHeraldField' => 'DiffusionCommitHeraldField',
|
||||
'DiffusionCommitChangeTableView' => 'DiffusionView',
|
||||
'DiffusionCommitCommitterHeraldField' => 'DiffusionCommitHeraldField',
|
||||
'DiffusionCommitController' => 'DiffusionController',
|
||||
'DiffusionCommitDiffContentAddedHeraldField' => 'DiffusionCommitHeraldField',
|
||||
|
@ -4603,6 +4616,7 @@ phutil_register_library_map(array(
|
|||
'FundSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'HarbormasterArcLintBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterArcUnitBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterArtifact' => 'Phobject',
|
||||
'HarbormasterAutotargetsTestCase' => 'PhabricatorTestCase',
|
||||
'HarbormasterBuild' => array(
|
||||
'HarbormasterDAO',
|
||||
|
@ -4616,6 +4630,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'HarbormasterBuildArtifactPHIDType' => 'PhabricatorPHIDType',
|
||||
'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'HarbormasterBuildAutoplan' => 'Phobject',
|
||||
'HarbormasterBuildCommand' => 'HarbormasterDAO',
|
||||
|
@ -4700,9 +4715,12 @@ phutil_register_library_map(array(
|
|||
'HarbormasterCommandBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'HarbormasterController' => 'PhabricatorController',
|
||||
'HarbormasterCreateArtifactConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
|
||||
'HarbormasterDAO' => 'PhabricatorLiskDAO',
|
||||
'HarbormasterExternalBuildStepGroup' => 'HarbormasterBuildStepGroup',
|
||||
'HarbormasterFileArtifact' => 'HarbormasterArtifact',
|
||||
'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterHostArtifact' => 'HarbormasterArtifact',
|
||||
'HarbormasterLeaseHostBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterLintMessagesController' => 'HarbormasterController',
|
||||
'HarbormasterLintPropertyView' => 'AphrontView',
|
||||
|
@ -4738,6 +4756,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterTestBuildStepGroup' => 'HarbormasterBuildStepGroup',
|
||||
'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterUIEventListener' => 'PhabricatorEventListener',
|
||||
'HarbormasterURIArtifact' => 'HarbormasterArtifact',
|
||||
'HarbormasterUnitMessagesController' => 'HarbormasterController',
|
||||
'HarbormasterUnitPropertyView' => 'AphrontView',
|
||||
'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
|
@ -5137,6 +5156,8 @@ phutil_register_library_map(array(
|
|||
'PHUIDiffInlineCommentView' => 'AphrontView',
|
||||
'PHUIDiffOneUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold',
|
||||
'PHUIDiffRevealIconView' => 'AphrontView',
|
||||
'PHUIDiffTableOfContentsItemView' => 'AphrontView',
|
||||
'PHUIDiffTableOfContentsListView' => 'AphrontView',
|
||||
'PHUIDiffTwoUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold',
|
||||
'PHUIDocumentExample' => 'PhabricatorUIExample',
|
||||
'PHUIDocumentView' => 'AphrontTagView',
|
||||
|
@ -6166,6 +6187,9 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMacroTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
'PhabricatorMacroTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorMacroViewController' => 'PhabricatorMacroController',
|
||||
'PhabricatorMailEmailHeraldField' => 'HeraldField',
|
||||
'PhabricatorMailEmailHeraldFieldGroup' => 'HeraldFieldGroup',
|
||||
'PhabricatorMailEmailSubjectHeraldField' => 'PhabricatorMailEmailHeraldField',
|
||||
'PhabricatorMailImplementationAdapter' => 'Phobject',
|
||||
'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
|
||||
'PhabricatorMailImplementationMailgunAdapter' => 'PhabricatorMailImplementationAdapter',
|
||||
|
@ -6182,10 +6206,15 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMailManagementShowOutboundWorkflow' => 'PhabricatorMailManagementWorkflow',
|
||||
'PhabricatorMailManagementVolumeWorkflow' => 'PhabricatorMailManagementWorkflow',
|
||||
'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorMailOutboundMailHeraldAdapter' => 'HeraldAdapter',
|
||||
'PhabricatorMailOutboundRoutingHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction',
|
||||
'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction',
|
||||
'PhabricatorMailOutboundStatus' => 'Phobject',
|
||||
'PhabricatorMailReceiver' => 'Phobject',
|
||||
'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorMailReplyHandler' => 'Phobject',
|
||||
'PhabricatorMailRoutingRule' => 'Phobject',
|
||||
'PhabricatorMailSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorMailTarget' => 'Phobject',
|
||||
'PhabricatorMailgunConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
|
@ -7579,8 +7608,10 @@ phutil_register_library_map(array(
|
|||
'PonderAnswerEditor' => 'PonderEditor',
|
||||
'PonderAnswerHasVotingUserEdgeType' => 'PhabricatorEdgeType',
|
||||
'PonderAnswerHistoryController' => 'PonderController',
|
||||
'PonderAnswerMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'PonderAnswerPHIDType' => 'PhabricatorPHIDType',
|
||||
'PonderAnswerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PonderAnswerReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
'PonderAnswerSaveController' => 'PonderController',
|
||||
'PonderAnswerStatus' => 'PonderConstants',
|
||||
'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction',
|
||||
|
@ -7626,7 +7657,6 @@ phutil_register_library_map(array(
|
|||
'PonderRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PonderSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PonderSearchIndexer' => 'PhabricatorSearchDocumentIndexer',
|
||||
'PonderTransactionFeedStory' => 'PhabricatorApplicationTransactionFeedStory',
|
||||
'PonderVote' => 'PonderConstants',
|
||||
'PonderVoteEditor' => 'PhabricatorEditor',
|
||||
'PonderVotingUserHasAnswerEdgeType' => 'PhabricatorEdgeType',
|
||||
|
|
|
@ -58,6 +58,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
public static function runHTTPRequest(AphrontHTTPSink $sink) {
|
||||
PhabricatorStartup::beginStartupPhase('multimeter');
|
||||
$multimeter = MultimeterControl::newInstance();
|
||||
$multimeter->setEventContext('<http-init>');
|
||||
$multimeter->setEventViewer('<none>');
|
||||
|
@ -67,6 +68,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
// request object first.
|
||||
$write_guard = new AphrontWriteGuard('id');
|
||||
|
||||
PhabricatorStartup::beginStartupPhase('env.init');
|
||||
PhabricatorEnv::initializeWebEnvironment();
|
||||
|
||||
$multimeter->setSampleRate(
|
||||
|
@ -78,6 +80,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
}
|
||||
|
||||
// This is the earliest we can get away with this, we need env config first.
|
||||
PhabricatorStartup::beginStartupPhase('log.access');
|
||||
PhabricatorAccessLog::init();
|
||||
$access_log = PhabricatorAccessLog::getLog();
|
||||
PhabricatorStartup::setAccessLog($access_log);
|
||||
|
@ -89,6 +92,11 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
));
|
||||
|
||||
DarkConsoleXHProfPluginAPI::hookProfiler();
|
||||
|
||||
// We just activated the profiler, so we don't need to keep track of
|
||||
// startup phases anymore: it can take over from here.
|
||||
PhabricatorStartup::beginStartupPhase('startup.done');
|
||||
|
||||
DarkConsoleErrorLogPluginAPI::registerErrorHandler();
|
||||
|
||||
$response = PhabricatorSetupCheck::willProcessRequest();
|
||||
|
|
|
@ -20,7 +20,7 @@ final class PhabricatorAuditCommentEditor extends PhabricatorEditor {
|
|||
|
||||
$owned_packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($user)
|
||||
->withOwnerPHIDs(array($user->getPHID()))
|
||||
->withAuthorityPHIDs(array($user->getPHID()))
|
||||
->execute();
|
||||
foreach ($owned_packages as $package) {
|
||||
$phids[$package->getPHID()] = true;
|
||||
|
|
|
@ -201,7 +201,7 @@ final class PhabricatorBadgesEditor
|
|||
|
||||
$body->addLinkSection(
|
||||
pht('BADGE DETAIL'),
|
||||
PhabricatorEnv::getProductionURI('/badge/view/'.$object->getID().'/'));
|
||||
PhabricatorEnv::getProductionURI('/badges/view/'.$object->getID().'/'));
|
||||
return $body;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,23 +30,6 @@ final class ConpherenceWidgetConfigConstants extends ConpherenceConstants {
|
|||
'customHref' => null,
|
||||
),
|
||||
),
|
||||
'widgets-files' => array(
|
||||
'name' => pht('Files'),
|
||||
'icon' => 'fa-files-o',
|
||||
'deviceOnly' => false,
|
||||
'hasCreate' => false,
|
||||
),
|
||||
'widgets-calendar' => array(
|
||||
'name' => pht('Calendar'),
|
||||
'icon' => 'fa-calendar',
|
||||
'deviceOnly' => false,
|
||||
'hasCreate' => true,
|
||||
'createData' => array(
|
||||
'refreshFromResponse' => false,
|
||||
'action' => ConpherenceUpdateActions::ADD_STATUS,
|
||||
'customHref' => '/calendar/event/create/',
|
||||
),
|
||||
),
|
||||
'widgets-settings' => array(
|
||||
'name' => pht('Notifications'),
|
||||
'icon' => 'fa-wrench',
|
||||
|
|
|
@ -27,14 +27,6 @@ abstract class ConpherenceController extends PhabricatorController {
|
|||
->addSigil('conpherence-widget-adder')
|
||||
->setMetadata(array('widget' => 'widgets-people')));
|
||||
|
||||
$nav->addMenuItem(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('New Calendar Item'))
|
||||
->setType(PHUIListItemView::TYPE_LINK)
|
||||
->setHref('/calendar/event/create/')
|
||||
->addSigil('conpherence-widget-adder')
|
||||
->setMetadata(array('widget' => 'widgets-calendar')));
|
||||
|
||||
return $nav;
|
||||
}
|
||||
|
||||
|
|
|
@ -524,13 +524,6 @@ final class ConpherenceUpdateController
|
|||
->renderSingleThread($conpherence, $policy_objects);
|
||||
$nav_item = hsprintf('%s', $nav_item);
|
||||
break;
|
||||
case ConpherenceUpdateActions::MESSAGE:
|
||||
$file_widget = id(new ConpherenceFileWidgetView())
|
||||
->setUser($this->getRequest()->getUser())
|
||||
->setConpherence($conpherence)
|
||||
->setUpdateURI($widget_uri);
|
||||
$file_widget = hsprintf('%s', $file_widget->render());
|
||||
break;
|
||||
case ConpherenceUpdateActions::ADD_PERSON:
|
||||
$people_widget = id(new ConpherencePeopleWidgetView())
|
||||
->setUser($user)
|
||||
|
|
|
@ -41,13 +41,6 @@ final class ConpherenceWidgetController extends ConpherenceController {
|
|||
case 'widgets-people':
|
||||
$content = $this->renderPeopleWidgetPaneContent();
|
||||
break;
|
||||
case 'widgets-files':
|
||||
$content = $this->renderFileWidgetPaneContent();
|
||||
break;
|
||||
case 'widgets-calendar':
|
||||
$widget = $this->renderCalendarWidgetPaneContent();
|
||||
$content = phutil_implode_html('', $widget);
|
||||
break;
|
||||
case 'widgets-settings':
|
||||
$content = $this->renderSettingsWidgetPaneContent();
|
||||
break;
|
||||
|
@ -94,23 +87,6 @@ final class ConpherenceWidgetController extends ConpherenceController {
|
|||
'sigil' => 'widgets-people',
|
||||
),
|
||||
$this->renderPeopleWidgetPaneContent());
|
||||
$widgets[] = javelin_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'widgets-body',
|
||||
'id' => 'widgets-files',
|
||||
'sigil' => 'widgets-files',
|
||||
'style' => 'display: none;',
|
||||
),
|
||||
$this->renderFileWidgetPaneContent());
|
||||
$widgets[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'widgets-body',
|
||||
'id' => 'widgets-calendar',
|
||||
'style' => 'display: none;',
|
||||
),
|
||||
$this->renderCalendarWidgetPaneContent());
|
||||
$widgets[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
|
@ -139,12 +115,6 @@ final class ConpherenceWidgetController extends ConpherenceController {
|
|||
->setUpdateURI($this->getWidgetURI());
|
||||
}
|
||||
|
||||
private function renderFileWidgetPaneContent() {
|
||||
return id(new ConpherenceFileWidgetView())
|
||||
->setUser($this->getViewer())
|
||||
->setConpherence($this->getConpherence())
|
||||
->setUpdateURI($this->getWidgetURI());
|
||||
}
|
||||
|
||||
private function renderSettingsWidgetPaneContent() {
|
||||
$viewer = $this->getViewer();
|
||||
|
@ -227,205 +197,6 @@ final class ConpherenceWidgetController extends ConpherenceController {
|
|||
$layout);
|
||||
}
|
||||
|
||||
private function renderCalendarWidgetPaneContent() {
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$conpherence = $this->getConpherence();
|
||||
$participants = $conpherence->getParticipants();
|
||||
$widget_data = $conpherence->getWidgetData();
|
||||
|
||||
// TODO: This panel is built around an outdated notion of events and isn't
|
||||
// invitee-aware.
|
||||
|
||||
$statuses = $widget_data['events'];
|
||||
$handles = $conpherence->getHandles();
|
||||
$content = array();
|
||||
$layout = id(new AphrontMultiColumnView())
|
||||
->setFluidLayout(true);
|
||||
$timestamps = CalendarTimeUtil::getCalendarWidgetTimestamps($user);
|
||||
$today = $timestamps['today'];
|
||||
$epoch_stamps = $timestamps['epoch_stamps'];
|
||||
$one_day = 24 * 60 * 60;
|
||||
$is_today = false;
|
||||
$calendar_columns = 0;
|
||||
$list_days = 0;
|
||||
foreach ($epoch_stamps as $day) {
|
||||
// build a header for the new day
|
||||
if ($day->format('Ymd') == $today->format('Ymd')) {
|
||||
$active_class = 'today';
|
||||
$is_today = true;
|
||||
} else {
|
||||
$active_class = '';
|
||||
$is_today = false;
|
||||
}
|
||||
|
||||
$should_draw_list = $list_days < 7;
|
||||
$list_days++;
|
||||
|
||||
if ($should_draw_list) {
|
||||
$content[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'day-header '.$active_class,
|
||||
),
|
||||
array(
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'day-name',
|
||||
),
|
||||
$day->format('l')),
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'day-date',
|
||||
),
|
||||
$day->format('m/d/y')),
|
||||
));
|
||||
}
|
||||
|
||||
$week_day_number = $day->format('w');
|
||||
|
||||
$epoch_start = $day->format('U');
|
||||
$next_day = clone $day;
|
||||
$next_day->modify('+1 day');
|
||||
$epoch_end = $next_day->format('U');
|
||||
|
||||
$first_status_of_the_day = true;
|
||||
$statuses_of_the_day = array();
|
||||
// keep looking through statuses where we last left off
|
||||
foreach ($statuses as $status) {
|
||||
if ($status->getDateFrom() >= $epoch_end) {
|
||||
// This list is sorted, so we can stop looking.
|
||||
break;
|
||||
}
|
||||
|
||||
if ($status->getDateFrom() < $epoch_end &&
|
||||
$status->getDateTo() > $epoch_start) {
|
||||
$statuses_of_the_day[$status->getUserPHID()] = $status;
|
||||
if ($should_draw_list) {
|
||||
$top_border = '';
|
||||
if (!$first_status_of_the_day) {
|
||||
$top_border = ' top-border';
|
||||
}
|
||||
$timespan = $status->getDateTo() - $status->getDateFrom();
|
||||
if ($timespan > $one_day) {
|
||||
$time_str = 'm/d';
|
||||
} else {
|
||||
$time_str = 'h:i A';
|
||||
}
|
||||
$epoch_range =
|
||||
phabricator_format_local_time(
|
||||
$status->getDateFrom(),
|
||||
$user,
|
||||
$time_str).
|
||||
' - '.
|
||||
phabricator_format_local_time(
|
||||
$status->getDateTo(),
|
||||
$user,
|
||||
$time_str);
|
||||
|
||||
if (isset($handles[$status->getUserPHID()])) {
|
||||
$secondary_info = pht(
|
||||
'%s, %s',
|
||||
$handles[$status->getUserPHID()]->getName(),
|
||||
$epoch_range);
|
||||
} else {
|
||||
$secondary_info = $epoch_range;
|
||||
}
|
||||
|
||||
$content[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'user-status '.$top_border,
|
||||
),
|
||||
array(
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'icon',
|
||||
),
|
||||
''),
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'description',
|
||||
),
|
||||
array(
|
||||
$status->getName(),
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'participant',
|
||||
),
|
||||
$secondary_info),
|
||||
)),
|
||||
));
|
||||
}
|
||||
$first_status_of_the_day = false;
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't get a status on this day so add a spacer
|
||||
if ($first_status_of_the_day && $should_draw_list) {
|
||||
$content[] = phutil_tag(
|
||||
'div',
|
||||
array('class' => 'no-events pm'),
|
||||
pht('No Events Scheduled.'));
|
||||
}
|
||||
if ($is_today || ($calendar_columns && $calendar_columns < 3)) {
|
||||
$active_class = '';
|
||||
if ($is_today) {
|
||||
$active_class = '-active';
|
||||
}
|
||||
$inner_layout = array();
|
||||
foreach ($participants as $phid => $participant) {
|
||||
$status = idx($statuses_of_the_day, $phid, false);
|
||||
if ($status) {
|
||||
$inner_layout[] = phutil_tag(
|
||||
'div',
|
||||
array(),
|
||||
'');
|
||||
} else {
|
||||
$inner_layout[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'present',
|
||||
),
|
||||
'');
|
||||
}
|
||||
}
|
||||
$layout->addColumn(
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'day-column'.$active_class,
|
||||
),
|
||||
array(
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'day-name',
|
||||
),
|
||||
$day->format('D')),
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'day-number',
|
||||
),
|
||||
$day->format('j')),
|
||||
$inner_layout,
|
||||
)));
|
||||
$calendar_columns++;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
$layout,
|
||||
$content,
|
||||
);
|
||||
}
|
||||
|
||||
private function getWidgetURI() {
|
||||
$conpherence = $this->getConpherence();
|
||||
return $this->getApplicationURI('update/'.$conpherence->getID().'/');
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ConpherenceFileWidgetView extends ConpherenceWidgetView {
|
||||
|
||||
public function render() {
|
||||
$conpherence = $this->getConpherence();
|
||||
$widget_data = $conpherence->getWidgetData();
|
||||
$files = $widget_data['files'];
|
||||
$files_authors = $widget_data['files_authors'];
|
||||
$files_html = array();
|
||||
|
||||
foreach ($files as $file) {
|
||||
$icon_class = $file->getDisplayIconForMimeType();
|
||||
$icon_view = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'file-icon phui-font-fa phui-icon-view '.$icon_class,
|
||||
),
|
||||
'');
|
||||
$file_view = id(new PhabricatorFileLinkView())
|
||||
->setFilePHID($file->getPHID())
|
||||
->setFileName(id(new PhutilUTF8StringTruncator())
|
||||
->setMaximumGlyphs(28)
|
||||
->truncateString($file->getName()))
|
||||
->setFileViewable($file->isViewableImage())
|
||||
->setFileViewURI($file->getBestURI())
|
||||
->setCustomClass('file-title');
|
||||
|
||||
$who_done_it_text = '';
|
||||
// system generated files don't have authors
|
||||
if ($file->getAuthorPHID()) {
|
||||
$who_done_it_text = pht(
|
||||
'By %s ',
|
||||
$files_authors[$file->getPHID()]->renderLink());
|
||||
}
|
||||
$date_text = phabricator_relative_date(
|
||||
$file->getDateCreated(),
|
||||
$this->getUser());
|
||||
|
||||
$who_done_it = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'file-uploaded-by',
|
||||
),
|
||||
pht('%s%s.', $who_done_it_text, $date_text));
|
||||
|
||||
$files_html[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'file-entry',
|
||||
),
|
||||
array(
|
||||
$icon_view,
|
||||
$file_view,
|
||||
$who_done_it,
|
||||
));
|
||||
}
|
||||
|
||||
if (empty($files)) {
|
||||
$files_html[] = javelin_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'no-files',
|
||||
'sigil' => 'no-files',
|
||||
),
|
||||
pht('No files.'));
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array('class' => 'file-list'),
|
||||
$files_html);
|
||||
|
||||
}
|
||||
|
||||
}
|
84
src/applications/console/plugin/DarkConsoleStartupPlugin.php
Normal file
84
src/applications/console/plugin/DarkConsoleStartupPlugin.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
final class DarkConsoleStartupPlugin extends DarkConsolePlugin {
|
||||
|
||||
public function getName() {
|
||||
return pht('Startup');
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return pht('Timing information about the startup sequence.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
public function generateData() {
|
||||
return PhabricatorStartup::getPhases();
|
||||
}
|
||||
|
||||
public function renderPanel() {
|
||||
$data = $this->getData();
|
||||
|
||||
// Compute the time offset and duration of each startup phase.
|
||||
$prev_key = null;
|
||||
$init = null;
|
||||
$phases = array();
|
||||
foreach ($data as $key => $value) {
|
||||
if ($init === null) {
|
||||
$init = $value;
|
||||
}
|
||||
|
||||
$offset = (int)floor(1000 * ($value - $init));
|
||||
|
||||
$phases[$key] = array(
|
||||
'time' => $value,
|
||||
'offset' => $value - $init,
|
||||
);
|
||||
|
||||
|
||||
if ($prev_key !== null) {
|
||||
$phases[$prev_key]['duration'] = $value - $phases[$prev_key]['time'];
|
||||
}
|
||||
$prev_key = $key;
|
||||
}
|
||||
|
||||
// Render the phases.
|
||||
$rows = array();
|
||||
foreach ($phases as $key => $phase) {
|
||||
$offset_ms = (int)floor(1000 * $phase['offset']);
|
||||
|
||||
if (isset($phase['duration'])) {
|
||||
$duration_us = (int)floor(1000000 * $phase['duration']);
|
||||
} else {
|
||||
$duration_us = null;
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
$key,
|
||||
pht('+%s ms', new PhutilNumber($offset_ms)),
|
||||
($duration_us === null)
|
||||
? pht('-')
|
||||
: pht('%s us', new PhutilNumber($duration_us)),
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
return id(new AphrontTableView($rows))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Phase'),
|
||||
pht('Offset'),
|
||||
pht('Duration'),
|
||||
null,
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'n right',
|
||||
'n right',
|
||||
'wide',
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -124,7 +124,13 @@ final class DarkConsoleXHProfPluginAPI extends Phobject {
|
|||
self::startProfiler();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
private static function startProfiler() {
|
||||
PhabricatorStartup::beginStartupPhase('profiler.init');
|
||||
|
||||
self::includeXHProfLib();
|
||||
xhprof_enable();
|
||||
|
||||
|
@ -132,15 +138,23 @@ final class DarkConsoleXHProfPluginAPI extends Phobject {
|
|||
self::$profilerRunning = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
public static function getProfileFilePHID() {
|
||||
if (!self::isProfilerRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PhabricatorStartup::beginStartupPhase('profiler.stop');
|
||||
self::stopProfiler();
|
||||
PhabricatorStartup::beginStartupPhase('profiler.done');
|
||||
|
||||
return self::$profileFilePHID;
|
||||
}
|
||||
|
||||
private static function stopProfiler() {
|
||||
if (!self::isProfilerRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = xhprof_disable();
|
||||
$data = @json_encode($data);
|
||||
|
|
|
@ -147,12 +147,10 @@ final class PhabricatorCountdownEditor
|
|||
|
||||
public function getMailTagsMap() {
|
||||
return array(
|
||||
PhabricatorCountdownTransaction::MAILTAG_TITLE =>
|
||||
pht('Someone changes the countdown title.'),
|
||||
PhabricatorCountdownTransaction::MAILTAG_DESCRIPTION =>
|
||||
pht('Someone changes the countdown description.'),
|
||||
PhabricatorCountdownTransaction::MAILTAG_EPOCH =>
|
||||
pht('Someone changes the countdown end date.'),
|
||||
PhabricatorCountdownTransaction::MAILTAG_DETAILS =>
|
||||
pht('Someone changes the countdown details.'),
|
||||
PhabricatorCountdownTransaction::MAILTAG_COMMENT =>
|
||||
pht('Someone comments on a countdown.'),
|
||||
PhabricatorCountdownTransaction::MAILTAG_OTHER =>
|
||||
pht('Other countdown activity not listed above occurs.'),
|
||||
);
|
||||
|
|
|
@ -7,9 +7,8 @@ final class PhabricatorCountdownTransaction
|
|||
const TYPE_EPOCH = 'countdown:epoch';
|
||||
const TYPE_DESCRIPTION = 'countdown:description';
|
||||
|
||||
const MAILTAG_TITLE = 'countdown:title';
|
||||
const MAILTAG_EPOCH = 'countdown:epoch';
|
||||
const MAILTAG_DESCRIPTION = 'countdown:description';
|
||||
const MAILTAG_DETAILS = 'countdown:details';
|
||||
const MAILTAG_COMMENT = 'countdown:comment';
|
||||
const MAILTAG_OTHER = 'countdown:other';
|
||||
|
||||
public function getApplicationName() {
|
||||
|
@ -135,14 +134,13 @@ final class PhabricatorCountdownTransaction
|
|||
$tags = parent::getMailTags();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
$tags[] = self::MAILTAG_COMMENT;
|
||||
break;
|
||||
case self::TYPE_TITLE:
|
||||
$tags[] = self::MAILTAG_TITLE;
|
||||
break;
|
||||
case self::TYPE_EPOCH:
|
||||
$tags[] = self::MAILTAG_EPOCH;
|
||||
break;
|
||||
case self::TYPE_DESCRIPTION:
|
||||
$tags[] = self::MAILTAG_DESCRIPTION;
|
||||
$tags[] = self::MAILTAG_DETAILS;
|
||||
break;
|
||||
default:
|
||||
$tags[] = self::MAILTAG_OTHER;
|
||||
|
|
|
@ -21,4 +21,75 @@ abstract class DifferentialController extends PhabricatorController {
|
|||
return $this->buildSideNavView(true)->getMenu();
|
||||
}
|
||||
|
||||
protected function buildTableOfContents(
|
||||
array $changesets,
|
||||
array $visible_changesets,
|
||||
array $coverage) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$toc_view = id(new PHUIDiffTableOfContentsListView())
|
||||
->setUser($viewer);
|
||||
|
||||
$have_owners = PhabricatorApplication::isClassInstalledForViewer(
|
||||
'PhabricatorOwnersApplication',
|
||||
$viewer);
|
||||
if ($have_owners) {
|
||||
$repository_phid = null;
|
||||
if ($changesets) {
|
||||
$changeset = head($changesets);
|
||||
$diff = $changeset->getDiff();
|
||||
$repository_phid = $diff->getRepositoryPHID();
|
||||
}
|
||||
|
||||
if (!$repository_phid) {
|
||||
$have_owners = false;
|
||||
} else {
|
||||
if ($viewer->getPHID()) {
|
||||
$packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
|
||||
->withAuthorityPHIDs(array($viewer->getPHID()))
|
||||
->execute();
|
||||
$toc_view->setAuthorityPackages($packages);
|
||||
}
|
||||
|
||||
// TODO: For Subversion, we should adjust these paths to be relative to
|
||||
// the repository root where possible.
|
||||
$paths = mpull($changesets, 'getFilename');
|
||||
|
||||
$control_query = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
|
||||
->withControl($repository_phid, $paths);
|
||||
$control_query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($changesets as $changeset_id => $changeset) {
|
||||
$is_visible = isset($visible_changesets[$changeset_id]);
|
||||
$anchor = $changeset->getAnchorName();
|
||||
|
||||
$filename = $changeset->getFilename();
|
||||
$coverage_id = 'differential-mcoverage-'.md5($filename);
|
||||
|
||||
$item = id(new PHUIDiffTableOfContentsItemView())
|
||||
->setChangeset($changeset)
|
||||
->setIsVisible($is_visible)
|
||||
->setAnchor($anchor)
|
||||
->setCoverage(idx($coverage, $filename))
|
||||
->setCoverageID($coverage_id);
|
||||
|
||||
if ($have_owners) {
|
||||
$packages = $control_query->getControllingPackagesForPath(
|
||||
$repository_phid,
|
||||
$changeset->getFilename());
|
||||
$item->setPackages($packages);
|
||||
}
|
||||
|
||||
$toc_view->addItem($item);
|
||||
}
|
||||
|
||||
return $toc_view;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -116,10 +116,10 @@ final class DifferentialDiffViewController extends DifferentialController {
|
|||
$changesets = $diff->loadChangesets();
|
||||
$changesets = msort($changesets, 'getSortKey');
|
||||
|
||||
$table_of_contents = id(new DifferentialDiffTableOfContentsView())
|
||||
->setChangesets($changesets)
|
||||
->setVisibleChangesets($changesets)
|
||||
->setCoverageMap($diff->loadCoverageMap($viewer));
|
||||
$table_of_contents = $this->buildTableOfContents(
|
||||
$changesets,
|
||||
$changesets,
|
||||
$diff->loadCoverageMap($viewer));
|
||||
|
||||
$refs = array();
|
||||
foreach ($changesets as $changeset) {
|
||||
|
|
|
@ -349,18 +349,10 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
$other_view = $this->renderOtherRevisions($other_revisions);
|
||||
}
|
||||
|
||||
$toc_view = new DifferentialDiffTableOfContentsView();
|
||||
$toc_view->setChangesets($changesets);
|
||||
$toc_view->setVisibleChangesets($visible_changesets);
|
||||
$toc_view->setRenderingReferences($rendering_references);
|
||||
$toc_view->setCoverageMap($target->loadCoverageMap($user));
|
||||
if ($repository) {
|
||||
$toc_view->setRepository($repository);
|
||||
}
|
||||
$toc_view->setDiff($target);
|
||||
$toc_view->setUser($user);
|
||||
$toc_view->setRevisionID($revision->getID());
|
||||
$toc_view->setWhitespace($whitespace);
|
||||
$toc_view = $this->buildTableOfContents(
|
||||
$changesets,
|
||||
$visible_changesets,
|
||||
$target->loadCoverageMap($user));
|
||||
|
||||
$comment_form = null;
|
||||
if (!$viewer_is_anonymous) {
|
||||
|
@ -1044,5 +1036,4 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
return $view;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -101,9 +101,15 @@ final class DifferentialDiff
|
|||
if (!$this->getID()) {
|
||||
return array();
|
||||
}
|
||||
return id(new DifferentialChangeset())->loadAllWhere(
|
||||
$changesets = id(new DifferentialChangeset())->loadAllWhere(
|
||||
'diffID = %d',
|
||||
$this->getID());
|
||||
|
||||
foreach ($changesets as $changeset) {
|
||||
$changeset->attachDiff($this);
|
||||
}
|
||||
|
||||
return $changesets;
|
||||
}
|
||||
|
||||
public function save() {
|
||||
|
|
|
@ -1,312 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialDiffTableOfContentsView extends AphrontView {
|
||||
|
||||
private $changesets = array();
|
||||
private $visibleChangesets = array();
|
||||
private $references = array();
|
||||
private $repository;
|
||||
private $diff;
|
||||
private $renderURI = '/differential/changeset/';
|
||||
private $revisionID;
|
||||
private $whitespace;
|
||||
private $coverageMap;
|
||||
|
||||
public function setChangesets($changesets) {
|
||||
$this->changesets = $changesets;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setVisibleChangesets($visible_changesets) {
|
||||
$this->visibleChangesets = $visible_changesets;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRenderingReferences(array $references) {
|
||||
$this->references = $references;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRepository(PhabricatorRepository $repository) {
|
||||
$this->repository = $repository;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDiff(DifferentialDiff $diff) {
|
||||
$this->diff = $diff;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCoverageMap(array $coverage_map) {
|
||||
$this->coverageMap = $coverage_map;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRevisionID($revision_id) {
|
||||
$this->revisionID = $revision_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setWhitespace($whitespace) {
|
||||
$this->whitespace = $whitespace;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
|
||||
$this->requireResource('differential-core-view-css');
|
||||
$this->requireResource('differential-table-of-contents-css');
|
||||
$this->requireResource('phui-text-css');
|
||||
|
||||
$rows = array();
|
||||
|
||||
$changesets = $this->changesets;
|
||||
$paths = array();
|
||||
foreach ($changesets as $id => $changeset) {
|
||||
$type = $changeset->getChangeType();
|
||||
$ftype = $changeset->getFileType();
|
||||
$ref = idx($this->references, $id);
|
||||
$display_file = $changeset->getDisplayFilename();
|
||||
|
||||
$meta = null;
|
||||
if (DifferentialChangeType::isOldLocationChangeType($type)) {
|
||||
$away = $changeset->getAwayPaths();
|
||||
if (count($away) > 1) {
|
||||
$meta = array();
|
||||
if ($type == DifferentialChangeType::TYPE_MULTICOPY) {
|
||||
$meta[] = pht('Deleted after being copied to multiple locations:');
|
||||
} else {
|
||||
$meta[] = pht('Copied to multiple locations:');
|
||||
}
|
||||
foreach ($away as $path) {
|
||||
$meta[] = $path;
|
||||
}
|
||||
$meta = phutil_implode_html(phutil_tag('br'), $meta);
|
||||
} else {
|
||||
if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) {
|
||||
$display_file = $this->renderRename(
|
||||
$display_file,
|
||||
reset($away),
|
||||
"\xE2\x86\x92");
|
||||
} else {
|
||||
$meta = pht('Copied to %s', reset($away));
|
||||
}
|
||||
}
|
||||
} else if ($type == DifferentialChangeType::TYPE_MOVE_HERE) {
|
||||
$old_file = $changeset->getOldFile();
|
||||
$display_file = $this->renderRename(
|
||||
$display_file,
|
||||
$old_file,
|
||||
"\xE2\x86\x90");
|
||||
} else if ($type == DifferentialChangeType::TYPE_COPY_HERE) {
|
||||
$meta = pht('Copied from %s', $changeset->getOldFile());
|
||||
}
|
||||
|
||||
$link = $this->renderChangesetLink($changeset, $ref, $display_file);
|
||||
|
||||
$line_count = $changeset->getAffectedLineCount();
|
||||
if ($line_count == 0) {
|
||||
$lines = '';
|
||||
} else {
|
||||
$lines = ' '.pht('(%d line(s))', $line_count);
|
||||
}
|
||||
|
||||
$char = DifferentialChangeType::getSummaryCharacterForChangeType($type);
|
||||
$chartitle = DifferentialChangeType::getFullNameForChangeType($type);
|
||||
$desc = DifferentialChangeType::getShortNameForFileType($ftype);
|
||||
$color = DifferentialChangeType::getSummaryColorForChangeType($type);
|
||||
if ($desc) {
|
||||
$desc = '('.$desc.')';
|
||||
}
|
||||
$pchar =
|
||||
($changeset->getOldProperties() === $changeset->getNewProperties())
|
||||
? ''
|
||||
: phutil_tag(
|
||||
'span',
|
||||
array('title' => pht('Properties Changed')),
|
||||
'M');
|
||||
|
||||
$fname = $changeset->getFilename();
|
||||
$cov = $this->renderCoverage($this->coverageMap, $fname);
|
||||
if ($cov === null) {
|
||||
$mcov = $cov = phutil_tag('em', array(), '-');
|
||||
} else {
|
||||
$mcov = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => 'differential-mcoverage-'.md5($fname),
|
||||
'class' => 'differential-mcoverage-loading',
|
||||
),
|
||||
(isset($this->visibleChangesets[$id]) ?
|
||||
pht('Loading...') : pht('?')));
|
||||
}
|
||||
|
||||
if ($meta) {
|
||||
$meta = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'differential-toc-meta',
|
||||
),
|
||||
$meta);
|
||||
}
|
||||
|
||||
if ($this->diff && $this->repository) {
|
||||
$paths[] =
|
||||
$changeset->getAbsoluteRepositoryPath($this->repository, $this->diff);
|
||||
}
|
||||
|
||||
$char = phutil_tag('span', array('class' => 'phui-text-'.$color), $char);
|
||||
|
||||
$rows[] = array(
|
||||
$char,
|
||||
$pchar,
|
||||
$desc,
|
||||
array($link, $lines, $meta),
|
||||
$cov,
|
||||
$mcov,
|
||||
);
|
||||
}
|
||||
|
||||
$editor_link = null;
|
||||
if ($paths && $this->user) {
|
||||
$editor_link = $this->user->loadEditorLink(
|
||||
$paths,
|
||||
1, // line number
|
||||
$this->repository->getCallsign());
|
||||
if ($editor_link) {
|
||||
$editor_link =
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $editor_link,
|
||||
'class' => 'button differential-toc-edit-all',
|
||||
),
|
||||
pht('Open All in Editor'));
|
||||
}
|
||||
}
|
||||
|
||||
$reveal_link = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'sigil' => 'differential-reveal-all',
|
||||
'mustcapture' => true,
|
||||
'class' => 'button differential-toc-reveal-all',
|
||||
),
|
||||
pht('Show All Context'));
|
||||
|
||||
$buttons = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'differential-toc-buttons grouped',
|
||||
),
|
||||
array(
|
||||
$editor_link,
|
||||
$reveal_link,
|
||||
));
|
||||
|
||||
$table = id(new AphrontTableView($rows));
|
||||
$table->setHeaders(
|
||||
array(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
pht('Path'),
|
||||
pht('Coverage (All)'),
|
||||
pht('Coverage (Touched)'),
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'differential-toc-char center',
|
||||
'differential-toc-prop center',
|
||||
'differential-toc-ftype center',
|
||||
'differential-toc-file wide',
|
||||
'differential-toc-cov',
|
||||
'differential-toc-cov',
|
||||
));
|
||||
$table->setDeviceVisibility(
|
||||
array(
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
$anchor = id(new PhabricatorAnchorView())
|
||||
->setAnchorName('toc')
|
||||
->setNavigationMarker(true);
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Table of Contents'))
|
||||
->setTable($table)
|
||||
->appendChild($anchor)
|
||||
->appendChild($buttons);
|
||||
}
|
||||
|
||||
private function renderRename($display_file, $other_file, $arrow) {
|
||||
$old = explode('/', $display_file);
|
||||
$new = explode('/', $other_file);
|
||||
|
||||
$start = count($old);
|
||||
foreach ($old as $index => $part) {
|
||||
if (!isset($new[$index]) || $part != $new[$index]) {
|
||||
$start = $index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$end = count($old);
|
||||
foreach (array_reverse($old) as $from_end => $part) {
|
||||
$index = count($new) - $from_end - 1;
|
||||
if (!isset($new[$index]) || $part != $new[$index]) {
|
||||
$end = $from_end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$rename =
|
||||
'{'.
|
||||
implode('/', array_slice($old, $start, count($old) - $end - $start)).
|
||||
' '.$arrow.' '.
|
||||
implode('/', array_slice($new, $start, count($new) - $end - $start)).
|
||||
'}';
|
||||
|
||||
array_splice($new, $start, count($new) - $end - $start, $rename);
|
||||
return implode('/', $new);
|
||||
}
|
||||
|
||||
private function renderCoverage(array $coverage, $file) {
|
||||
$info = idx($coverage, $file);
|
||||
if (!$info) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$not_covered = substr_count($info, 'U');
|
||||
$covered = substr_count($info, 'C');
|
||||
|
||||
if (!$not_covered && !$covered) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered)));
|
||||
}
|
||||
|
||||
|
||||
private function renderChangesetLink(
|
||||
DifferentialChangeset $changeset,
|
||||
$ref,
|
||||
$display_file) {
|
||||
|
||||
return javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '#'.$changeset->getAnchorName(),
|
||||
'sigil' => 'differential-load',
|
||||
'meta' => array(
|
||||
'id' => 'diff-'.$changeset->getAnchorName(),
|
||||
),
|
||||
),
|
||||
$display_file);
|
||||
}
|
||||
|
||||
}
|
|
@ -111,25 +111,6 @@ abstract class DiffusionBrowseController extends DiffusionController {
|
|||
->setIcon('fa-home')
|
||||
->setDisabled(!$behind_head));
|
||||
|
||||
// TODO: Ideally, this should live in Owners and be event-triggered, but
|
||||
// there's no reasonable object for it to react to right now.
|
||||
|
||||
$owners = 'PhabricatorOwnersApplication';
|
||||
if (PhabricatorApplication::isClassInstalled($owners)) {
|
||||
$owners_uri = id(new PhutilURI('/owners/view/search/'))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'repository' => $drequest->getCallsign(),
|
||||
'path' => '/'.$drequest->getPath(),
|
||||
));
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Find Owners'))
|
||||
->setHref((string)$owners_uri)
|
||||
->setIcon('fa-users'));
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
@ -137,7 +118,7 @@ abstract class DiffusionBrowseController extends DiffusionController {
|
|||
DiffusionRequest $drequest,
|
||||
PhabricatorActionListView $actions) {
|
||||
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
|
@ -180,6 +161,46 @@ abstract class DiffusionBrowseController extends DiffusionController {
|
|||
}
|
||||
}
|
||||
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
$owners = 'PhabricatorOwnersApplication';
|
||||
if (PhabricatorApplication::isClassInstalled($owners)) {
|
||||
$package_query = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
|
||||
->withControl(
|
||||
$repository->getPHID(),
|
||||
array(
|
||||
$drequest->getPath(),
|
||||
));
|
||||
|
||||
$package_query->execute();
|
||||
|
||||
$packages = $package_query->getControllingPackagesForPath(
|
||||
$repository->getPHID(),
|
||||
$drequest->getPath());
|
||||
|
||||
if ($packages) {
|
||||
$ownership = id(new PHUIStatusListView())
|
||||
->setUser($viewer);
|
||||
|
||||
foreach ($packages as $package) {
|
||||
$icon = 'fa-list-alt';
|
||||
$color = 'grey';
|
||||
|
||||
$item = id(new PHUIStatusItemView())
|
||||
->setIcon($icon, $color)
|
||||
->setTarget($viewer->renderHandle($package->getPHID()));
|
||||
|
||||
$ownership->addItem($item);
|
||||
}
|
||||
} else {
|
||||
$ownership = phutil_tag('em', array(), pht('None'));
|
||||
}
|
||||
|
||||
$view->addProperty(pht('Packages'), $ownership);
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
|
||||
$commit_data = $commit->getCommitData();
|
||||
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
|
||||
$changesets = null;
|
||||
if ($is_foreign) {
|
||||
$subpath = $commit_data->getCommitDetail('svn-subpath');
|
||||
|
||||
|
@ -181,24 +180,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$user,
|
||||
$this->auditAuthorityPHIDs);
|
||||
|
||||
$owners_paths = array();
|
||||
if ($highlighted_audits) {
|
||||
$packages = id(new PhabricatorOwnersPackage())->loadAllWhere(
|
||||
'phid IN (%Ls)',
|
||||
mpull($highlighted_audits, 'getAuditorPHID'));
|
||||
if ($packages) {
|
||||
$owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere(
|
||||
'repositoryPHID = %s AND packageID IN (%Ld)',
|
||||
$repository->getPHID(),
|
||||
mpull($packages, 'getID'));
|
||||
}
|
||||
}
|
||||
|
||||
$change_table = new DiffusionCommitChangeTableView();
|
||||
$change_table->setDiffusionRequest($drequest);
|
||||
$change_table->setPathChanges($changes);
|
||||
$change_table->setOwnersPaths($owners_paths);
|
||||
|
||||
$count = count($changes);
|
||||
|
||||
$bad_commit = null;
|
||||
|
@ -210,6 +191,7 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
'r'.$callsign.$commit->getCommitIdentifier());
|
||||
}
|
||||
|
||||
$show_changesets = false;
|
||||
if ($bad_commit) {
|
||||
$content[] = $this->renderStatusMessage(
|
||||
pht('Bad Commit'),
|
||||
|
@ -235,6 +217,8 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
'Changes are not shown.',
|
||||
$hard_limit));
|
||||
} else {
|
||||
$show_changesets = true;
|
||||
|
||||
// The user has clicked "Show All Changes", and we should show all the
|
||||
// changes inline even if there are more than the soft limit.
|
||||
$show_all_details = $request->getBool('show_all');
|
||||
|
@ -264,15 +248,20 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$header->addActionLink($button);
|
||||
}
|
||||
|
||||
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
|
||||
$user,
|
||||
$changes);
|
||||
|
||||
// TODO: This table and panel shouldn't really be separate, but we need
|
||||
// to clean up the "Load All Files" interaction first.
|
||||
$change_table = $this->buildTableOfContents(
|
||||
$changesets);
|
||||
|
||||
$change_panel->setTable($change_table);
|
||||
$change_panel->setHeader($header);
|
||||
|
||||
$content[] = $change_panel;
|
||||
|
||||
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
|
||||
$user,
|
||||
$changes);
|
||||
|
||||
$vcs = $repository->getVersionControlSystem();
|
||||
switch ($vcs) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
|
@ -353,12 +342,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$change_list->setInlineCommentControllerURI(
|
||||
'/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
|
||||
|
||||
$change_references = array();
|
||||
foreach ($changesets as $key => $changeset) {
|
||||
$change_references[$changeset->getID()] = $references[$key];
|
||||
}
|
||||
$change_table->setRenderingReferences($change_references);
|
||||
|
||||
$content[] = $change_list->render();
|
||||
}
|
||||
|
||||
|
@ -375,7 +358,7 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$show_filetree = $prefs->getPreference($pref_filetree);
|
||||
$collapsed = $prefs->getPreference($pref_collapse);
|
||||
|
||||
if ($changesets && $show_filetree) {
|
||||
if ($show_changesets && $show_filetree) {
|
||||
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
|
||||
->setTitle($short_name)
|
||||
->setBaseURI(new PhutilURI('/'.$commit_id))
|
||||
|
@ -1082,4 +1065,74 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
return $parser->processCorpus($corpus);
|
||||
}
|
||||
|
||||
private function buildTableOfContents(array $changesets) {
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$toc_view = id(new PHUIDiffTableOfContentsListView())
|
||||
->setUser($viewer);
|
||||
|
||||
// TODO: This is hacky, we just want access to the linkX() methods on
|
||||
// DiffusionView.
|
||||
$diffusion_view = id(new DiffusionEmptyResultView())
|
||||
->setDiffusionRequest($drequest);
|
||||
|
||||
$have_owners = PhabricatorApplication::isClassInstalledForViewer(
|
||||
'PhabricatorOwnersApplication',
|
||||
$viewer);
|
||||
|
||||
if (!$changesets) {
|
||||
$have_owners = false;
|
||||
}
|
||||
|
||||
if ($have_owners) {
|
||||
if ($viewer->getPHID()) {
|
||||
$packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
|
||||
->withAuthorityPHIDs(array($viewer->getPHID()))
|
||||
->execute();
|
||||
$toc_view->setAuthorityPackages($packages);
|
||||
}
|
||||
|
||||
$repository = $drequest->getRepository();
|
||||
$repository_phid = $repository->getPHID();
|
||||
|
||||
$control_query = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
|
||||
->withControl($repository_phid, mpull($changesets, 'getFilename'));
|
||||
$control_query->execute();
|
||||
}
|
||||
|
||||
foreach ($changesets as $changeset_id => $changeset) {
|
||||
$path = $changeset->getFilename();
|
||||
$anchor = substr(md5($path), 0, 8);
|
||||
|
||||
$history_link = $diffusion_view->linkHistory($path);
|
||||
$browse_link = $diffusion_view->linkBrowse($path);
|
||||
|
||||
$item = id(new PHUIDiffTableOfContentsItemView())
|
||||
->setChangeset($changeset)
|
||||
->setAnchor($anchor)
|
||||
->setContext(
|
||||
array(
|
||||
$history_link,
|
||||
' ',
|
||||
$browse_link,
|
||||
));
|
||||
|
||||
if ($have_owners) {
|
||||
$packages = $control_query->getControllingPackagesForPath(
|
||||
$repository_phid,
|
||||
$changeset->getFilename());
|
||||
$item->setPackages($packages);
|
||||
}
|
||||
|
||||
$toc_view->addItem($item);
|
||||
}
|
||||
|
||||
return $toc_view;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCommitChangeTableView extends DiffusionView {
|
||||
|
||||
private $pathChanges;
|
||||
private $ownersPaths = array();
|
||||
private $renderingReferences;
|
||||
|
||||
public function setPathChanges(array $path_changes) {
|
||||
assert_instances_of($path_changes, 'DiffusionPathChange');
|
||||
$this->pathChanges = $path_changes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setOwnersPaths(array $owners_paths) {
|
||||
assert_instances_of($owners_paths, 'PhabricatorOwnersPath');
|
||||
$this->ownersPaths = $owners_paths;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRenderingReferences(array $value) {
|
||||
$this->renderingReferences = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$rows = array();
|
||||
$rowc = array();
|
||||
|
||||
// TODO: Experiment with path stack rendering.
|
||||
|
||||
// TODO: Copy Away and Move Away are rendered junkily still.
|
||||
|
||||
foreach ($this->pathChanges as $id => $change) {
|
||||
$path = $change->getPath();
|
||||
$hash = substr(md5($path), 0, 8);
|
||||
if ($change->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
if (isset($this->renderingReferences[$id])) {
|
||||
$path_column = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '#'.$hash,
|
||||
'meta' => array(
|
||||
'id' => 'diff-'.$hash,
|
||||
'ref' => $this->renderingReferences[$id],
|
||||
),
|
||||
'sigil' => 'differential-load',
|
||||
),
|
||||
$path);
|
||||
} else {
|
||||
$path_column = $path;
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
$this->linkHistory($change->getPath()),
|
||||
$this->linkBrowse($change->getPath()),
|
||||
$this->linkChange(
|
||||
$change->getChangeType(),
|
||||
$change->getFileType(),
|
||||
$change->getPath()),
|
||||
$path_column,
|
||||
);
|
||||
|
||||
$row_class = null;
|
||||
foreach ($this->ownersPaths as $owners_path) {
|
||||
$excluded = $owners_path->getExcluded();
|
||||
$owners_path = $owners_path->getPath();
|
||||
if (strncmp('/'.$path, $owners_path, strlen($owners_path)) == 0) {
|
||||
if ($excluded) {
|
||||
$row_class = null;
|
||||
break;
|
||||
}
|
||||
$row_class = 'highlighted';
|
||||
}
|
||||
}
|
||||
$rowc[] = $row_class;
|
||||
}
|
||||
|
||||
$view = new AphrontTableView($rows);
|
||||
$view->setHeaders(
|
||||
array(
|
||||
pht('History'),
|
||||
pht('Browse'),
|
||||
pht('Change'),
|
||||
pht('Path'),
|
||||
));
|
||||
$view->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'wide',
|
||||
));
|
||||
$view->setRowClasses($rowc);
|
||||
$view->setNoDataString(pht('This change has not been fully parsed yet.'));
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
}
|
|
@ -27,19 +27,12 @@ final class DrydockLeaseQuery extends DrydockQuery {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new DrydockLease();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new DrydockLease();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT lease.* FROM %T lease %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($data);
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $leases) {
|
||||
|
@ -69,40 +62,38 @@ final class DrydockLeaseQuery extends DrydockQuery {
|
|||
return $leases;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->resourceIDs) {
|
||||
if ($this->resourceIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'resourceID IN (%Ld)',
|
||||
$this->resourceIDs);
|
||||
}
|
||||
|
||||
if ($this->ids) {
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids) {
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->statuses) {
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'status IN (%Ld)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
|
||||
abstract class HarbormasterArtifact extends Phobject {
|
||||
|
||||
private $buildArtifact;
|
||||
|
||||
abstract public function getArtifactTypeName();
|
||||
|
||||
public function getArtifactTypeSummary() {
|
||||
return $this->getArtifactTypeDescription();
|
||||
}
|
||||
|
||||
abstract public function getArtifactTypeDescription();
|
||||
abstract public function getArtifactParameterSpecification();
|
||||
abstract public function getArtifactParameterDescriptions();
|
||||
abstract public function willCreateArtifact(PhabricatorUser $actor);
|
||||
|
||||
public function validateArtifactData(array $artifact_data) {
|
||||
$artifact_spec = $this->getArtifactParameterSpecification();
|
||||
PhutilTypeSpec::checkMap($artifact_data, $artifact_spec);
|
||||
}
|
||||
|
||||
public function renderArtifactSummary(PhabricatorUser $viewer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function releaseArtifact(PhabricatorUser $actor) {
|
||||
return;
|
||||
}
|
||||
|
||||
public function getArtifactDataExample() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setBuildArtifact(HarbormasterBuildArtifact $build_artifact) {
|
||||
$this->buildArtifact = $build_artifact;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBuildArtifact() {
|
||||
return $this->buildArtifact;
|
||||
}
|
||||
|
||||
final public function getArtifactConstant() {
|
||||
$class = new ReflectionClass($this);
|
||||
|
||||
$const = $class->getConstant('ARTIFACTCONST');
|
||||
if ($const === false) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'"%s" class "%s" must define a "%s" property.',
|
||||
__CLASS__,
|
||||
get_class($this),
|
||||
'ARTIFACTCONST'));
|
||||
}
|
||||
|
||||
$limit = self::getArtifactConstantByteLimit();
|
||||
if (!is_string($const) || (strlen($const) > $limit)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'"%s" class "%s" has an invalid "%s" property. Action constants '.
|
||||
'must be strings and no more than %s bytes in length.',
|
||||
__CLASS__,
|
||||
get_class($this),
|
||||
'ARTIFACTCONST',
|
||||
new PhutilNumber($limit)));
|
||||
}
|
||||
|
||||
return $const;
|
||||
}
|
||||
|
||||
final public static function getArtifactConstantByteLimit() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
final public static function getAllArtifactTypes() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getArtifactConstant')
|
||||
->execute();
|
||||
}
|
||||
|
||||
final public static function getArtifactType($type) {
|
||||
return idx(self::getAllArtifactTypes(), $type);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterFileArtifact extends HarbormasterArtifact {
|
||||
|
||||
const ARTIFACTCONST = 'file';
|
||||
|
||||
public function getArtifactTypeName() {
|
||||
return pht('File');
|
||||
}
|
||||
|
||||
public function getArtifactTypeDescription() {
|
||||
return pht(
|
||||
'Stores a reference to file data which has been uploaded to '.
|
||||
'Phabricator.');
|
||||
}
|
||||
|
||||
public function getArtifactParameterSpecification() {
|
||||
return array(
|
||||
'filePHID' => 'string',
|
||||
);
|
||||
}
|
||||
|
||||
public function getArtifactParameterDescriptions() {
|
||||
return array(
|
||||
'filePHID' => pht('File to create an artifact from.'),
|
||||
);
|
||||
}
|
||||
|
||||
public function getArtifactDataExample() {
|
||||
return array(
|
||||
'filePHID' => 'PHID-FILE-abcdefghijklmnopqrst',
|
||||
);
|
||||
}
|
||||
|
||||
public function renderArtifactSummary(PhabricatorUser $viewer) {
|
||||
$artifact = $this->getBuildArtifact();
|
||||
$file_phid = $artifact->getProperty('filePHID');
|
||||
return $viewer->renderHandle($file_phid);
|
||||
}
|
||||
|
||||
public function willCreateArtifact(PhabricatorUser $actor) {
|
||||
// NOTE: This is primarily making sure the actor has permission to view the
|
||||
// file. We don't want to let you run builds using files you don't have
|
||||
// permission to see, since this could let you violate permissions.
|
||||
$this->loadArtifactFile($actor);
|
||||
}
|
||||
|
||||
public function loadArtifactFile(PhabricatorUser $viewer) {
|
||||
$artifact = $this->getBuildArtifact();
|
||||
$file_phid = $artifact->getProperty('filePHID');
|
||||
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($file_phid))
|
||||
->executeOne();
|
||||
if (!$file) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'File PHID "%s" does not correspond to a valid file.',
|
||||
$file_phid));
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterHostArtifact extends HarbormasterArtifact {
|
||||
|
||||
const ARTIFACTCONST = 'host';
|
||||
|
||||
public function getArtifactTypeName() {
|
||||
return pht('Drydock Host');
|
||||
}
|
||||
|
||||
public function getArtifactTypeDescription() {
|
||||
return pht('References a host lease from Drydock.');
|
||||
}
|
||||
|
||||
|
||||
public function getArtifactParameterSpecification() {
|
||||
return array(
|
||||
'drydockLeasePHID' => 'string',
|
||||
);
|
||||
}
|
||||
|
||||
public function getArtifactParameterDescriptions() {
|
||||
return array(
|
||||
'drydockLeasePHID' => pht(
|
||||
'Drydock host lease to create an artifact from.'),
|
||||
);
|
||||
}
|
||||
|
||||
public function getArtifactDataExample() {
|
||||
return array(
|
||||
'drydockLeasePHID' => 'PHID-DRYL-abcdefghijklmnopqrst',
|
||||
);
|
||||
}
|
||||
|
||||
public function renderArtifactSummary(PhabricatorUser $viewer) {
|
||||
$artifact = $this->getBuildArtifact();
|
||||
$file_phid = $artifact->getProperty('drydockLeasePHID');
|
||||
return $viewer->renderHandle($file_phid);
|
||||
}
|
||||
|
||||
public function willCreateArtifact(PhabricatorUser $actor) {
|
||||
$this->loadArtifactLease($actor);
|
||||
}
|
||||
|
||||
public function loadArtifactLease(PhabricatorUser $viewer) {
|
||||
$artifact = $this->getBuildArtifact();
|
||||
$lease_phid = $artifact->getProperty('drydockLeasePHID');
|
||||
|
||||
$lease = id(new DrydockLeaseQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($lease_phid))
|
||||
->executeOne();
|
||||
if (!$lease) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Drydock lease PHID "%s" does not correspond to a valid lease.',
|
||||
$lease_phid));
|
||||
}
|
||||
|
||||
return $lease;
|
||||
}
|
||||
|
||||
public function releaseArtifact(PhabricatorUser $actor) {
|
||||
$lease = $this->loadArtifactLease($actor);
|
||||
$resource = $lease->getResource();
|
||||
$blueprint = $resource->getBlueprint();
|
||||
|
||||
if ($lease->isActive()) {
|
||||
$blueprint->releaseLease($resource, $lease);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterURIArtifact extends HarbormasterArtifact {
|
||||
|
||||
const ARTIFACTCONST = 'uri';
|
||||
|
||||
public function getArtifactTypeName() {
|
||||
return pht('URI');
|
||||
}
|
||||
|
||||
public function getArtifactTypeSummary() {
|
||||
return pht('Stores a URI.');
|
||||
}
|
||||
|
||||
public function getArtifactTypeDescription() {
|
||||
return pht(
|
||||
"Stores a URI.\n\n".
|
||||
"With `ui.external`, you can use this artifact type to add links to ".
|
||||
"build results in an external build system.");
|
||||
}
|
||||
|
||||
public function getArtifactParameterSpecification() {
|
||||
return array(
|
||||
'uri' => 'string',
|
||||
'name' => 'optional string',
|
||||
'ui.external' => 'optional bool',
|
||||
);
|
||||
}
|
||||
|
||||
public function getArtifactParameterDescriptions() {
|
||||
return array(
|
||||
'uri' => pht('The URI to store.'),
|
||||
'name' => pht('Optional label for this URI.'),
|
||||
'ui.external' => pht(
|
||||
'If true, display this URI in the UI as an link to '.
|
||||
'additional build details in an external build system.'),
|
||||
);
|
||||
}
|
||||
|
||||
public function getArtifactDataExample() {
|
||||
return array(
|
||||
'uri' => 'https://buildserver.mycompany.com/build/123/',
|
||||
'name' => pht('View External Build Results'),
|
||||
'ui.external' => true,
|
||||
);
|
||||
}
|
||||
|
||||
public function renderArtifactSummary(PhabricatorUser $viewer) {
|
||||
return $this->renderLink();
|
||||
}
|
||||
|
||||
public function isExternalLink() {
|
||||
$artifact = $this->getBuildArtifact();
|
||||
return (bool)$artifact->getProperty('ui.external', false);
|
||||
}
|
||||
|
||||
public function renderLink() {
|
||||
$artifact = $this->getBuildArtifact();
|
||||
$uri = $artifact->getProperty('uri');
|
||||
|
||||
try {
|
||||
$this->validateURI($uri);
|
||||
} catch (Exception $ex) {
|
||||
return pht('<Invalid URI>');
|
||||
}
|
||||
|
||||
$name = $artifact->getProperty('name', $uri);
|
||||
|
||||
return phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $uri,
|
||||
'target' => '_blank',
|
||||
),
|
||||
$name);
|
||||
}
|
||||
|
||||
public function willCreateArtifact(PhabricatorUser $actor) {
|
||||
$artifact = $this->getBuildArtifact();
|
||||
$uri = $artifact->getProperty('uri');
|
||||
$this->validateURI($uri);
|
||||
}
|
||||
|
||||
private function validateURI($raw_uri) {
|
||||
$uri = new PhutilURI($raw_uri);
|
||||
|
||||
$protocol = $uri->getProtocol();
|
||||
if (!strlen($protocol)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to identify the protocol for URI "%s". URIs must be '.
|
||||
'fully qualified and have an identifiable protocol.',
|
||||
$raw_uri));
|
||||
}
|
||||
|
||||
$protocol_key = 'uri.allowed-protocols';
|
||||
$protocols = PhabricatorEnv::getEnvConfig($protocol_key);
|
||||
if (empty($protocols[$protocol])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'URI "%s" does not have an allowable protocol. Configure '.
|
||||
'protocols in `%s`. Allowed protocols are: %s.',
|
||||
$raw_uri,
|
||||
$protocol_key,
|
||||
implode(', ', array_keys($protocols))));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -15,4 +15,16 @@ abstract class HarbormasterConduitAPIMethod extends ConduitAPIMethod {
|
|||
return pht('All Harbormaster APIs are new and subject to change.');
|
||||
}
|
||||
|
||||
protected function returnArtifactList(array $artifacts) {
|
||||
$list = array();
|
||||
|
||||
foreach ($artifacts as $artifact) {
|
||||
$list[] = array(
|
||||
'phid' => $artifact->getPHID(),
|
||||
);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterCreateArtifactConduitAPIMethod
|
||||
extends HarbormasterConduitAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'harbormaster.createartifact';
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht('Create a build artifact.');
|
||||
}
|
||||
|
||||
public function getMethodDescription() {
|
||||
$types = HarbormasterArtifact::getAllArtifactTypes();
|
||||
$types = msort($types, 'getArtifactTypeName');
|
||||
|
||||
$head_key = pht('Key');
|
||||
$head_type = pht('Type');
|
||||
$head_desc = pht('Description');
|
||||
$head_atype = pht('Artifact Type');
|
||||
$head_name = pht('Name');
|
||||
$head_summary = pht('Summary');
|
||||
|
||||
$out = array();
|
||||
$out[] = pht(
|
||||
'Use this method to attach artifacts to build targets while running '.
|
||||
'builds. Artifacts can be used to carry data through a complex build '.
|
||||
'workflow, provide extra information to users, or store build results.');
|
||||
$out[] = null;
|
||||
$out[] = pht(
|
||||
'When creating an artifact, you will choose an `artifactType` from '.
|
||||
'this table. These types of artifacts are supported:');
|
||||
|
||||
$out[] = "| {$head_atype} | {$head_name} | {$head_summary} |";
|
||||
$out[] = '|-------------|--------------|--------------|';
|
||||
foreach ($types as $type) {
|
||||
$type_name = $type->getArtifactTypeName();
|
||||
$type_const = $type->getArtifactConstant();
|
||||
$type_summary = $type->getArtifactTypeSummary();
|
||||
$out[] = "| `{$type_const}` | **{$type_name}** | {$type_summary} |";
|
||||
}
|
||||
|
||||
$out[] = null;
|
||||
$out[] = pht(
|
||||
'Each artifact also needs an `artifactKey`, which names the artifact. '.
|
||||
'Finally, you will provide some `artifactData` to fill in the content '.
|
||||
'of the artifact. The data you provide depends on what type of artifact '.
|
||||
'you are creating.');
|
||||
|
||||
foreach ($types as $type) {
|
||||
$type_name = $type->getArtifactTypeName();
|
||||
$type_const = $type->getArtifactConstant();
|
||||
|
||||
$out[] = $type_name;
|
||||
$out[] = '--------------------------';
|
||||
$out[] = null;
|
||||
$out[] = $type->getArtifactTypeDescription();
|
||||
$out[] = null;
|
||||
$out[] = pht(
|
||||
'Create an artifact of this type by passing `%s` as the '.
|
||||
'`artifactType`. When creating an artifact of this type, provide '.
|
||||
'these parameters as a dictionary to `artifactData`:',
|
||||
$type_const);
|
||||
|
||||
$spec = $type->getArtifactParameterSpecification();
|
||||
$desc = $type->getArtifactParameterDescriptions();
|
||||
$out[] = "| {$head_key} | {$head_type} | {$head_desc} |";
|
||||
$out[] = '|-------------|--------------|--------------|';
|
||||
foreach ($spec as $key => $key_type) {
|
||||
$key_desc = idx($desc, $key);
|
||||
$out[] = "| `{$key}` | //{$key_type}// | {$key_desc} |";
|
||||
}
|
||||
|
||||
$example = $type->getArtifactDataExample();
|
||||
if ($example !== null) {
|
||||
$json = new PhutilJSON();
|
||||
$rendered = $json->encodeFormatted($example);
|
||||
|
||||
$out[] = pht('For example:');
|
||||
$out[] = '```lang=json';
|
||||
$out[] = $rendered;
|
||||
$out[] = '```';
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", $out);
|
||||
}
|
||||
|
||||
protected function defineParamTypes() {
|
||||
return array(
|
||||
'buildTargetPHID' => 'phid',
|
||||
'artifactKey' => 'string',
|
||||
'artifactType' => 'string',
|
||||
'artifactData' => 'map<string, wild>',
|
||||
);
|
||||
}
|
||||
|
||||
protected function defineReturnType() {
|
||||
return 'wild';
|
||||
}
|
||||
|
||||
protected function execute(ConduitAPIRequest $request) {
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$build_target_phid = $request->getValue('buildTargetPHID');
|
||||
$build_target = id(new HarbormasterBuildTargetQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($build_target_phid))
|
||||
->executeOne();
|
||||
if (!$build_target) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'No such build target "%s"!',
|
||||
$build_target_phid));
|
||||
}
|
||||
|
||||
$artifact = $build_target->createArtifact(
|
||||
$viewer,
|
||||
$request->getValue('artifactKey'),
|
||||
$request->getValue('artifactType'),
|
||||
$request->getValue('artifactData'));
|
||||
|
||||
return array(
|
||||
'data' => $this->returnArtifactList(array($artifact)),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,17 +3,11 @@
|
|||
final class HarbormasterBuildViewController
|
||||
extends HarbormasterController {
|
||||
|
||||
private $id;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = $data['id'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$id = $this->id;
|
||||
$id = $request->getURIData('id');
|
||||
$generation = $request->getInt('g');
|
||||
|
||||
$build = id(new HarbormasterBuildQuery())
|
||||
|
@ -73,6 +67,18 @@ final class HarbormasterBuildViewController
|
|||
$messages = array();
|
||||
}
|
||||
|
||||
if ($build_targets) {
|
||||
$artifacts = id(new HarbormasterBuildArtifactQuery())
|
||||
->setViewer($viewer)
|
||||
->withBuildTargetPHIDs(mpull($build_targets, 'getPHID'))
|
||||
->execute();
|
||||
$artifacts = msort($artifacts, 'getArtifactKey');
|
||||
$artifacts = mgroup($artifacts, 'getBuildTargetPHID');
|
||||
} else {
|
||||
$artifacts = array();
|
||||
}
|
||||
|
||||
|
||||
$targets = array();
|
||||
foreach ($build_targets as $build_target) {
|
||||
$header = id(new PHUIHeaderView())
|
||||
|
@ -83,6 +89,27 @@ final class HarbormasterBuildViewController
|
|||
->setHeader($header);
|
||||
|
||||
$properties = new PHUIPropertyListView();
|
||||
|
||||
$target_artifacts = idx($artifacts, $build_target->getPHID(), array());
|
||||
|
||||
$links = array();
|
||||
$type_uri = HarbormasterURIArtifact::ARTIFACTCONST;
|
||||
foreach ($target_artifacts as $artifact) {
|
||||
if ($artifact->getArtifactType() == $type_uri) {
|
||||
$impl = $artifact->getArtifactImplementation();
|
||||
if ($impl->isExternalLink()) {
|
||||
$links[] = $impl->renderLink();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($links) {
|
||||
$links = phutil_implode_html(phutil_tag('br'), $links);
|
||||
$properties->addProperty(
|
||||
pht('External Link'),
|
||||
$links);
|
||||
}
|
||||
|
||||
$status_view = new PHUIStatusListView();
|
||||
|
||||
$item = new PHUIStatusItemView();
|
||||
|
@ -183,9 +210,9 @@ final class HarbormasterBuildViewController
|
|||
$properties->addRawContent($this->buildProperties($variables));
|
||||
$target_box->addPropertyList($properties, pht('Variables'));
|
||||
|
||||
$artifacts = $this->buildArtifacts($build_target);
|
||||
$artifacts_tab = $this->buildArtifacts($build_target, $target_artifacts);
|
||||
$properties = new PHUIPropertyListView();
|
||||
$properties->addRawContent($artifacts);
|
||||
$properties->addRawContent($artifacts_tab);
|
||||
$target_box->addPropertyList($properties, pht('Artifacts'));
|
||||
|
||||
$build_messages = idx($messages, $build_target->getPHID(), array());
|
||||
|
@ -225,28 +252,45 @@ final class HarbormasterBuildViewController
|
|||
}
|
||||
|
||||
private function buildArtifacts(
|
||||
HarbormasterBuildTarget $build_target) {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$artifacts = id(new HarbormasterBuildArtifactQuery())
|
||||
->setViewer($viewer)
|
||||
->withBuildTargetPHIDs(array($build_target->getPHID()))
|
||||
->execute();
|
||||
|
||||
$list = id(new PHUIObjectItemListView())
|
||||
->setNoDataString(pht('This target has no associated artifacts.'))
|
||||
->setFlush(true);
|
||||
HarbormasterBuildTarget $build_target,
|
||||
array $artifacts) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$rows = array();
|
||||
foreach ($artifacts as $artifact) {
|
||||
$item = $artifact->getObjectItemView($viewer);
|
||||
if ($item !== null) {
|
||||
$list->addItem($item);
|
||||
$impl = $artifact->getArtifactImplementation();
|
||||
|
||||
if ($impl) {
|
||||
$summary = $impl->renderArtifactSummary($viewer);
|
||||
$type_name = $impl->getArtifactTypeName();
|
||||
} else {
|
||||
$summary = pht('<Unknown Artifact Type>');
|
||||
$type_name = $artifact->getType();
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
$artifact->getArtifactKey(),
|
||||
$type_name,
|
||||
$summary,
|
||||
);
|
||||
}
|
||||
|
||||
return $list;
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setNoDataString(pht('This target has no associated artifacts.'))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Key'),
|
||||
pht('Type'),
|
||||
pht('Summary'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'pri',
|
||||
'',
|
||||
'wide',
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
private function buildLog(
|
||||
|
|
|
@ -483,7 +483,7 @@ final class HarbormasterBuildEngine extends Phobject {
|
|||
->execute();
|
||||
|
||||
foreach ($artifacts as $artifact) {
|
||||
$artifact->release();
|
||||
$artifact->releaseArtifact();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ final class HarbormasterUIEventListener
|
|||
}
|
||||
|
||||
private function handlePropertyEvent($ui_event) {
|
||||
$user = $ui_event->getUser();
|
||||
$viewer = $ui_event->getUser();
|
||||
$object = $ui_event->getValue('object');
|
||||
|
||||
if (!$object || !$object->getPHID()) {
|
||||
|
@ -52,10 +52,11 @@ final class HarbormasterUIEventListener
|
|||
}
|
||||
|
||||
$buildable = id(new HarbormasterBuildableQuery())
|
||||
->setViewer($user)
|
||||
->setViewer($viewer)
|
||||
->withManualBuildables(false)
|
||||
->withBuildablePHIDs(array($buildable_phid))
|
||||
->needBuilds(true)
|
||||
->needTargets(true)
|
||||
->executeOne();
|
||||
if (!$buildable) {
|
||||
return;
|
||||
|
@ -63,10 +64,26 @@ final class HarbormasterUIEventListener
|
|||
|
||||
$builds = $buildable->getBuilds();
|
||||
|
||||
$build_handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($user)
|
||||
->withPHIDs(mpull($builds, 'getPHID'))
|
||||
->execute();
|
||||
$targets = array();
|
||||
foreach ($builds as $build) {
|
||||
foreach ($build->getBuildTargets() as $target) {
|
||||
$targets[] = $target;
|
||||
}
|
||||
}
|
||||
|
||||
if ($targets) {
|
||||
$artifacts = id(new HarbormasterBuildArtifactQuery())
|
||||
->setViewer($viewer)
|
||||
->withBuildTargetPHIDs(mpull($targets, 'getPHID'))
|
||||
->withArtifactTypes(
|
||||
array(
|
||||
HarbormasterURIArtifact::ARTIFACTCONST,
|
||||
))
|
||||
->execute();
|
||||
$artifacts = mgroup($artifacts, 'getBuildTargetPHID');
|
||||
} else {
|
||||
$artifacts = array();
|
||||
}
|
||||
|
||||
$status_view = new PHUIStatusListView();
|
||||
|
||||
|
@ -87,6 +104,7 @@ final class HarbormasterUIEventListener
|
|||
|
||||
$target = phutil_tag('strong', array(), $target);
|
||||
|
||||
|
||||
$status_view
|
||||
->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
|
@ -95,7 +113,23 @@ final class HarbormasterUIEventListener
|
|||
|
||||
foreach ($builds as $build) {
|
||||
$item = new PHUIStatusItemView();
|
||||
$item->setTarget($build_handles[$build->getPHID()]->renderLink());
|
||||
$item->setTarget($viewer->renderHandle($build->getPHID()));
|
||||
|
||||
$links = array();
|
||||
foreach ($build->getBuildTargets() as $build_target) {
|
||||
$uris = idx($artifacts, $build_target->getPHID(), array());
|
||||
foreach ($uris as $uri) {
|
||||
$impl = $uri->getArtifactImplementation();
|
||||
if ($impl->isExternalLink()) {
|
||||
$links[] = $impl->renderLink();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($links) {
|
||||
$links = phutil_implode_html(" \xC2\xB7 ", $links);
|
||||
$item->setNote($links);
|
||||
}
|
||||
|
||||
$status = $build->getBuildStatus();
|
||||
$status_name = HarbormasterBuild::getBuildStatusName($status);
|
||||
|
@ -104,7 +138,6 @@ final class HarbormasterUIEventListener
|
|||
|
||||
$item->setIcon($icon, $color, $status_name);
|
||||
|
||||
|
||||
$status_view->addItem($item);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildArtifactPHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'HMBA';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Build Artifact');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new HarbormasterBuildArtifact();
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new HarbormasterBuildArtifactQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$artifact = $objects[$phid];
|
||||
$artifact_id = $artifact->getID();
|
||||
$handle->setName(pht('Build Artifact %d', $artifact_id));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@ final class HarbormasterBuildArtifactQuery
|
|||
private $ids;
|
||||
private $buildTargetPHIDs;
|
||||
private $artifactTypes;
|
||||
private $artifactKeys;
|
||||
private $artifactIndexes;
|
||||
private $keyBuildPHID;
|
||||
private $keyBuildGeneration;
|
||||
|
||||
|
@ -25,29 +25,17 @@ final class HarbormasterBuildArtifactQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withArtifactKeys(
|
||||
$build_phid,
|
||||
$build_gen,
|
||||
array $artifact_keys) {
|
||||
$this->keyBuildPHID = $build_phid;
|
||||
$this->keyBuildGeneration = $build_gen;
|
||||
$this->artifactKeys = $artifact_keys;
|
||||
public function withArtifactIndexes(array $artifact_indexes) {
|
||||
$this->artifactIndexes = $artifact_indexes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new HarbormasterBuildArtifact();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new HarbormasterBuildArtifact();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($data);
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $page) {
|
||||
|
@ -75,46 +63,38 @@ final class HarbormasterBuildArtifactQuery
|
|||
return $page;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids) {
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->buildTargetPHIDs) {
|
||||
if ($this->buildTargetPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'buildTargetPHID IN (%Ls)',
|
||||
$this->buildTargetPHIDs);
|
||||
}
|
||||
|
||||
if ($this->artifactTypes) {
|
||||
if ($this->artifactTypes !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'artifactType in (%Ls)',
|
||||
$this->artifactTypes);
|
||||
}
|
||||
|
||||
if ($this->artifactKeys) {
|
||||
$indexes = array();
|
||||
foreach ($this->artifactKeys as $key) {
|
||||
$indexes[] = PhabricatorHash::digestForIndex(
|
||||
$this->keyBuildPHID.$this->keyBuildGeneration.$key);
|
||||
}
|
||||
|
||||
if ($this->artifactIndexes !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'artifactIndex IN (%Ls)',
|
||||
$indexes);
|
||||
$this->artifactIndexes);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
|
|
|
@ -40,19 +40,12 @@ final class HarbormasterBuildQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new HarbormasterBuild();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new HarbormasterBuild();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($data);
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $page) {
|
||||
|
@ -136,47 +129,45 @@ final class HarbormasterBuildQuery
|
|||
return $page;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'phid in (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->buildStatuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'buildStatus in (%Ls)',
|
||||
$this->buildStatuses);
|
||||
}
|
||||
|
||||
if ($this->buildablePHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'buildablePHID IN (%Ls)',
|
||||
$this->buildablePHIDs);
|
||||
}
|
||||
|
||||
if ($this->buildPlanPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'buildPlanPHID IN (%Ls)',
|
||||
$this->buildPlanPHIDs);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
|
|
|
@ -34,55 +34,46 @@ final class HarbormasterBuildTargetQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new HarbormasterBuildTarget();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($data);
|
||||
public function newResultObject() {
|
||||
return new HarbormasterBuildTarget();
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
if ($this->ids) {
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids) {
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'phid in (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->buildPHIDs) {
|
||||
if ($this->buildPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'buildPHID in (%Ls)',
|
||||
$this->buildPHIDs);
|
||||
}
|
||||
|
||||
if ($this->buildGenerations) {
|
||||
if ($this->buildGenerations !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'buildGeneration in (%Ld)',
|
||||
$this->buildGenerations);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function didFilterPage(array $page) {
|
||||
|
|
|
@ -39,13 +39,14 @@ final class HarbormasterCommandBuildStepImplementation
|
|||
public function execute(
|
||||
HarbormasterBuild $build,
|
||||
HarbormasterBuildTarget $build_target) {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
$settings = $this->getSettings();
|
||||
$variables = $build_target->getVariables();
|
||||
|
||||
$artifact = $build->loadArtifact($settings['hostartifact']);
|
||||
|
||||
$lease = $artifact->loadDrydockLease();
|
||||
$artifact = $build_target->loadArtifact($settings['hostartifact']);
|
||||
$impl = $artifact->getArtifactImplementation();
|
||||
$lease = $impl->loadArtifactLease($viewer);
|
||||
|
||||
$this->platform = $lease->getAttribute('platform');
|
||||
|
||||
|
@ -120,9 +121,9 @@ final class HarbormasterCommandBuildStepImplementation
|
|||
public function getArtifactInputs() {
|
||||
return array(
|
||||
array(
|
||||
'name' => pht('Run on Host'),
|
||||
'key' => $this->getSetting('hostartifact'),
|
||||
'type' => HarbormasterBuildArtifact::TYPE_HOST,
|
||||
'name' => pht('Run on Host'),
|
||||
'key' => $this->getSetting('hostartifact'),
|
||||
'type' => HarbormasterHostArtifact::ARTIFACTCONST,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,14 +36,13 @@ final class HarbormasterLeaseHostBuildStepImplementation
|
|||
$lease->waitUntilActive();
|
||||
|
||||
// Create the associated artifact.
|
||||
$artifact = $build->createArtifact(
|
||||
$build_target,
|
||||
$artifact = $build_target->createArtifact(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$settings['name'],
|
||||
HarbormasterBuildArtifact::TYPE_HOST);
|
||||
$artifact->setArtifactData(array(
|
||||
'drydock-lease' => $lease->getID(),
|
||||
));
|
||||
$artifact->save();
|
||||
HarbormasterHostArtifact::ARTIFACTCONST,
|
||||
array(
|
||||
'drydockLeasePHID' => $lease->getPHID(),
|
||||
));
|
||||
}
|
||||
|
||||
public function getArtifactOutputs() {
|
||||
|
@ -51,7 +50,7 @@ final class HarbormasterLeaseHostBuildStepImplementation
|
|||
array(
|
||||
'name' => pht('Leased Host'),
|
||||
'key' => $this->getSetting('name'),
|
||||
'type' => HarbormasterBuildArtifact::TYPE_HOST,
|
||||
'type' => HarbormasterHostArtifact::ARTIFACTCONST,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,33 +29,34 @@ final class HarbormasterPublishFragmentBuildStepImplementation
|
|||
|
||||
$settings = $this->getSettings();
|
||||
$variables = $build_target->getVariables();
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
$path = $this->mergeVariables(
|
||||
'vsprintf',
|
||||
$settings['path'],
|
||||
$variables);
|
||||
|
||||
$artifact = $build->loadArtifact($settings['artifact']);
|
||||
|
||||
$file = $artifact->loadPhabricatorFile();
|
||||
$artifact = $build_target->loadArtifact($settings['artifact']);
|
||||
$impl = $artifact->getArtifactImplementation();
|
||||
$file = $impl->loadArtifactFile($viewer);
|
||||
|
||||
$fragment = id(new PhragmentFragmentQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->setViewer($viewer)
|
||||
->withPaths(array($path))
|
||||
->executeOne();
|
||||
|
||||
if ($fragment === null) {
|
||||
PhragmentFragment::createFromFile(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$viewer,
|
||||
$file,
|
||||
$path,
|
||||
PhabricatorPolicies::getMostOpenPolicy(),
|
||||
PhabricatorPolicies::POLICY_USER);
|
||||
} else {
|
||||
if ($file->getMimeType() === 'application/zip') {
|
||||
$fragment->updateFromZIP(PhabricatorUser::getOmnipotentUser(), $file);
|
||||
$fragment->updateFromZIP($viewer, $file);
|
||||
} else {
|
||||
$fragment->updateFromFile(PhabricatorUser::getOmnipotentUser(), $file);
|
||||
$fragment->updateFromFile($viewer, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +66,7 @@ final class HarbormasterPublishFragmentBuildStepImplementation
|
|||
array(
|
||||
'name' => pht('Publishes File'),
|
||||
'key' => $this->getSetting('artifact'),
|
||||
'type' => HarbormasterBuildArtifact::TYPE_FILE,
|
||||
'type' => HarbormasterFileArtifact::ARTIFACTCONST,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ final class HarbormasterUploadArtifactBuildStepImplementation
|
|||
public function execute(
|
||||
HarbormasterBuild $build,
|
||||
HarbormasterBuildTarget $build_target) {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
$settings = $this->getSettings();
|
||||
$variables = $build_target->getVariables();
|
||||
|
@ -34,9 +35,9 @@ final class HarbormasterUploadArtifactBuildStepImplementation
|
|||
$settings['path'],
|
||||
$variables);
|
||||
|
||||
$artifact = $build->loadArtifact($settings['hostartifact']);
|
||||
|
||||
$lease = $artifact->loadDrydockLease();
|
||||
$artifact = $build_target->loadArtifact($settings['hostartifact']);
|
||||
$impl = $artifact->getArtifactImplementation();
|
||||
$lease = $impl->loadArtifactLease($viewer);
|
||||
|
||||
$interface = $lease->getInterface('filesystem');
|
||||
|
||||
|
@ -44,14 +45,13 @@ final class HarbormasterUploadArtifactBuildStepImplementation
|
|||
$file = $interface->saveFile($path, $settings['name']);
|
||||
|
||||
// Insert the artifact record.
|
||||
$artifact = $build->createArtifact(
|
||||
$build_target,
|
||||
$artifact = $build_target->createArtifact(
|
||||
$viewer,
|
||||
$settings['name'],
|
||||
HarbormasterBuildArtifact::TYPE_FILE);
|
||||
$artifact->setArtifactData(array(
|
||||
'filePHID' => $file->getPHID(),
|
||||
));
|
||||
$artifact->save();
|
||||
HarbormasterFileArtifact::ARTIFACTCONST,
|
||||
array(
|
||||
'filePHID' => $file->getPHID(),
|
||||
));
|
||||
}
|
||||
|
||||
public function getArtifactInputs() {
|
||||
|
@ -59,7 +59,7 @@ final class HarbormasterUploadArtifactBuildStepImplementation
|
|||
array(
|
||||
'name' => pht('Upload From Host'),
|
||||
'key' => $this->getSetting('hostartifact'),
|
||||
'type' => HarbormasterBuildArtifact::TYPE_HOST,
|
||||
'type' => HarbormasterHostArtifact::ARTIFACTCONST,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ final class HarbormasterUploadArtifactBuildStepImplementation
|
|||
array(
|
||||
'name' => pht('Uploaded File'),
|
||||
'key' => $this->getSetting('name'),
|
||||
'type' => HarbormasterBuildArtifact::TYPE_FILE,
|
||||
'type' => HarbormasterHostArtifact::ARTIFACTCONST,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -235,36 +235,6 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
return $log;
|
||||
}
|
||||
|
||||
public function createArtifact(
|
||||
HarbormasterBuildTarget $build_target,
|
||||
$artifact_key,
|
||||
$artifact_type) {
|
||||
|
||||
$artifact =
|
||||
HarbormasterBuildArtifact::initializeNewBuildArtifact($build_target);
|
||||
$artifact->setArtifactKey(
|
||||
$this->getPHID(),
|
||||
$this->getBuildGeneration(),
|
||||
$artifact_key);
|
||||
$artifact->setArtifactType($artifact_type);
|
||||
$artifact->save();
|
||||
return $artifact;
|
||||
}
|
||||
|
||||
public function loadArtifact($name) {
|
||||
$artifact = id(new HarbormasterBuildArtifactQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withArtifactKeys(
|
||||
$this->getPHID(),
|
||||
$this->getBuildGeneration(),
|
||||
array($name))
|
||||
->executeOne();
|
||||
if ($artifact === null) {
|
||||
throw new Exception(pht('Artifact not found!'));
|
||||
}
|
||||
return $artifact;
|
||||
}
|
||||
|
||||
public function retrieveVariablesFromBuild() {
|
||||
$results = array(
|
||||
'buildable.diff' => null,
|
||||
|
|
|
@ -10,19 +10,18 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
|
|||
protected $artifactData = array();
|
||||
|
||||
private $buildTarget = self::ATTACHABLE;
|
||||
|
||||
const TYPE_FILE = 'file';
|
||||
const TYPE_HOST = 'host';
|
||||
const TYPE_URI = 'uri';
|
||||
private $artifactImplementation;
|
||||
|
||||
public static function initializeNewBuildArtifact(
|
||||
HarbormasterBuildTarget $build_target) {
|
||||
return id(new HarbormasterBuildArtifact())
|
||||
->attachBuildTarget($build_target)
|
||||
->setBuildTargetPHID($build_target->getPHID());
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'artifactData' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
|
@ -43,6 +42,11 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
|
|||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function generatePHID() {
|
||||
return PhabricatorPHID::generateNewPHID(
|
||||
HarbormasterBuildArtifactPHIDType::TYPECONST);
|
||||
}
|
||||
|
||||
public function attachBuildTarget(HarbormasterBuildTarget $build_target) {
|
||||
$this->buildTarget = $build_target;
|
||||
return $this;
|
||||
|
@ -52,113 +56,58 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
|
|||
return $this->assertAttached($this->buildTarget);
|
||||
}
|
||||
|
||||
public function setArtifactKey($build_phid, $build_gen, $key) {
|
||||
$this->artifactIndex =
|
||||
PhabricatorHash::digestForIndex($build_phid.$build_gen.$key);
|
||||
public function setArtifactKey($key) {
|
||||
$target = $this->getBuildTarget();
|
||||
$this->artifactIndex = self::getArtifactIndex($target, $key);
|
||||
$this->artifactKey = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObjectItemView(PhabricatorUser $viewer) {
|
||||
$data = $this->getArtifactData();
|
||||
switch ($this->getArtifactType()) {
|
||||
case self::TYPE_FILE:
|
||||
$handle = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($data)
|
||||
->executeOne();
|
||||
public static function getArtifactIndex(
|
||||
HarbormasterBuildTarget $target,
|
||||
$artifact_key) {
|
||||
|
||||
return id(new PHUIObjectItemView())
|
||||
->setObjectName(pht('File'))
|
||||
->setHeader($handle->getFullName())
|
||||
->setHref($handle->getURI());
|
||||
case self::TYPE_HOST:
|
||||
$leases = id(new DrydockLeaseQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($data['drydock-lease']))
|
||||
->execute();
|
||||
$lease = idx($leases, $data['drydock-lease']);
|
||||
$build = $target->getBuild();
|
||||
|
||||
return id(new PHUIObjectItemView())
|
||||
->setObjectName(pht('Drydock Lease'))
|
||||
->setHeader($lease->getID())
|
||||
->setHref('/drydock/lease/'.$lease->getID());
|
||||
case self::TYPE_URI:
|
||||
return id(new PHUIObjectItemView())
|
||||
->setObjectName($data['name'])
|
||||
->setHeader($data['uri'])
|
||||
->setHref($data['uri']);
|
||||
default:
|
||||
$parts = array(
|
||||
$build->getPHID(),
|
||||
$target->getBuildGeneration(),
|
||||
$artifact_key,
|
||||
);
|
||||
$parts = implode("\0", $parts);
|
||||
|
||||
return PhabricatorHash::digestForIndex($parts);
|
||||
}
|
||||
|
||||
public function releaseArtifact() {
|
||||
$impl = $this->getArtifactImplementation();
|
||||
|
||||
if ($impl) {
|
||||
$impl->releaseArtifact(PhabricatorUser::getOmnipotentUser());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getArtifactImplementation() {
|
||||
if ($this->artifactImplementation === null) {
|
||||
$type = $this->getArtifactType();
|
||||
$impl = HarbormasterArtifact::getArtifactType($type);
|
||||
if (!$impl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$impl = clone $impl;
|
||||
$impl->setBuildArtifact($this);
|
||||
$this->artifactImplementation = $impl;
|
||||
}
|
||||
|
||||
return $this->artifactImplementation;
|
||||
}
|
||||
|
||||
public function loadDrydockLease() {
|
||||
if ($this->getArtifactType() !== self::TYPE_HOST) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'`%s` may only be called on host artifacts.',
|
||||
__FUNCTION__));
|
||||
}
|
||||
|
||||
$data = $this->getArtifactData();
|
||||
|
||||
// FIXME: Is there a better way of doing this?
|
||||
// TODO: Policy stuff, etc.
|
||||
$lease = id(new DrydockLease())->load(
|
||||
$data['drydock-lease']);
|
||||
if ($lease === null) {
|
||||
throw new Exception(pht('Associated Drydock lease not found!'));
|
||||
}
|
||||
$resource = id(new DrydockResource())->load(
|
||||
$lease->getResourceID());
|
||||
if ($resource === null) {
|
||||
throw new Exception(pht('Associated Drydock resource not found!'));
|
||||
}
|
||||
$lease->attachResource($resource);
|
||||
|
||||
return $lease;
|
||||
}
|
||||
|
||||
public function loadPhabricatorFile() {
|
||||
if ($this->getArtifactType() !== self::TYPE_FILE) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'`%s` may only be called on file artifacts.',
|
||||
__FUNCTION__));
|
||||
}
|
||||
|
||||
$data = $this->getArtifactData();
|
||||
|
||||
// The data for TYPE_FILE is an array with a single PHID in it.
|
||||
$phid = $data['filePHID'];
|
||||
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs(array($phid))
|
||||
->executeOne();
|
||||
if ($file === null) {
|
||||
throw new Exception(pht('Associated file not found!'));
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
public function release() {
|
||||
switch ($this->getArtifactType()) {
|
||||
case self::TYPE_HOST:
|
||||
$this->releaseDrydockLease();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function releaseDrydockLease() {
|
||||
$lease = $this->loadDrydockLease();
|
||||
$resource = $lease->getResource();
|
||||
$blueprint = $resource->getBlueprint();
|
||||
|
||||
if ($lease->isActive()) {
|
||||
$blueprint->releaseLease($resource, $lease);
|
||||
}
|
||||
public function getProperty($key, $default = null) {
|
||||
return idx($this->artifactData, $key, $default);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -201,6 +201,54 @@ final class HarbormasterBuildTarget extends HarbormasterDAO
|
|||
);
|
||||
}
|
||||
|
||||
public function createArtifact(
|
||||
PhabricatorUser $actor,
|
||||
$artifact_key,
|
||||
$artifact_type,
|
||||
array $artifact_data) {
|
||||
|
||||
$impl = HarbormasterArtifact::getArtifactType($artifact_type);
|
||||
if (!$impl) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'There is no implementation available for artifacts of type "%s".',
|
||||
$artifact_type));
|
||||
}
|
||||
|
||||
$impl->validateArtifactData($artifact_data);
|
||||
|
||||
$artifact = HarbormasterBuildArtifact::initializeNewBuildArtifact($this)
|
||||
->setArtifactKey($artifact_key)
|
||||
->setArtifactType($artifact_type)
|
||||
->setArtifactData($artifact_data);
|
||||
|
||||
$impl = $artifact->getArtifactImplementation();
|
||||
$impl->willCreateArtifact($actor);
|
||||
|
||||
return $artifact->save();
|
||||
}
|
||||
|
||||
public function loadArtifact($artifact_key) {
|
||||
$indexes = array();
|
||||
|
||||
$indexes[] = HarbormasterBuildArtifact::getArtifactIndex(
|
||||
$this,
|
||||
$artifact_key);
|
||||
|
||||
$artifact = id(new HarbormasterBuildArtifactQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withArtifactIndexes($indexes)
|
||||
->executeOne();
|
||||
if ($artifact === null) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Artifact "%s" not found!',
|
||||
$artifact_key));
|
||||
}
|
||||
|
||||
return $artifact;
|
||||
}
|
||||
|
||||
|
||||
/* -( Status )------------------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@ final class HeraldTestConsoleController extends HeraldController {
|
|||
} else if ($object instanceof PonderQuestion) {
|
||||
$adapter = id(new HeraldPonderQuestionAdapter())
|
||||
->setQuestion($object);
|
||||
} else if ($object instanceof PhabricatorMetaMTAMail) {
|
||||
$adapter = id(new PhabricatorMailOutboundMailHeraldAdapter())
|
||||
->setObject($object);
|
||||
} else {
|
||||
throw new Exception(pht('Can not build adapter for object!'));
|
||||
}
|
||||
|
|
|
@ -356,7 +356,17 @@ final class HeraldTranscriptController extends HeraldController {
|
|||
|
||||
// Handle older transcripts which used a static string to record
|
||||
// action results.
|
||||
if (!is_array($log)) {
|
||||
|
||||
if ($xscript->getDryRun()) {
|
||||
$action_list->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon('fa-ban', 'grey')
|
||||
->setTarget(pht('Dry Run'))
|
||||
->setNote(
|
||||
pht(
|
||||
'This was a dry run, so no actions were taken.')));
|
||||
continue;
|
||||
} else if (!is_array($log)) {
|
||||
$action_list->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon('fa-clock-o', 'grey')
|
||||
|
|
|
@ -111,7 +111,9 @@ final class PhabricatorMacroSearchEngine
|
|||
|
||||
switch ($query_key) {
|
||||
case 'active':
|
||||
return $query;
|
||||
return $query->setParameter(
|
||||
'status',
|
||||
PhabricatorMacroQuery::STATUS_ACTIVE);
|
||||
case 'all':
|
||||
return $query->setParameter(
|
||||
'status',
|
||||
|
|
|
@ -25,7 +25,7 @@ final class PhabricatorManiphestConfigOptions
|
|||
100 => array(
|
||||
'name' => pht('Unbreak Now!'),
|
||||
'short' => pht('Unbreak!'),
|
||||
'color' => 'indigo',
|
||||
'color' => 'pink',
|
||||
'keywords' => array('unbreak'),
|
||||
),
|
||||
90 => array(
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMailRoutingRule extends Phobject {
|
||||
|
||||
const ROUTE_AS_NOTIFICATION = 'route.notification';
|
||||
const ROUTE_AS_MAIL = 'route.mail';
|
||||
|
||||
public static function isStrongerThan($rule_u, $rule_v) {
|
||||
$strength_u = self::getRuleStrength($rule_u);
|
||||
$strength_v = self::getRuleStrength($rule_v);
|
||||
|
||||
return ($strength_u > $strength_v);
|
||||
}
|
||||
|
||||
public static function getRuleStrength($const) {
|
||||
$strength = array(
|
||||
self::ROUTE_AS_NOTIFICATION => 1,
|
||||
self::ROUTE_AS_MAIL => 2,
|
||||
);
|
||||
|
||||
return idx($strength, $const, 0);
|
||||
}
|
||||
|
||||
public static function getRuleName($const) {
|
||||
$names = array(
|
||||
self::ROUTE_AS_NOTIFICATION => pht('Route as Notification'),
|
||||
self::ROUTE_AS_MAIL => pht('Route as Mail'),
|
||||
);
|
||||
|
||||
return idx($names, $const, $const);
|
||||
}
|
||||
|
||||
public static function getRuleIcon($const) {
|
||||
$icons = array(
|
||||
self::ROUTE_AS_NOTIFICATION => 'fa-bell',
|
||||
self::ROUTE_AS_MAIL => 'fa-envelope',
|
||||
);
|
||||
|
||||
return idx($icons, $const, 'fa-question-circle');
|
||||
}
|
||||
|
||||
public static function getRuleColor($const) {
|
||||
$colors = array(
|
||||
self::ROUTE_AS_NOTIFICATION => 'grey',
|
||||
self::ROUTE_AS_MAIL => 'grey',
|
||||
);
|
||||
|
||||
return idx($colors, $const, 'yellow');
|
||||
}
|
||||
|
||||
}
|
|
@ -86,6 +86,10 @@ final class PhabricatorMetaMTAMailViewController
|
|||
pht('Cc'),
|
||||
$cc_list);
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Sent'),
|
||||
phabricator_datetime($mail->getDateCreated(), $viewer));
|
||||
|
||||
$properties->addSectionHeader(
|
||||
pht('Message'),
|
||||
PHUIPropertyListView::ICON_SUMMARY);
|
||||
|
@ -144,23 +148,16 @@ final class PhabricatorMetaMTAMailViewController
|
|||
$actors = $mail->getDeliveredActors();
|
||||
$reasons = null;
|
||||
if (!$actors) {
|
||||
// TODO: We can get rid of this special-cased message after these changes
|
||||
// have been live for a while, but provide a more tailored message for
|
||||
// now so things are a little less confusing for users.
|
||||
if ($mail->getStatus() == PhabricatorMetaMTAMail::STATUS_SENT) {
|
||||
$delivery = phutil_tag(
|
||||
'em',
|
||||
array(),
|
||||
pht(
|
||||
'This is an older message that predates recording delivery '.
|
||||
'information, so none is available.'));
|
||||
} else {
|
||||
$delivery = phutil_tag(
|
||||
'em',
|
||||
array(),
|
||||
if ($mail->getStatus() == PhabricatorMailOutboundStatus::STATUS_QUEUE) {
|
||||
$delivery = $this->renderEmptyMessage(
|
||||
pht(
|
||||
'This message has not been delivered yet, so delivery information '.
|
||||
'is not available.'));
|
||||
} else {
|
||||
$delivery = $this->renderEmptyMessage(
|
||||
pht(
|
||||
'This is an older message that predates recording delivery '.
|
||||
'information, so none is available.'));
|
||||
}
|
||||
} else {
|
||||
$actor = idx($actors, $viewer->getPHID());
|
||||
|
@ -214,6 +211,127 @@ final class PhabricatorMetaMTAMailViewController
|
|||
$properties->addProperty(pht('Delivery'), $delivery);
|
||||
if ($reasons) {
|
||||
$properties->addProperty(pht('Reasons'), $reasons);
|
||||
$properties->addProperty(
|
||||
null,
|
||||
$this->renderEmptyMessage(
|
||||
pht(
|
||||
'Delivery reasons are listed from weakest to strongest.')));
|
||||
}
|
||||
|
||||
$properties->addSectionHeader(pht('Routing Rules'));
|
||||
|
||||
$map = $mail->getDeliveredRoutingMap();
|
||||
$routing_detail = null;
|
||||
if ($map === null) {
|
||||
if ($mail->getStatus() == PhabricatorMailOutboundStatus::STATUS_QUEUE) {
|
||||
$routing_result = $this->renderEmptyMessage(
|
||||
pht(
|
||||
'This message has not been sent yet, so routing rules have '.
|
||||
'not been computed.'));
|
||||
} else {
|
||||
$routing_result = $this->renderEmptyMessage(
|
||||
pht(
|
||||
'This is an older message which predates routing rules.'));
|
||||
}
|
||||
} else {
|
||||
$rule = idx($map, $viewer->getPHID());
|
||||
if ($rule === null) {
|
||||
$rule = idx($map, 'default');
|
||||
}
|
||||
|
||||
if ($rule === null) {
|
||||
$routing_result = $this->renderEmptyMessage(
|
||||
pht(
|
||||
'No routing rules applied when delivering this message to you.'));
|
||||
} else {
|
||||
$rule_const = $rule['rule'];
|
||||
$reason_phid = $rule['reason'];
|
||||
switch ($rule_const) {
|
||||
case PhabricatorMailRoutingRule::ROUTE_AS_NOTIFICATION:
|
||||
$routing_result = pht(
|
||||
'This message was routed as a notification because it '.
|
||||
'matched %s.',
|
||||
$viewer->renderHandle($reason_phid)->render());
|
||||
break;
|
||||
case PhabricatorMailRoutingRule::ROUTE_AS_MAIL:
|
||||
$routing_result = pht(
|
||||
'This message was routed as an email because it matched %s.',
|
||||
$viewer->renderHandle($reason_phid)->render());
|
||||
break;
|
||||
default:
|
||||
$routing_result = pht('Unknown routing rule "%s".', $rule_const);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$routing_rules = $mail->getDeliveredRoutingRules();
|
||||
if ($routing_rules) {
|
||||
$rules = array();
|
||||
foreach ($routing_rules as $rule) {
|
||||
$phids = idx($rule, 'phids');
|
||||
if ($phids === null) {
|
||||
$rules[] = $rule;
|
||||
} else if (in_array($viewer->getPHID(), $phids)) {
|
||||
$rules[] = $rule;
|
||||
}
|
||||
}
|
||||
|
||||
// Reorder rules by strength.
|
||||
foreach ($rules as $key => $rule) {
|
||||
$const = $rule['routingRule'];
|
||||
$phids = $rule['phids'];
|
||||
|
||||
if ($phids === null) {
|
||||
$type = 'A';
|
||||
} else {
|
||||
$type = 'B';
|
||||
}
|
||||
|
||||
$rules[$key]['strength'] = sprintf(
|
||||
'~%s%08d',
|
||||
$type,
|
||||
PhabricatorMailRoutingRule::getRuleStrength($const));
|
||||
}
|
||||
$rules = isort($rules, 'strength');
|
||||
|
||||
$routing_detail = id(new PHUIStatusListView());
|
||||
foreach ($rules as $rule) {
|
||||
$const = $rule['routingRule'];
|
||||
$phids = $rule['phids'];
|
||||
|
||||
$name = PhabricatorMailRoutingRule::getRuleName($const);
|
||||
|
||||
$icon = PhabricatorMailRoutingRule::getRuleIcon($const);
|
||||
$color = PhabricatorMailRoutingRule::getRuleColor($const);
|
||||
|
||||
if ($phids === null) {
|
||||
$kind = pht('Global');
|
||||
} else {
|
||||
$kind = pht('Personal');
|
||||
}
|
||||
|
||||
$target = array($kind, ': ', $name);
|
||||
$target = phutil_tag('strong', array(), $target);
|
||||
|
||||
$item = id(new PHUIStatusItemView())
|
||||
->setTarget($target)
|
||||
->setNote($viewer->renderHandle($rule['reasonPHID']))
|
||||
->setIcon($icon, $color);
|
||||
|
||||
$routing_detail->addItem($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$properties->addProperty(pht('Effective Rule'), $routing_result);
|
||||
|
||||
if ($routing_detail !== null) {
|
||||
$properties->addProperty(pht('All Matching Rules'), $routing_detail);
|
||||
$properties->addProperty(
|
||||
null,
|
||||
$this->renderEmptyMessage(
|
||||
pht(
|
||||
'Matching rules are listed from weakest to strongest.')));
|
||||
}
|
||||
|
||||
return $properties;
|
||||
|
@ -252,4 +370,8 @@ final class PhabricatorMetaMTAMailViewController
|
|||
return $properties;
|
||||
}
|
||||
|
||||
private function renderEmptyMessage($message) {
|
||||
return phutil_tag('em', array(), $message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorMailEmailHeraldField
|
||||
extends HeraldField {
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof PhabricatorMetaMTAMail);
|
||||
}
|
||||
|
||||
public function getFieldGroupKey() {
|
||||
return PhabricatorMailEmailHeraldFieldGroup::FIELDGROUPKEY;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMailEmailHeraldFieldGroup extends HeraldFieldGroup {
|
||||
|
||||
const FIELDGROUPKEY = 'mail.message';
|
||||
|
||||
public function getGroupLabel() {
|
||||
return pht('Message Fields');
|
||||
}
|
||||
|
||||
protected function getGroupOrder() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMailEmailSubjectHeraldField
|
||||
extends PhabricatorMailEmailHeraldField {
|
||||
|
||||
const FIELDCONST = 'mail.message.subject';
|
||||
|
||||
public function getHeraldFieldName() {
|
||||
return pht('Subject');
|
||||
}
|
||||
|
||||
public function getHeraldFieldValue($object) {
|
||||
return $object->getSubject();
|
||||
}
|
||||
|
||||
protected function getHeraldFieldStandardType() {
|
||||
return self::STANDARD_TEXT;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMailOutboundMailHeraldAdapter
|
||||
extends HeraldAdapter {
|
||||
|
||||
private $mail;
|
||||
|
||||
public function getAdapterApplicationClass() {
|
||||
return 'PhabricatorMetaMTAApplication';
|
||||
}
|
||||
|
||||
public function getAdapterContentDescription() {
|
||||
return pht('Route outbound email.');
|
||||
}
|
||||
|
||||
protected function initializeNewAdapter() {
|
||||
$this->mail = $this->newObject();
|
||||
}
|
||||
|
||||
protected function newObject() {
|
||||
return new PhabricatorMetaMTAMail();
|
||||
}
|
||||
|
||||
public function getObject() {
|
||||
return $this->mail;
|
||||
}
|
||||
|
||||
public function setObject(PhabricatorMetaMTAMail $mail) {
|
||||
$this->mail = $mail;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdapterContentName() {
|
||||
return pht('Outbound Mail');
|
||||
}
|
||||
|
||||
public function isSingleEventAdapter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRepetitionOptions() {
|
||||
return array(
|
||||
HeraldRepetitionPolicyConfig::FIRST,
|
||||
);
|
||||
}
|
||||
|
||||
public function supportsRuleType($rule_type) {
|
||||
switch ($rule_type) {
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL:
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
|
||||
return true;
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_OBJECT:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getHeraldName() {
|
||||
return pht('Mail %d', $this->getObject()->getID());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorMailOutboundRoutingHeraldAction
|
||||
extends HeraldAction {
|
||||
|
||||
const DO_ROUTE = 'do.route';
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof PhabricatorMetaMTAMail);
|
||||
}
|
||||
|
||||
public function getActionGroupKey() {
|
||||
return HeraldApplicationActionGroup::ACTIONGROUPKEY;
|
||||
}
|
||||
|
||||
protected function applyRouting(HeraldRule $rule, $route, $phids) {
|
||||
$adapter = $this->getAdapter();
|
||||
$mail = $adapter->getObject();
|
||||
$mail->addRoutingRule($route, $phids, $rule->getPHID());
|
||||
|
||||
$this->logEffect(
|
||||
self::DO_ROUTE,
|
||||
array(
|
||||
'route' => $route,
|
||||
'phids' => $phids,
|
||||
));
|
||||
}
|
||||
|
||||
protected function getActionEffectMap() {
|
||||
return array(
|
||||
self::DO_ROUTE => array(
|
||||
'icon' => 'fa-arrow-right',
|
||||
'color' => 'green',
|
||||
'name' => pht('Routed Message'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function renderActionEffectDescription($type, $data) {
|
||||
switch ($type) {
|
||||
case self::DO_ROUTE:
|
||||
return pht('Routed mail.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMailOutboundRoutingSelfEmailHeraldAction
|
||||
extends PhabricatorMailOutboundRoutingHeraldAction {
|
||||
|
||||
const ACTIONCONST = 'routing.self.email';
|
||||
|
||||
public function getHeraldActionName() {
|
||||
return pht('Deliver as email');
|
||||
}
|
||||
|
||||
public function supportsRuleType($rule_type) {
|
||||
return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
||||
}
|
||||
|
||||
public function applyEffect($object, HeraldEffect $effect) {
|
||||
$rule = $effect->getRule();
|
||||
$author_phid = $rule->getAuthorPHID();
|
||||
|
||||
$this->applyRouting(
|
||||
$rule,
|
||||
PhabricatorMailRoutingRule::ROUTE_AS_MAIL,
|
||||
array($author_phid));
|
||||
}
|
||||
|
||||
public function getHeraldActionStandardType() {
|
||||
return self::STANDARD_NONE;
|
||||
}
|
||||
|
||||
public function renderActionDescription($value) {
|
||||
return pht('Deliver as email.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMailOutboundRoutingSelfNotificationHeraldAction
|
||||
extends PhabricatorMailOutboundRoutingHeraldAction {
|
||||
|
||||
const ACTIONCONST = 'routing.self.notification';
|
||||
|
||||
public function getHeraldActionName() {
|
||||
return pht('Deliver as notification');
|
||||
}
|
||||
|
||||
public function supportsRuleType($rule_type) {
|
||||
return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
||||
}
|
||||
|
||||
public function applyEffect($object, HeraldEffect $effect) {
|
||||
$rule = $effect->getRule();
|
||||
$author_phid = $rule->getAuthorPHID();
|
||||
|
||||
$this->applyRouting(
|
||||
$rule,
|
||||
PhabricatorMailRoutingRule::ROUTE_AS_NOTIFICATION,
|
||||
array($author_phid));
|
||||
}
|
||||
|
||||
public function getHeraldActionStandardType() {
|
||||
return self::STANDARD_NONE;
|
||||
}
|
||||
|
||||
public function renderActionDescription($value) {
|
||||
return pht('Deliver as notification.');
|
||||
}
|
||||
|
||||
}
|
|
@ -14,6 +14,10 @@ abstract class PhabricatorMetaMTAEmailHeraldAction
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($object instanceof PhabricatorMetaMTAMail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ final class PhabricatorMetaMTAMailPHIDType extends PhabricatorPHIDType {
|
|||
|
||||
$handle
|
||||
->setName($name)
|
||||
->setFullName($name);
|
||||
->setURI('/mail/detail/'.$id.'/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
|||
const REASON_BOT = 'bot';
|
||||
const REASON_FORCE = 'force';
|
||||
const REASON_FORCE_HERALD = 'force-herald';
|
||||
const REASON_ROUTE_AS_NOTIFICATION = 'route-as-notification';
|
||||
const REASON_ROUTE_AS_MAIL = 'route-as-mail';
|
||||
|
||||
private $phid;
|
||||
private $emailAddress;
|
||||
|
@ -77,6 +79,7 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
|||
case self::REASON_NONE:
|
||||
case self::REASON_FORCE:
|
||||
case self::REASON_FORCE_HERALD:
|
||||
case self::REASON_ROUTE_AS_MAIL:
|
||||
return true;
|
||||
default:
|
||||
// All other reasons cause the message to not be delivered.
|
||||
|
@ -99,6 +102,8 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
|||
self::REASON_UNLOADABLE => pht('Bad Recipient'),
|
||||
self::REASON_FORCE => pht('Forced Mail'),
|
||||
self::REASON_FORCE_HERALD => pht('Forced by Herald'),
|
||||
self::REASON_ROUTE_AS_NOTIFICATION => pht('Route as Notification'),
|
||||
self::REASON_ROUTE_AS_MAIL => pht('Route as Mail'),
|
||||
);
|
||||
|
||||
return idx($names, $reason, pht('Unknown ("%s")', $reason));
|
||||
|
@ -147,6 +152,12 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
|||
self::REASON_FORCE_HERALD => pht(
|
||||
'This recipient was added by a "Send me an Email" rule in Herald, '.
|
||||
'which overrides some delivery settings.'),
|
||||
self::REASON_ROUTE_AS_NOTIFICATION => pht(
|
||||
'This message was downgraded to a notification by outbound mail '.
|
||||
'rules in Herald.'),
|
||||
self::REASON_ROUTE_AS_MAIL => pht(
|
||||
'This message was upgraded to email by outbound mail rules '.
|
||||
'in Herald.'),
|
||||
);
|
||||
|
||||
return idx($descriptions, $reason, pht('Unknown Reason ("%s")', $reason));
|
||||
|
|
|
@ -16,6 +16,7 @@ final class PhabricatorMetaMTAMail
|
|||
protected $relatedPHID;
|
||||
|
||||
private $recipientExpansionMap;
|
||||
private $routingMap;
|
||||
|
||||
public function __construct() {
|
||||
|
||||
|
@ -656,6 +657,9 @@ final class PhabricatorMetaMTAMail
|
|||
}
|
||||
$this->setParam('actors.sent', $actor_list);
|
||||
|
||||
$this->setParam('routing.sent', $this->getParam('routing'));
|
||||
$this->setParam('routingmap.sent', $this->getRoutingRuleMap());
|
||||
|
||||
if (!$add_to && !$add_cc) {
|
||||
$this->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID);
|
||||
$this->setMessage(
|
||||
|
@ -963,9 +967,25 @@ final class PhabricatorMetaMTAMail
|
|||
}
|
||||
}
|
||||
|
||||
foreach ($deliverable as $phid) {
|
||||
switch ($this->getRoutingRule($phid)) {
|
||||
case PhabricatorMailRoutingRule::ROUTE_AS_NOTIFICATION:
|
||||
$actors[$phid]->setUndeliverable(
|
||||
PhabricatorMetaMTAActor::REASON_ROUTE_AS_NOTIFICATION);
|
||||
break;
|
||||
case PhabricatorMailRoutingRule::ROUTE_AS_MAIL:
|
||||
$actors[$phid]->setDeliverable(
|
||||
PhabricatorMetaMTAActor::REASON_ROUTE_AS_MAIL);
|
||||
break;
|
||||
default:
|
||||
// No change.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If recipients were initially deliverable and were added by "Send me an
|
||||
// email" Herald rules, annotate them as such and make them deliverable
|
||||
// again, overriding any changes made by the "self mail" and "mail tags"
|
||||
// again, overriding any changes made by the "self mail" and "mail tags"
|
||||
// settings.
|
||||
$force_recipients = $this->getForceHeraldMailRecipientPHIDs();
|
||||
$force_recipients = array_fuse($force_recipients);
|
||||
|
@ -1065,6 +1085,82 @@ final class PhabricatorMetaMTAMail
|
|||
return $this->getParam('actors.sent');
|
||||
}
|
||||
|
||||
public function getDeliveredRoutingRules() {
|
||||
return $this->getParam('routing.sent');
|
||||
}
|
||||
|
||||
public function getDeliveredRoutingMap() {
|
||||
return $this->getParam('routingmap.sent');
|
||||
}
|
||||
|
||||
|
||||
/* -( Routing )------------------------------------------------------------ */
|
||||
|
||||
|
||||
public function addRoutingRule($routing_rule, $phids, $reason_phid) {
|
||||
$routing = $this->getParam('routing', array());
|
||||
$routing[] = array(
|
||||
'routingRule' => $routing_rule,
|
||||
'phids' => $phids,
|
||||
'reasonPHID' => $reason_phid,
|
||||
);
|
||||
$this->setParam('routing', $routing);
|
||||
|
||||
// Throw the routing map away so we rebuild it.
|
||||
$this->routingMap = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getRoutingRule($phid) {
|
||||
$map = $this->getRoutingRuleMap();
|
||||
|
||||
$info = idx($map, $phid, idx($map, 'default'));
|
||||
if ($info) {
|
||||
return idx($info, 'rule');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getRoutingRuleMap() {
|
||||
if ($this->routingMap === null) {
|
||||
$map = array();
|
||||
|
||||
$routing = $this->getParam('routing', array());
|
||||
foreach ($routing as $route) {
|
||||
$phids = $route['phids'];
|
||||
if ($phids === null) {
|
||||
$phids = array('default');
|
||||
}
|
||||
|
||||
foreach ($phids as $phid) {
|
||||
$new_rule = $route['routingRule'];
|
||||
|
||||
$current_rule = idx($map, $phid);
|
||||
if ($current_rule === null) {
|
||||
$is_stronger = true;
|
||||
} else {
|
||||
$is_stronger = PhabricatorMailRoutingRule::isStrongerThan(
|
||||
$new_rule,
|
||||
$current_rule);
|
||||
}
|
||||
|
||||
if ($is_stronger) {
|
||||
$map[$phid] = array(
|
||||
'rule' => $new_rule,
|
||||
'reason' => $route['reasonPHID'],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->routingMap = $map;
|
||||
}
|
||||
|
||||
return $this->routingMap;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -110,7 +110,6 @@ final class OwnersQueryConduitAPIMethod extends OwnersConduitAPIMethod {
|
|||
'phid' => $package->getPHID(),
|
||||
'name' => $package->getName(),
|
||||
'description' => $package->getDescription(),
|
||||
'primaryOwner' => $package->getPrimaryOwnerPHID(),
|
||||
'owners' => $owners,
|
||||
'paths' => $paths,
|
||||
);
|
||||
|
@ -142,7 +141,7 @@ final class OwnersQueryConduitAPIMethod extends OwnersConduitAPIMethod {
|
|||
$query = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($request->getUser());
|
||||
|
||||
$query->withOwnerPHIDs(array($request->getValue('userAffiliated')));
|
||||
$query->withAuthorityPHIDs(array($request->getValue('userAffiliated')));
|
||||
|
||||
$packages = $query->execute();
|
||||
} else if ($is_owner_query) {
|
||||
|
|
|
@ -13,12 +13,14 @@ final class PhabricatorOwnersDetailController
|
|||
$package = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->needPaths(true)
|
||||
->needOwners(true)
|
||||
->executeOne();
|
||||
if (!$package) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$paths = $package->loadPaths();
|
||||
$paths = $package->getPaths();
|
||||
|
||||
$repository_phids = array();
|
||||
foreach ($paths as $path) {
|
||||
|
@ -39,9 +41,20 @@ final class PhabricatorOwnersDetailController
|
|||
$properties = $this->buildPackagePropertyView($package);
|
||||
$properties->setActionList($actions);
|
||||
|
||||
if ($package->isArchived()) {
|
||||
$header_icon = 'fa-ban';
|
||||
$header_name = pht('Archived');
|
||||
$header_color = 'dark';
|
||||
} else {
|
||||
$header_icon = 'fa-check';
|
||||
$header_name = pht('Active');
|
||||
$header_color = 'bluegrey';
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setUser($viewer)
|
||||
->setHeader($package->getName())
|
||||
->setStatus($header_icon, $header_color, $header_name)
|
||||
->setPolicyObject($package);
|
||||
|
||||
$panel = id(new PHUIObjectBoxView())
|
||||
|
@ -149,16 +162,7 @@ final class PhabricatorOwnersDetailController
|
|||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($viewer);
|
||||
|
||||
$primary_phid = $package->getPrimaryOwnerPHID();
|
||||
if ($primary_phid) {
|
||||
$primary_owner = $viewer->renderHandle($primary_phid);
|
||||
} else {
|
||||
$primary_owner = phutil_tag('em', array(), pht('None'));
|
||||
}
|
||||
$view->addProperty(pht('Primary Owner'), $primary_owner);
|
||||
|
||||
// TODO: needOwners() this on the Query.
|
||||
$owners = $package->loadOwners();
|
||||
$owners = $package->getOwners();
|
||||
if ($owners) {
|
||||
$owner_list = $viewer->renderHandleList(mpull($owners, 'getUserPHID'));
|
||||
} else {
|
||||
|
|
|
@ -17,6 +17,7 @@ final class PhabricatorOwnersEditController
|
|||
// TODO: Support this capability.
|
||||
// PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->needOwners(true)
|
||||
->executeOne();
|
||||
if (!$package) {
|
||||
return new Aphront404Response();
|
||||
|
@ -28,14 +29,12 @@ final class PhabricatorOwnersEditController
|
|||
}
|
||||
|
||||
$e_name = true;
|
||||
$e_primary = true;
|
||||
|
||||
$v_name = $package->getName();
|
||||
$v_primary = $package->getPrimaryOwnerPHID();
|
||||
// TODO: Pull these off needOwners() on the Query.
|
||||
$v_owners = mpull($package->loadOwners(), 'getUserPHID');
|
||||
$v_owners = mpull($package->getOwners(), 'getUserPHID');
|
||||
$v_auditing = $package->getAuditingEnabled();
|
||||
$v_description = $package->getDescription();
|
||||
$v_status = $package->getStatus();
|
||||
|
||||
|
||||
$errors = array();
|
||||
|
@ -43,30 +42,21 @@ final class PhabricatorOwnersEditController
|
|||
$xactions = array();
|
||||
|
||||
$v_name = $request->getStr('name');
|
||||
$v_primary = head($request->getArr('primary'));
|
||||
$v_owners = $request->getArr('owners');
|
||||
$v_auditing = ($request->getStr('auditing') == 'enabled');
|
||||
$v_description = $request->getStr('description');
|
||||
|
||||
if ($v_primary) {
|
||||
$v_owners[] = $v_primary;
|
||||
$v_owners = array_unique($v_owners);
|
||||
}
|
||||
$v_status = $request->getStr('status');
|
||||
|
||||
$type_name = PhabricatorOwnersPackageTransaction::TYPE_NAME;
|
||||
$type_primary = PhabricatorOwnersPackageTransaction::TYPE_PRIMARY;
|
||||
$type_owners = PhabricatorOwnersPackageTransaction::TYPE_OWNERS;
|
||||
$type_auditing = PhabricatorOwnersPackageTransaction::TYPE_AUDITING;
|
||||
$type_description = PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION;
|
||||
$type_status = PhabricatorOwnersPackageTransaction::TYPE_STATUS;
|
||||
|
||||
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
|
||||
->setTransactionType($type_name)
|
||||
->setNewValue($v_name);
|
||||
|
||||
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
|
||||
->setTransactionType($type_primary)
|
||||
->setNewValue($v_primary);
|
||||
|
||||
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
|
||||
->setTransactionType($type_owners)
|
||||
->setNewValue($v_owners);
|
||||
|
@ -79,6 +69,12 @@ final class PhabricatorOwnersEditController
|
|||
->setTransactionType($type_description)
|
||||
->setNewValue($v_description);
|
||||
|
||||
if (!$is_new) {
|
||||
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
|
||||
->setTransactionType($type_status)
|
||||
->setNewValue($v_status);
|
||||
}
|
||||
|
||||
$editor = id(new PhabricatorOwnersPackageTransactionEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
|
@ -102,16 +98,9 @@ final class PhabricatorOwnersEditController
|
|||
$validation_exception = $ex;
|
||||
|
||||
$e_name = $ex->getShortMessage($type_name);
|
||||
$e_primary = $ex->getShortMessage($type_primary);
|
||||
}
|
||||
}
|
||||
|
||||
if ($v_primary) {
|
||||
$value_primary_owner = array($v_primary);
|
||||
} else {
|
||||
$value_primary_owner = array();
|
||||
}
|
||||
|
||||
if ($is_new) {
|
||||
$cancel_uri = '/owners/';
|
||||
$title = pht('New Package');
|
||||
|
@ -130,21 +119,23 @@ final class PhabricatorOwnersEditController
|
|||
->setName('name')
|
||||
->setValue($v_name)
|
||||
->setError($e_name))
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new PhabricatorProjectOrUserDatasource())
|
||||
->setLabel(pht('Primary Owner'))
|
||||
->setName('primary')
|
||||
->setLimit(1)
|
||||
->setValue($value_primary_owner)
|
||||
->setError($e_primary))
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new PhabricatorProjectOrUserDatasource())
|
||||
->setLabel(pht('Owners'))
|
||||
->setName('owners')
|
||||
->setValue($v_owners))
|
||||
->appendChild(
|
||||
->setValue($v_owners));
|
||||
|
||||
if (!$is_new) {
|
||||
$form->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Status'))
|
||||
->setName('status')
|
||||
->setValue($v_status)
|
||||
->setOptions($package->getStatusNameMap()));
|
||||
}
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setName('auditing')
|
||||
->setLabel(pht('Auditing'))
|
||||
|
|
|
@ -15,6 +15,7 @@ final class PhabricatorOwnersPathsController
|
|||
// TODO: Support this capability.
|
||||
// PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->needPaths(true)
|
||||
->executeOne();
|
||||
if (!$package) {
|
||||
return new Aphront404Response();
|
||||
|
@ -66,7 +67,7 @@ final class PhabricatorOwnersPathsController
|
|||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/owners/package/'.$package->getID().'/');
|
||||
} else {
|
||||
$paths = $package->loadPaths();
|
||||
$paths = $package->getPaths();
|
||||
$path_refs = mpull($paths, 'getRef');
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_NAME;
|
||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PRIMARY;
|
||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_OWNERS;
|
||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_AUDITING;
|
||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION;
|
||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
|
||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
@ -31,8 +31,6 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
|
||||
return $object->getName();
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
|
||||
return $object->getPrimaryOwnerPHID();
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
|
||||
// TODO: needOwners() this on the Query.
|
||||
$phids = mpull($object->loadOwners(), 'getUserPHID');
|
||||
|
@ -43,9 +41,10 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
|
||||
return $object->getDescription();
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
|
||||
// TODO: needPaths() this on the query
|
||||
$paths = $object->loadPaths();
|
||||
$paths = $object->getPaths();
|
||||
return mpull($paths, 'getRef');
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
|
||||
return $object->getStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,9 +54,9 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
|
||||
return $xaction->getNewValue();
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
|
||||
return (int)$xaction->getNewValue();
|
||||
|
@ -95,9 +94,6 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
|
||||
$object->setName($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
|
||||
$object->setPrimaryOwnerPHID($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
|
||||
$object->setDescription($xaction->getNewValue());
|
||||
return;
|
||||
|
@ -107,6 +103,9 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
|
||||
return;
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
|
||||
$object->setStatus($xaction->getNewValue());
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||
|
@ -118,9 +117,9 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
|
||||
return;
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
|
||||
$old = $xaction->getOldValue();
|
||||
|
@ -152,8 +151,7 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
$old = $xaction->getOldValue();
|
||||
$new = $xaction->getNewValue();
|
||||
|
||||
// TODO: needPaths this
|
||||
$paths = $object->loadPaths();
|
||||
$paths = $object->getPaths();
|
||||
|
||||
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
|
||||
list($rem, $add) = $diffs;
|
||||
|
@ -202,22 +200,6 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
|
||||
$missing = $this->validateIsEmptyTextField(
|
||||
$object->getPrimaryOwnerPHID(),
|
||||
$xactions);
|
||||
|
||||
if ($missing) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Required'),
|
||||
pht('Packages must have a primary owner.'),
|
||||
nonempty(last($xactions), null));
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
|
@ -247,7 +229,6 @@ final class PhabricatorOwnersPackageTransactionEditor
|
|||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
return array(
|
||||
$object->getPrimaryOwnerPHID(),
|
||||
$this->requireActor()->getPHID(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,17 +6,38 @@ final class PhabricatorOwnersPackageQuery
|
|||
private $ids;
|
||||
private $phids;
|
||||
private $ownerPHIDs;
|
||||
private $authorityPHIDs;
|
||||
private $repositoryPHIDs;
|
||||
private $paths;
|
||||
private $namePrefix;
|
||||
private $statuses;
|
||||
|
||||
private $controlMap = array();
|
||||
private $controlResults;
|
||||
|
||||
private $needPaths;
|
||||
private $needOwners;
|
||||
|
||||
|
||||
/**
|
||||
* Owners are direct owners, and members of owning projects.
|
||||
* Query owner PHIDs exactly. This does not expand authorities, so a user
|
||||
* PHID will not match projects the user is a member of.
|
||||
*/
|
||||
public function withOwnerPHIDs(array $phids) {
|
||||
$this->ownerPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query owner authority. This will expand authorities, so a user PHID will
|
||||
* match both packages they own directly and packages owned by a project they
|
||||
* are a member of.
|
||||
*/
|
||||
public function withAuthorityPHIDs(array $phids) {
|
||||
$this->authorityPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
|
@ -32,30 +53,104 @@ final class PhabricatorOwnersPackageQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withPaths(array $paths) {
|
||||
$this->paths = $paths;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withStatuses(array $statuses) {
|
||||
$this->statuses = $statuses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withControl($repository_phid, array $paths) {
|
||||
if (empty($this->controlMap[$repository_phid])) {
|
||||
$this->controlMap[$repository_phid] = array();
|
||||
}
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$path = (string)$path;
|
||||
$this->controlMap[$repository_phid][$path] = $path;
|
||||
}
|
||||
|
||||
// We need to load paths to execute control queries.
|
||||
$this->needPaths = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withNamePrefix($prefix) {
|
||||
$this->namePrefix = $prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function needPaths($need_paths) {
|
||||
$this->needPaths = $need_paths;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function needOwners($need_owners) {
|
||||
$this->needOwners = $need_owners;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorOwnersPackage();
|
||||
}
|
||||
|
||||
protected function willExecute() {
|
||||
$this->controlResults = array();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage(new PhabricatorOwnersPackage());
|
||||
}
|
||||
|
||||
protected function didFilterPage(array $packages) {
|
||||
if ($this->needPaths) {
|
||||
$package_ids = mpull($packages, 'getID');
|
||||
|
||||
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
|
||||
'packageID IN (%Ld)',
|
||||
$package_ids);
|
||||
$paths = mgroup($paths, 'getPackageID');
|
||||
|
||||
foreach ($packages as $package) {
|
||||
$package->attachPaths(idx($paths, $package->getID(), array()));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->needOwners) {
|
||||
$package_ids = mpull($packages, 'getID');
|
||||
|
||||
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(
|
||||
'packageID IN (%Ld)',
|
||||
$package_ids);
|
||||
$owners = mgroup($owners, 'getPackageID');
|
||||
|
||||
foreach ($packages as $package) {
|
||||
$package->attachOwners(idx($owners, $package->getID(), array()));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->controlMap) {
|
||||
$this->controlResults += mpull($packages, null, 'getID');
|
||||
}
|
||||
|
||||
return $packages;
|
||||
}
|
||||
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$joins = parent::buildJoinClauseParts($conn);
|
||||
|
||||
if ($this->ownerPHIDs !== null) {
|
||||
if ($this->shouldJoinOwnersTable()) {
|
||||
$joins[] = qsprintf(
|
||||
$conn,
|
||||
'JOIN %T o ON o.packageID = p.id',
|
||||
id(new PhabricatorOwnersOwner())->getTableName());
|
||||
}
|
||||
|
||||
if ($this->repositoryPHIDs !== null) {
|
||||
if ($this->shouldJoinPathTable()) {
|
||||
$joins[] = qsprintf(
|
||||
$conn,
|
||||
'JOIN %T rpath ON rpath.packageID = p.id',
|
||||
|
@ -89,21 +184,33 @@ final class PhabricatorOwnersPackageQuery
|
|||
$this->repositoryPHIDs);
|
||||
}
|
||||
|
||||
if ($this->ownerPHIDs !== null) {
|
||||
$base_phids = $this->ownerPHIDs;
|
||||
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withMemberPHIDs($base_phids)
|
||||
->execute();
|
||||
$project_phids = mpull($projects, 'getPHID');
|
||||
|
||||
$all_phids = array_merge($base_phids, $project_phids);
|
||||
|
||||
if ($this->authorityPHIDs !== null) {
|
||||
$authority_phids = $this->expandAuthority($this->authorityPHIDs);
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'o.userPHID IN (%Ls)',
|
||||
$all_phids);
|
||||
$authority_phids);
|
||||
}
|
||||
|
||||
if ($this->ownerPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'o.userPHID IN (%Ls)',
|
||||
$this->ownerPHIDs);
|
||||
}
|
||||
|
||||
if ($this->paths !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'rpath.path IN (%Ls)',
|
||||
$this->getFragmentsForPaths($this->paths));
|
||||
}
|
||||
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'p.status IN (%Ls)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
if (strlen($this->namePrefix)) {
|
||||
|
@ -115,15 +222,29 @@ final class PhabricatorOwnersPackageQuery
|
|||
phutil_utf8_strtolower($this->namePrefix));
|
||||
}
|
||||
|
||||
if ($this->controlMap) {
|
||||
$clauses = array();
|
||||
foreach ($this->controlMap as $repository_phid => $paths) {
|
||||
$fragments = $this->getFragmentsForPaths($paths);
|
||||
|
||||
$clauses[] = qsprintf(
|
||||
$conn,
|
||||
'(rpath.repositoryPHID = %s AND rpath.path IN (%Ls))',
|
||||
$repository_phid,
|
||||
$fragments);
|
||||
}
|
||||
$where[] = implode(' OR ', $clauses);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function shouldGroupQueryResultRows() {
|
||||
if ($this->repositoryPHIDs) {
|
||||
if ($this->shouldJoinOwnersTable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->ownerPHIDs) {
|
||||
if ($this->shouldJoinPathTable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -167,4 +288,109 @@ final class PhabricatorOwnersPackageQuery
|
|||
return 'p';
|
||||
}
|
||||
|
||||
private function shouldJoinOwnersTable() {
|
||||
if ($this->ownerPHIDs !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->authorityPHIDs !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function shouldJoinPathTable() {
|
||||
if ($this->repositoryPHIDs !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->paths !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->controlMap) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function expandAuthority(array $phids) {
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withMemberPHIDs($phids)
|
||||
->execute();
|
||||
$project_phids = mpull($projects, 'getPHID');
|
||||
|
||||
return array_fuse($phids) + array_fuse($project_phids);
|
||||
}
|
||||
|
||||
private function getFragmentsForPaths(array $paths) {
|
||||
$fragments = array();
|
||||
|
||||
foreach ($paths as $path) {
|
||||
foreach (PhabricatorOwnersPackage::splitPath($path) as $fragment) {
|
||||
$fragments[$fragment] = $fragment;
|
||||
}
|
||||
}
|
||||
|
||||
return $fragments;
|
||||
}
|
||||
|
||||
|
||||
/* -( Path Control )------------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of all packages which control a path or its parent directories,
|
||||
* ordered from weakest to strongest.
|
||||
*
|
||||
* The first package has the most specific claim on the path; the last
|
||||
* package has the most general claim. Multiple packages may have claims of
|
||||
* equal strength, so this ordering is primarily one of usability and
|
||||
* convenience.
|
||||
*
|
||||
* @return list<PhabricatorOwnersPackage> List of controlling packages.
|
||||
*/
|
||||
public function getControllingPackagesForPath($repository_phid, $path) {
|
||||
$path = (string)$path;
|
||||
|
||||
if (!isset($this->controlMap[$repository_phid][$path])) {
|
||||
throw new PhutilInvalidStateException('withControl');
|
||||
}
|
||||
|
||||
if ($this->controlResults === null) {
|
||||
throw new PhutilInvalidStateException('execute');
|
||||
}
|
||||
|
||||
$packages = $this->controlResults;
|
||||
|
||||
$matches = array();
|
||||
foreach ($packages as $package_id => $package) {
|
||||
$best_match = null;
|
||||
$include = false;
|
||||
|
||||
foreach ($package->getPaths() as $package_path) {
|
||||
$strength = $package_path->getPathMatchStrength($path);
|
||||
if ($strength > $best_match) {
|
||||
$best_match = $strength;
|
||||
$include = !$package_path->getExcluded();
|
||||
}
|
||||
}
|
||||
|
||||
if ($best_match && $include) {
|
||||
$matches[$package_id] = array(
|
||||
'strength' => $best_match,
|
||||
'package' => $package,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$matches = isort($matches, 'strength');
|
||||
$matches = array_reverse($matches);
|
||||
|
||||
return array_values(ipull($matches, 'package'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,29 +18,47 @@ final class PhabricatorOwnersPackageSearchEngine
|
|||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->setLabel(pht('Owners'))
|
||||
->setKey('ownerPHIDs')
|
||||
->setAliases(array('owner', 'owners'))
|
||||
->setLabel(pht('Authority'))
|
||||
->setKey('authorityPHIDs')
|
||||
->setAliases(array('authority', 'authorities'))
|
||||
->setDatasource(new PhabricatorProjectOrUserDatasource()),
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->setLabel(pht('Repositories'))
|
||||
->setKey('repositoryPHIDs')
|
||||
->setAliases(array('repository', 'repositories'))
|
||||
->setDatasource(new DiffusionRepositoryDatasource()),
|
||||
id(new PhabricatorSearchStringListField())
|
||||
->setLabel(pht('Paths'))
|
||||
->setKey('paths')
|
||||
->setAliases(array('path')),
|
||||
id(new PhabricatorSearchCheckboxesField())
|
||||
->setKey('statuses')
|
||||
->setLabel(pht('Status'))
|
||||
->setOptions(
|
||||
id(new PhabricatorOwnersPackage())
|
||||
->getStatusNameMap()),
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['ownerPHIDs']) {
|
||||
$query->withOwnerPHIDs($map['ownerPHIDs']);
|
||||
if ($map['authorityPHIDs']) {
|
||||
$query->withAuthorityPHIDs($map['authorityPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['repositoryPHIDs']) {
|
||||
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['paths']) {
|
||||
$query->withPaths($map['paths']);
|
||||
}
|
||||
|
||||
if ($map['statuses']) {
|
||||
$query->withStatuses($map['statuses']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
@ -52,10 +70,11 @@ final class PhabricatorOwnersPackageSearchEngine
|
|||
$names = array();
|
||||
|
||||
if ($this->requireViewer()->isLoggedIn()) {
|
||||
$names['owned'] = pht('Owned');
|
||||
$names['authority'] = pht('Owned');
|
||||
}
|
||||
|
||||
$names += array(
|
||||
'active' => pht('Active Packages'),
|
||||
'all' => pht('All Packages'),
|
||||
);
|
||||
|
||||
|
@ -69,9 +88,15 @@ final class PhabricatorOwnersPackageSearchEngine
|
|||
switch ($query_key) {
|
||||
case 'all':
|
||||
return $query;
|
||||
case 'owned':
|
||||
case 'active':
|
||||
return $query->setParameter(
|
||||
'ownerPHIDs',
|
||||
'statuses',
|
||||
array(
|
||||
PhabricatorOwnersPackage::STATUS_ACTIVE,
|
||||
));
|
||||
case 'authority':
|
||||
return $query->setParameter(
|
||||
'authorityPHIDs',
|
||||
array($this->requireViewer()->getPHID()));
|
||||
}
|
||||
|
||||
|
@ -97,6 +122,10 @@ final class PhabricatorOwnersPackageSearchEngine
|
|||
->setHeader($package->getName())
|
||||
->setHref('/owners/package/'.$id.'/');
|
||||
|
||||
if ($package->isArchived()) {
|
||||
$item->setDisabled(true);
|
||||
}
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,11 +12,20 @@ final class PhabricatorOwnersPackage
|
|||
protected $description;
|
||||
protected $primaryOwnerPHID;
|
||||
protected $mailKey;
|
||||
protected $status;
|
||||
|
||||
private $paths = self::ATTACHABLE;
|
||||
private $owners = self::ATTACHABLE;
|
||||
|
||||
const STATUS_ACTIVE = 'active';
|
||||
const STATUS_ARCHIVED = 'archived';
|
||||
|
||||
public static function initializeNewPackage(PhabricatorUser $actor) {
|
||||
return id(new PhabricatorOwnersPackage())
|
||||
->setAuditingEnabled(0)
|
||||
->setPrimaryOwnerPHID($actor->getPHID());
|
||||
->attachPaths(array())
|
||||
->setStatus(self::STATUS_ACTIVE)
|
||||
->attachOwners(array());
|
||||
}
|
||||
|
||||
public function getCapabilities() {
|
||||
|
@ -37,6 +46,13 @@ final class PhabricatorOwnersPackage
|
|||
return null;
|
||||
}
|
||||
|
||||
public static function getStatusNameMap() {
|
||||
return array(
|
||||
self::STATUS_ACTIVE => pht('Active'),
|
||||
self::STATUS_ARCHIVED => pht('Archived'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
// This information is better available from the history table.
|
||||
|
@ -49,6 +65,7 @@ final class PhabricatorOwnersPackage
|
|||
'primaryOwnerPHID' => 'phid?',
|
||||
'auditingEnabled' => 'bool',
|
||||
'mailKey' => 'bytes20',
|
||||
'status' => 'text32',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_phid' => null,
|
||||
|
@ -77,6 +94,10 @@ final class PhabricatorOwnersPackage
|
|||
return parent::save();
|
||||
}
|
||||
|
||||
public function isArchived() {
|
||||
return ($this->getStatus() == self::STATUS_ARCHIVED);
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
if (!$this->getID()) {
|
||||
|
@ -225,17 +246,40 @@ final class PhabricatorOwnersPackage
|
|||
return $ids;
|
||||
}
|
||||
|
||||
private static function splitPath($path) {
|
||||
$result = array('/');
|
||||
public static function splitPath($path) {
|
||||
$trailing_slash = preg_match('@/$@', $path) ? '/' : '';
|
||||
$path = trim($path, '/');
|
||||
$parts = explode('/', $path);
|
||||
|
||||
$result = array();
|
||||
while (count($parts)) {
|
||||
$result[] = '/'.implode('/', $parts).$trailing_slash;
|
||||
$trailing_slash = '/';
|
||||
array_pop($parts);
|
||||
}
|
||||
return $result;
|
||||
$result[] = '/';
|
||||
|
||||
return array_reverse($result);
|
||||
}
|
||||
|
||||
public function attachPaths(array $paths) {
|
||||
assert_instances_of($paths, 'PhabricatorOwnersPath');
|
||||
$this->paths = $paths;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPaths() {
|
||||
return $this->assertAttached($this->paths);
|
||||
}
|
||||
|
||||
public function attachOwners(array $owners) {
|
||||
assert_instances_of($owners, 'PhabricatorOwnersOwner');
|
||||
$this->owners = $owners;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOwners() {
|
||||
return $this->assertAttached($this->owners);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ final class PhabricatorOwnersPackageTransaction
|
|||
const TYPE_AUDITING = 'owners.auditing';
|
||||
const TYPE_DESCRIPTION = 'owners.description';
|
||||
const TYPE_PATHS = 'owners.paths';
|
||||
const TYPE_STATUS = 'owners.status';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'owners';
|
||||
|
@ -25,14 +26,6 @@ final class PhabricatorOwnersPackageTransaction
|
|||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_PRIMARY:
|
||||
if ($old) {
|
||||
$phids[] = $old;
|
||||
}
|
||||
if ($new) {
|
||||
$phids[] = $new;
|
||||
}
|
||||
break;
|
||||
case self::TYPE_OWNERS:
|
||||
$add = array_diff($new, $old);
|
||||
foreach ($add as $phid) {
|
||||
|
@ -55,6 +48,9 @@ final class PhabricatorOwnersPackageTransaction
|
|||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_DESCRIPTION:
|
||||
return ($old === null);
|
||||
case self::TYPE_PRIMARY:
|
||||
// TODO: Eventually, remove these transactions entirely.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,12 +72,6 @@ final class PhabricatorOwnersPackageTransaction
|
|||
$old,
|
||||
$new);
|
||||
}
|
||||
case self::TYPE_PRIMARY:
|
||||
return pht(
|
||||
'%s changed the primary owner for this package from %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($old),
|
||||
$this->renderHandleLink($new));
|
||||
case self::TYPE_OWNERS:
|
||||
$add = array_diff($new, $old);
|
||||
$rem = array_diff($old, $new);
|
||||
|
@ -126,6 +116,16 @@ final class PhabricatorOwnersPackageTransaction
|
|||
return pht(
|
||||
'%s updated paths for this package.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
case self::TYPE_STATUS:
|
||||
if ($new == PhabricatorOwnersPackage::STATUS_ACTIVE) {
|
||||
return pht(
|
||||
'%s activated this package.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
} else if ($new == PhabricatorOwnersPackage::STATUS_ARCHIVED) {
|
||||
return pht(
|
||||
'%s archived this package.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
|
|
|
@ -70,4 +70,36 @@ final class PhabricatorOwnersPath extends PhabricatorOwnersDAO {
|
|||
return isset($set[$ref['repositoryPHID']][$ref['path']][$ref['excluded']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of directory matches between this path specification and
|
||||
* some real path.
|
||||
*/
|
||||
public function getPathMatchStrength($path) {
|
||||
$this_path = $this->getPath();
|
||||
|
||||
if ($this_path === '/') {
|
||||
// The root path "/" just matches everything with strength 1.
|
||||
return 1;
|
||||
}
|
||||
|
||||
$self_fragments = PhabricatorOwnersPackage::splitPath($this_path);
|
||||
$path_fragments = PhabricatorOwnersPackage::splitPath($path);
|
||||
|
||||
$self_count = count($self_fragments);
|
||||
$path_count = count($path_fragments);
|
||||
if ($self_count > $path_count) {
|
||||
// If this path is longer (and therefor more specific) than the target
|
||||
// path, we don't match it at all.
|
||||
return 0;
|
||||
}
|
||||
|
||||
for ($ii = 0; $ii < $self_count; $ii++) {
|
||||
if ($self_fragments[$ii] != $path_fragments[$ii]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $self_count;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,10 +42,6 @@ final class PhabricatorPonderApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
public function isPrototype() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/Q(?P<id>[1-9]\d*)'
|
||||
|
|
|
@ -19,11 +19,12 @@ final class PonderAnswerCommentController extends PonderController {
|
|||
}
|
||||
|
||||
$is_preview = $request->isPreviewRequest();
|
||||
// $draft = PhabricatorDraft::buildFromRequest($request);
|
||||
|
||||
$qid = $answer->getQuestion()->getID();
|
||||
$aid = $answer->getID();
|
||||
$view_uri = "/Q{$qid}#A{$aid}";
|
||||
|
||||
// TODO, this behaves badly when redirecting to the answer
|
||||
$view_uri = "/Q{$qid}";
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PonderAnswerTransaction())
|
||||
|
@ -46,9 +47,6 @@ final class PonderAnswerCommentController extends PonderController {
|
|||
->setException($ex);
|
||||
}
|
||||
|
||||
// if ($draft) {
|
||||
// $draft->replaceOrDelete();
|
||||
// }
|
||||
|
||||
if ($request->isAjax() && $is_preview) {
|
||||
return id(new PhabricatorApplicationTransactionResponse())
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
final class PonderAnswerHistoryController extends PonderController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
final class PonderQuestionHistoryController extends PonderController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
final class PonderQuestionViewController extends PonderController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
@ -205,35 +209,28 @@ final class PonderQuestionViewController extends PonderController {
|
|||
private function buildAnswers(array $answers) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$xactions = id(new PonderAnswerTransactionQuery())
|
||||
->setViewer($viewer)
|
||||
->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))
|
||||
->withObjectPHIDs(mpull($answers, 'getPHID'))
|
||||
->execute();
|
||||
|
||||
$engine = id(new PhabricatorMarkupEngine())
|
||||
->setViewer($viewer);
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->getComment()) {
|
||||
$engine->addObject(
|
||||
$xaction->getComment(),
|
||||
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
||||
}
|
||||
}
|
||||
$engine->process();
|
||||
|
||||
$xaction_groups = mgroup($xactions, 'getObjectPHID');
|
||||
$author_phids = mpull($answers, 'getAuthorPHID');
|
||||
$handles = $this->loadViewerHandles($author_phids);
|
||||
$answers_sort = array_reverse(msort($answers, 'getVoteCount'));
|
||||
|
||||
$view = array();
|
||||
foreach ($answers as $answer) {
|
||||
$xactions = idx($xaction_groups, $answer->getPHID(), array());
|
||||
foreach ($answers_sort as $answer) {
|
||||
$id = $answer->getID();
|
||||
$handle = $handles[$answer->getAuthorPHID()];
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$answer,
|
||||
id(new PonderAnswerTransactionQuery())
|
||||
->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)));
|
||||
$xactions = $timeline->getTransactions();
|
||||
|
||||
|
||||
$view[] = id(new PonderAnswerView())
|
||||
->setUser($viewer)
|
||||
->setAnswer($answer)
|
||||
->setTransactions($xactions)
|
||||
->setMarkupEngine($engine);
|
||||
->setTimeline($timeline)
|
||||
->setHandle($handle);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -86,16 +86,17 @@ final class PonderAnswerEditor extends PonderEditor {
|
|||
}
|
||||
|
||||
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
||||
$question = $object->getQuestion();
|
||||
return id(new PonderQuestionReplyHandler())
|
||||
->setMailReceiver($question);
|
||||
return id(new PonderAnswerReplyHandler())
|
||||
->setMailReceiver($object);
|
||||
}
|
||||
|
||||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$question = $object->getQuestion();
|
||||
return parent::buildMailTemplate($question);
|
||||
}
|
||||
$id = $object->getID();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("ANSR{$id}")
|
||||
->addHeader('Thread-Topic', "ANSR{$id}");
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
PhabricatorLiskDAO $object,
|
||||
|
|
|
@ -7,23 +7,6 @@ abstract class PonderEditor
|
|||
return 'PhabricatorPonderApplication';
|
||||
}
|
||||
|
||||
protected function shouldPublishFeedStory(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$id = $object->getID();
|
||||
$title = $object->getTitle();
|
||||
$original_title = $object->getOriginalTitle();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("Q{$id}: {$title}")
|
||||
->addHeader('Thread-Topic', "Q{$id}: {$original_title}");
|
||||
}
|
||||
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
return array(
|
||||
$object->getAuthorPHID(),
|
||||
|
|
|
@ -177,30 +177,13 @@ final class PonderQuestionEditor
|
|||
return true;
|
||||
}
|
||||
|
||||
protected function getFeedStoryType() {
|
||||
return 'PonderTransactionFeedStory';
|
||||
}
|
||||
|
||||
protected function getFeedStoryData(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$data = parent::getFeedStoryData($object, $xactions);
|
||||
$answer = $this->getAnswer();
|
||||
if ($answer) {
|
||||
$data['answerPHID'] = $answer->getPHID();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function shouldImplyCC(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PonderQuestionTransaction::TYPE_ANSWERS:
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::shouldImplyCC($object, $xaction);
|
||||
|
@ -209,7 +192,25 @@ final class PonderQuestionEditor
|
|||
protected function shouldSendMail(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
return true;
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PonderQuestionTransaction::TYPE_ANSWERS:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function shouldPublishFeedStory(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PonderQuestionTransaction::TYPE_ANSWERS:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMailTagsMap() {
|
||||
|
@ -230,6 +231,16 @@ final class PonderQuestionEditor
|
|||
->setMailReceiver($object);
|
||||
}
|
||||
|
||||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$id = $object->getID();
|
||||
$title = $object->getTitle();
|
||||
$original_title = $object->getOriginalTitle();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("Q{$id}: {$title}")
|
||||
->addHeader('Thread-Topic', "Q{$id}: {$original_title}");
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
@ -248,14 +259,6 @@ final class PonderQuestionEditor
|
|||
$body->addRawSection($new);
|
||||
}
|
||||
}
|
||||
// If the user gave an answer, add the answer text. Also update
|
||||
// the header and uri to be more answer-specific.
|
||||
if ($type == PonderQuestionTransaction::TYPE_ANSWERS) {
|
||||
$answer = $this->getAnswer();
|
||||
$body->addRawSection($answer->getContent());
|
||||
$header = pht('ANSWER DETAIL');
|
||||
$uri = $answer->getURI();
|
||||
}
|
||||
}
|
||||
|
||||
$body->addLinkSection(
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PonderTransactionFeedStory
|
||||
extends PhabricatorApplicationTransactionFeedStory {
|
||||
|
||||
public function getRequiredObjectPHIDs() {
|
||||
$phids = parent::getRequiredObjectPHIDs();
|
||||
$answer_phid = $this->getValue('answerPHID');
|
||||
if ($answer_phid) {
|
||||
$phids[] = $answer_phid;
|
||||
}
|
||||
return $phids;
|
||||
}
|
||||
}
|
27
src/applications/ponder/mail/PonderAnswerMailReceiver.php
Normal file
27
src/applications/ponder/mail/PonderAnswerMailReceiver.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
final class PonderAnswerMailReceiver extends PhabricatorObjectMailReceiver {
|
||||
|
||||
public function isEnabled() {
|
||||
$app_class = 'PhabricatorPonderApplication';
|
||||
return PhabricatorApplication::isClassInstalled($app_class);
|
||||
}
|
||||
|
||||
protected function getObjectPattern() {
|
||||
return 'ANSR[1-9]\d*';
|
||||
}
|
||||
|
||||
protected function loadObject($pattern, PhabricatorUser $viewer) {
|
||||
$id = (int)trim($pattern, 'ANSR');
|
||||
|
||||
return id(new PonderAnswerQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
}
|
||||
|
||||
protected function getTransactionReplyHandler() {
|
||||
return new PonderAnswerReplyHandler();
|
||||
}
|
||||
|
||||
}
|
16
src/applications/ponder/mail/PonderAnswerReplyHandler.php
Normal file
16
src/applications/ponder/mail/PonderAnswerReplyHandler.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class PonderAnswerReplyHandler
|
||||
extends PhabricatorApplicationTransactionReplyHandler {
|
||||
|
||||
public function validateMailReceiver($mail_receiver) {
|
||||
if (!($mail_receiver instanceof PonderAnswer)) {
|
||||
throw new Exception(pht('Mail receiver is not a %s!', 'PonderAnswer'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getObjectPrefix() {
|
||||
return 'ANSR';
|
||||
}
|
||||
|
||||
}
|
|
@ -129,7 +129,7 @@ final class PonderQuestionQuery
|
|||
return $questions;
|
||||
}
|
||||
|
||||
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn_r) {
|
||||
$joins = array();
|
||||
|
||||
if ($this->answererPHIDs) {
|
||||
|
@ -141,7 +141,7 @@ final class PonderQuestionQuery
|
|||
$this->answererPHIDs);
|
||||
}
|
||||
|
||||
return implode(' ', $joins);
|
||||
return $joins;
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
|
|
|
@ -96,7 +96,7 @@ final class PonderQuestionSearchEngine
|
|||
array($this->requireViewer()->getPHID()));
|
||||
case 'answered':
|
||||
return $query->setParameter(
|
||||
'answererPHIDs',
|
||||
'answerers',
|
||||
array($this->requireViewer()->getPHID()));
|
||||
}
|
||||
|
||||
|
|
|
@ -336,23 +336,4 @@ final class PonderQuestionTransaction
|
|||
return reset($add);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generally, the answer object is only available if the transaction
|
||||
* type is `self::TYPE_ANSWERS`.
|
||||
*
|
||||
* Some stories - notably ones made before D7027 - will be of the more
|
||||
* generic @{class:PhabricatorApplicationTransactionFeedStory}. These
|
||||
* poor stories won't have the PonderAnswer loaded, and thus will have
|
||||
* less cool information.
|
||||
*/
|
||||
private function getNewAnswerObject(PhabricatorFeedStory $story) {
|
||||
if ($story instanceof PonderTransactionFeedStory) {
|
||||
$answer_phid = $this->getNewAnswerPHID();
|
||||
if ($answer_phid) {
|
||||
return $story->getObject($answer_phid);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,13 +34,42 @@ final class PonderAddAnswerView extends AphrontView {
|
|||
$info_panel = null;
|
||||
if ($question->getStatus() != PonderQuestionStatus::STATUS_OPEN) {
|
||||
$info_panel = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
||||
->appendChild(
|
||||
pht(
|
||||
'This question has been marked as closed,
|
||||
but you can still leave a new answer.'));
|
||||
}
|
||||
|
||||
$box_style = null;
|
||||
$own_question = null;
|
||||
$hide_action_id = celerity_generate_unique_node_id();
|
||||
$show_action_id = celerity_generate_unique_node_id();
|
||||
if ($question->getAuthorPHID() == $viewer->getPHID()) {
|
||||
$box_style = 'display: none;';
|
||||
$open_link = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'sigil' => 'reveal-content',
|
||||
'class' => 'mml',
|
||||
'id' => $hide_action_id,
|
||||
'href' => '#',
|
||||
'meta' => array(
|
||||
'showIDs' => array($show_action_id),
|
||||
'hideIDs' => array($hide_action_id),
|
||||
),
|
||||
),
|
||||
pht('Add an answer.'));
|
||||
$own_question = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setID($hide_action_id)
|
||||
->appendChild(
|
||||
pht(
|
||||
'This is your own question. You are welcome to provide
|
||||
an answer if you have found a resolution.'))
|
||||
->appendChild($open_link);
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Add Answer'));
|
||||
|
||||
|
@ -69,6 +98,15 @@ final class PonderAddAnswerView extends AphrontView {
|
|||
$box->setInfoView($info_panel);
|
||||
}
|
||||
|
||||
return $box;
|
||||
$box = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'style' => $box_style,
|
||||
'class' => 'mlt',
|
||||
'id' => $show_action_id,
|
||||
),
|
||||
$box);
|
||||
|
||||
return array($own_question, $box);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ final class PonderAnswerView extends AphrontTagView {
|
|||
|
||||
private $answer;
|
||||
private $transactions;
|
||||
private $engine;
|
||||
private $timeline;
|
||||
private $handle;
|
||||
|
||||
public function setAnswer($answer) {
|
||||
$this->answer = $answer;
|
||||
|
@ -16,8 +17,13 @@ final class PonderAnswerView extends AphrontTagView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setMarkupEngine(PhabricatorMarkupEngine $engine) {
|
||||
$this->engine = $engine;
|
||||
public function setTimeline($timeline) {
|
||||
$this->timeline = $timeline;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHandle($handle) {
|
||||
$this->handle = $handle;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -34,6 +40,7 @@ final class PonderAnswerView extends AphrontTagView {
|
|||
$status = $answer->getStatus();
|
||||
$author_phid = $answer->getAuthorPHID();
|
||||
$actions = $this->buildAnswerActions();
|
||||
$handle = $this->handle;
|
||||
$id = $answer->getID();
|
||||
|
||||
if ($status == PonderAnswerStatus::ANSWER_STATUS_HIDDEN) {
|
||||
|
@ -72,9 +79,11 @@ final class PonderAnswerView extends AphrontTagView {
|
|||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setUser($viewer)
|
||||
->setEpoch($answer->getDateCreated())
|
||||
->setHeader($viewer->renderHandle($author_phid))
|
||||
->addActionLink($action_button);
|
||||
->setEpoch($answer->getDateModified())
|
||||
->setHeader($handle->getName())
|
||||
->addActionLink($action_button)
|
||||
->setImage($handle->getImageURI())
|
||||
->setImageURL($handle->getURI());
|
||||
|
||||
$content = phutil_tag(
|
||||
'div',
|
||||
|
@ -95,17 +104,19 @@ final class PonderAnswerView extends AphrontTagView {
|
|||
->setCount(count($this->transactions));
|
||||
|
||||
$votes = $answer->getVoteCount();
|
||||
if ($votes) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIconFont('fa-thumbs-up');
|
||||
$helpful = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'ponder-footer-action',
|
||||
),
|
||||
array($votes, $icon));
|
||||
$footer->addAction($helpful);
|
||||
$vote_class = null;
|
||||
if ($votes > 0) {
|
||||
$vote_class = 'ponder-footer-action-helpful';
|
||||
}
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIconFont('fa-thumbs-up msr');
|
||||
$helpful = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'ponder-footer-action '.$vote_class,
|
||||
),
|
||||
array($icon, $votes));
|
||||
$footer->addAction($helpful);
|
||||
|
||||
$answer_view = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
|
@ -113,12 +124,6 @@ final class PonderAnswerView extends AphrontTagView {
|
|||
->appendChild($content)
|
||||
->appendChild($footer);
|
||||
|
||||
$transaction_view = id(new PhabricatorApplicationTransactionView())
|
||||
->setUser($viewer)
|
||||
->setObjectPHID($answer->getPHID())
|
||||
->setTransactions($this->transactions)
|
||||
->setMarkupEngine($this->engine);
|
||||
|
||||
$comment_view = id(new PhabricatorApplicationTransactionCommentView())
|
||||
->setUser($viewer)
|
||||
->setObjectPHID($answer->getPHID())
|
||||
|
@ -134,7 +139,7 @@ final class PonderAnswerView extends AphrontTagView {
|
|||
'style' => 'display: none;',
|
||||
),
|
||||
array(
|
||||
$transaction_view,
|
||||
$this->timeline,
|
||||
$comment_view,
|
||||
));
|
||||
|
||||
|
|
|
@ -36,9 +36,13 @@ final class PonderFooterView extends AphrontTagView {
|
|||
$content_id = $this->contentID;
|
||||
|
||||
if ($this->count == 0) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIconFont('fa-plus-circle msr');
|
||||
$text = pht('Add a Comment');
|
||||
} else {
|
||||
$text = pht('Show %s Comments', new PhutilNumber($this->count));
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIconFont('fa-comments msr');
|
||||
$text = pht('Show %d Comment(s)', new PhutilNumber($this->count));
|
||||
}
|
||||
|
||||
$actions = array();
|
||||
|
@ -54,7 +58,7 @@ final class PonderFooterView extends AphrontTagView {
|
|||
'hideIDs' => array($hide_action_id),
|
||||
),
|
||||
),
|
||||
$text);
|
||||
array($icon, $text));
|
||||
|
||||
$show_action = javelin_tag(
|
||||
'a',
|
||||
|
@ -69,12 +73,12 @@ final class PonderFooterView extends AphrontTagView {
|
|||
'hideIDs' => array($content_id, $show_action_id),
|
||||
),
|
||||
),
|
||||
pht('Hide Comments'));
|
||||
array($icon, pht('Hide Comments')));
|
||||
|
||||
$actions[] = $hide_action;
|
||||
$actions[] = $show_action;
|
||||
|
||||
return array($actions, $this->actions);
|
||||
return array($this->actions, $actions);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1055,6 +1055,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
}
|
||||
|
||||
if ($this->shouldPublishFeedStory($object, $xactions)) {
|
||||
|
||||
$mailed = array();
|
||||
foreach ($messages as $mail) {
|
||||
foreach ($mail->buildRecipientList() as $phid) {
|
||||
|
@ -2299,6 +2300,8 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
}
|
||||
}
|
||||
|
||||
$this->runHeraldMailRules($messages);
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
|
@ -3140,4 +3143,16 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
);
|
||||
}
|
||||
|
||||
private function runHeraldMailRules(array $messages) {
|
||||
foreach ($messages as $message) {
|
||||
$engine = new HeraldEngine();
|
||||
$adapter = id(new PhabricatorMailOutboundMailHeraldAdapter())
|
||||
->setObject($message);
|
||||
|
||||
$rules = $engine->loadRulesForAdapter($adapter);
|
||||
$effects = $engine->applyRules($rules, $adapter);
|
||||
$engine->applyEffects($effects, $adapter, $rules);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -642,23 +642,20 @@ abstract class PhabricatorApplicationTransaction
|
|||
$this->renderHandleLink($author_phid));
|
||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
return pht(
|
||||
'%s changed the visibility of this %s from "%s" to "%s".',
|
||||
'%s changed the visibility from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->getApplicationObjectTypeName(),
|
||||
$this->renderPolicyName($old, 'old'),
|
||||
$this->renderPolicyName($new, 'new'));
|
||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
return pht(
|
||||
'%s changed the edit policy of this %s from "%s" to "%s".',
|
||||
'%s changed the edit policy from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->getApplicationObjectTypeName(),
|
||||
$this->renderPolicyName($old, 'old'),
|
||||
$this->renderPolicyName($new, 'new'));
|
||||
case PhabricatorTransactions::TYPE_JOIN_POLICY:
|
||||
return pht(
|
||||
'%s changed the join policy of this %s from "%s" to "%s".',
|
||||
'%s changed the join policy from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->getApplicationObjectTypeName(),
|
||||
$this->renderPolicyName($old, 'old'),
|
||||
$this->renderPolicyName($new, 'new'));
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
|
|
|
@ -138,6 +138,25 @@ If the services tab looks fine, and particularly if a page is slow but the
|
|||
tool to understand problems in PHP is XHProf.
|
||||
|
||||
|
||||
Plugin: Startup
|
||||
===============
|
||||
|
||||
The "Startup" plugin shows information about startup phases. This information
|
||||
can provide insight about performance problems which occur before the profiler
|
||||
can start.
|
||||
|
||||
Normally, the profiler is the best tool for understanding runtime performance,
|
||||
but some work is performed before the profiler starts (for example, loading
|
||||
libraries and configuration). If there is a substantial difference between the
|
||||
wall time reported by the profiler and the "Entire Page" cost reported by the
|
||||
Services tab, the Startup tab can help account for that time.
|
||||
|
||||
It is normal for starting the profiler to increase the cost of the page
|
||||
somewhat: the profiler itself adds overhead while it is running, and the page
|
||||
must do some work after the profiler is stopped to save the profile and
|
||||
complete other shutdown operations.
|
||||
|
||||
|
||||
Plugin: XHProf
|
||||
==============
|
||||
|
||||
|
|
333
src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php
Normal file
333
src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php
Normal file
|
@ -0,0 +1,333 @@
|
|||
<?php
|
||||
|
||||
final class PHUIDiffTableOfContentsItemView extends AphrontView {
|
||||
|
||||
private $changeset;
|
||||
private $isVisible = true;
|
||||
private $anchor;
|
||||
private $coverage;
|
||||
private $coverageID;
|
||||
private $context;
|
||||
private $packages;
|
||||
|
||||
public function setChangeset(DifferentialChangeset $changeset) {
|
||||
$this->changeset = $changeset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getChangeset() {
|
||||
return $this->changeset;
|
||||
}
|
||||
|
||||
public function setIsVisible($is_visible) {
|
||||
$this->isVisible = $is_visible;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsVisible() {
|
||||
return $this->isVisible;
|
||||
}
|
||||
|
||||
public function setAnchor($anchor) {
|
||||
$this->anchor = $anchor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAnchor() {
|
||||
return $this->anchor;
|
||||
}
|
||||
|
||||
public function setCoverage($coverage) {
|
||||
$this->coverage = $coverage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCoverage() {
|
||||
return $this->coverage;
|
||||
}
|
||||
|
||||
public function setCoverageID($coverage_id) {
|
||||
$this->coverageID = $coverage_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCoverageID() {
|
||||
return $this->coverageID;
|
||||
}
|
||||
|
||||
public function setContext($context) {
|
||||
$this->context = $context;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContext() {
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
public function setPackages(array $packages) {
|
||||
assert_instances_of($packages, 'PhabricatorOwnersPackage');
|
||||
$this->packages = mpull($packages, null, 'getPHID');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPackages() {
|
||||
return $this->packages;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$changeset = $this->getChangeset();
|
||||
|
||||
$cells = array();
|
||||
|
||||
$cells[] = $this->getContext();
|
||||
|
||||
$cells[] = $this->renderPathChangeCharacter();
|
||||
$cells[] = $this->renderPropertyChangeCharacter();
|
||||
$cells[] = $this->renderPropertyChangeDescription();
|
||||
|
||||
$link = $this->renderChangesetLink();
|
||||
$lines = $this->renderChangesetLines();
|
||||
$meta = $this->renderChangesetMetadata();
|
||||
|
||||
$cells[] = array(
|
||||
$link,
|
||||
$lines,
|
||||
$meta,
|
||||
);
|
||||
|
||||
$cells[] = $this->renderCoverage();
|
||||
$cells[] = $this->renderModifiedCoverage();
|
||||
|
||||
$cells[] = $this->renderPackages();
|
||||
|
||||
return $cells;
|
||||
}
|
||||
|
||||
private function renderPathChangeCharacter() {
|
||||
$changeset = $this->getChangeset();
|
||||
$type = $changeset->getChangeType();
|
||||
|
||||
$color = DifferentialChangeType::getSummaryColorForChangeType($type);
|
||||
$char = DifferentialChangeType::getSummaryCharacterForChangeType($type);
|
||||
$title = DifferentialChangeType::getFullNameForChangeType($type);
|
||||
|
||||
return javelin_tag(
|
||||
'span',
|
||||
array(
|
||||
'sigil' => 'has-tooltip',
|
||||
'meta' => array(
|
||||
'tip' => $title,
|
||||
'align' => 'E',
|
||||
),
|
||||
'class' => 'phui-text-'.$color,
|
||||
),
|
||||
$char);
|
||||
}
|
||||
|
||||
private function renderPropertyChangeCharacter() {
|
||||
$changeset = $this->getChangeset();
|
||||
|
||||
$old = $changeset->getOldProperties();
|
||||
$new = $changeset->getNewProperties();
|
||||
|
||||
if ($old === $new) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return javelin_tag(
|
||||
'span',
|
||||
array(
|
||||
'sigil' => 'has-tooltip',
|
||||
'meta' => array(
|
||||
'tip' => pht('Properties Modified'),
|
||||
'align' => 'E',
|
||||
'size' => 200,
|
||||
),
|
||||
),
|
||||
'M');
|
||||
}
|
||||
|
||||
private function renderPropertyChangeDescription() {
|
||||
$changeset = $this->getChangeset();
|
||||
|
||||
$file_type = $changeset->getFileType();
|
||||
|
||||
$desc = DifferentialChangeType::getShortNameForFileType($file_type);
|
||||
if ($desc === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return pht('(%s)', $desc);
|
||||
}
|
||||
|
||||
private function renderChangesetLink() {
|
||||
$anchor = $this->getAnchor();
|
||||
|
||||
$changeset = $this->getChangeset();
|
||||
$name = $changeset->getDisplayFilename();
|
||||
|
||||
$change_type = $changeset->getChangeType();
|
||||
if (DifferentialChangeType::isOldLocationChangeType($change_type)) {
|
||||
$away = $changeset->getAwayPaths();
|
||||
if (count($away) == 1) {
|
||||
if ($change_type == DifferentialChangeType::TYPE_MOVE_AWAY) {
|
||||
$right_arrow = "\xE2\x86\x92";
|
||||
$name = $this->renderRename($name, head($away), $right_arrow);
|
||||
}
|
||||
}
|
||||
} else if ($change_type == DifferentialChangeType::TYPE_MOVE_HERE) {
|
||||
$left_arrow = "\xE2\x86\x90";
|
||||
$name = $this->renderRename($name, $changeset->getOldFile(), $left_arrow);
|
||||
}
|
||||
|
||||
return javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '#'.$anchor,
|
||||
'sigil' => 'differential-load',
|
||||
'meta' => array(
|
||||
'id' => 'diff-'.$anchor,
|
||||
),
|
||||
),
|
||||
$name);
|
||||
}
|
||||
|
||||
private function renderChangesetLines() {
|
||||
$changeset = $this->getChangeset();
|
||||
|
||||
$line_count = $changeset->getAffectedLineCount();
|
||||
if (!$line_count) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ' '.pht('(%d line(s))', $line_count);
|
||||
}
|
||||
|
||||
private function renderCoverage() {
|
||||
$not_applicable = '-';
|
||||
|
||||
$coverage = $this->getCoverage();
|
||||
if (!strlen($coverage)) {
|
||||
return $not_applicable;
|
||||
}
|
||||
|
||||
$covered = substr_count($coverage, 'C');
|
||||
$not_covered = substr_count($coverage, 'U');
|
||||
|
||||
if (!$not_covered && !$covered) {
|
||||
return $not_applicable;
|
||||
}
|
||||
|
||||
return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered)));
|
||||
}
|
||||
|
||||
private function renderModifiedCoverage() {
|
||||
$not_applicable = '-';
|
||||
|
||||
$coverage = $this->getCoverage();
|
||||
if (!strlen($coverage)) {
|
||||
return $not_applicable;
|
||||
}
|
||||
|
||||
if ($this->getIsVisible()) {
|
||||
$label = pht('Loading...');
|
||||
} else {
|
||||
$label = pht('?');
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => $this->getCoverageID(),
|
||||
'class' => 'differential-mcoverage-loading',
|
||||
),
|
||||
$label);
|
||||
}
|
||||
|
||||
private function renderChangesetMetadata() {
|
||||
$changeset = $this->getChangeset();
|
||||
$type = $changeset->getChangeType();
|
||||
|
||||
$meta = array();
|
||||
if (DifferentialChangeType::isOldLocationChangeType($type)) {
|
||||
$away = $changeset->getAwayPaths();
|
||||
if (count($away) > 1) {
|
||||
if ($type == DifferentialChangeType::TYPE_MULTICOPY) {
|
||||
$meta[] = pht('Deleted after being copied to multiple locations:');
|
||||
} else {
|
||||
$meta[] = pht('Copied to multiple locations:');
|
||||
}
|
||||
foreach ($away as $path) {
|
||||
$meta[] = $path;
|
||||
}
|
||||
} else {
|
||||
if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) {
|
||||
// This case is handled when we render the path.
|
||||
} else {
|
||||
$meta[] = pht('Copied to %s', head($away));
|
||||
}
|
||||
}
|
||||
} else if ($type == DifferentialChangeType::TYPE_COPY_HERE) {
|
||||
$meta[] = pht('Copied from %s', $changeset->getOldFile());
|
||||
}
|
||||
|
||||
if (!$meta) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$meta = phutil_implode_html(phutil_tag('br'), $meta);
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'differential-toc-meta',
|
||||
),
|
||||
$meta);
|
||||
}
|
||||
|
||||
private function renderPackages() {
|
||||
$packages = $this->getPackages();
|
||||
if (!$packages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$viewer = $this->getUser();
|
||||
$package_phids = mpull($packages, 'getPHID');
|
||||
|
||||
return $viewer->renderHandleList($package_phids);
|
||||
}
|
||||
|
||||
private function renderRename($self, $other, $arrow) {
|
||||
$old = explode('/', $self);
|
||||
$new = explode('/', $other);
|
||||
|
||||
$start = count($old);
|
||||
foreach ($old as $index => $part) {
|
||||
if (!isset($new[$index]) || $part != $new[$index]) {
|
||||
$start = $index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$end = count($old);
|
||||
foreach (array_reverse($old) as $from_end => $part) {
|
||||
$index = count($new) - $from_end - 1;
|
||||
if (!isset($new[$index]) || $part != $new[$index]) {
|
||||
$end = $from_end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$rename =
|
||||
'{'.
|
||||
implode('/', array_slice($old, $start, count($old) - $end - $start)).
|
||||
' '.$arrow.' '.
|
||||
implode('/', array_slice($new, $start, count($new) - $end - $start)).
|
||||
'}';
|
||||
|
||||
array_splice($new, $start, count($new) - $end - $start, $rename);
|
||||
|
||||
return implode('/', $new);
|
||||
}
|
||||
|
||||
}
|
153
src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php
Normal file
153
src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php
Normal file
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
final class PHUIDiffTableOfContentsListView extends AphrontView {
|
||||
|
||||
private $items = array();
|
||||
private $authorityPackages;
|
||||
|
||||
public function addItem(PHUIDiffTableOfContentsItemView $item) {
|
||||
$this->items[] = $item;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAuthorityPackages(array $authority_packages) {
|
||||
assert_instances_of($authority_packages, 'PhabricatorOwnersPackage');
|
||||
$this->authorityPackages = $authority_packages;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuthorityPackages() {
|
||||
return $this->authorityPackages;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$this->requireResource('differential-core-view-css');
|
||||
$this->requireResource('differential-table-of-contents-css');
|
||||
$this->requireResource('phui-text-css');
|
||||
|
||||
Javelin::initBehavior('phabricator-tooltips');
|
||||
|
||||
if ($this->getAuthorityPackages()) {
|
||||
$authority = mpull($this->getAuthorityPackages(), null, 'getPHID');
|
||||
} else {
|
||||
$authority = array();
|
||||
}
|
||||
|
||||
$items = $this->items;
|
||||
|
||||
$rows = array();
|
||||
$rowc = array();
|
||||
foreach ($items as $item) {
|
||||
$item->setUser($this->getUser());
|
||||
$rows[] = $item->render();
|
||||
|
||||
$have_authority = false;
|
||||
|
||||
$packages = $item->getPackages();
|
||||
if ($packages) {
|
||||
if (array_intersect_key($packages, $authority)) {
|
||||
$have_authority = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($have_authority) {
|
||||
$rowc[] = 'highlighted';
|
||||
} else {
|
||||
$rowc[] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any item has content in these columns. If no item does, we'll
|
||||
// just hide them.
|
||||
$any_coverage = false;
|
||||
$any_context = false;
|
||||
$any_packages = false;
|
||||
foreach ($items as $item) {
|
||||
if ($item->getContext() !== null) {
|
||||
$any_context = true;
|
||||
}
|
||||
|
||||
if (strlen($item->getCoverage())) {
|
||||
$any_coverage = true;
|
||||
}
|
||||
|
||||
if ($item->getPackages() !== null) {
|
||||
$any_packages = true;
|
||||
}
|
||||
}
|
||||
|
||||
$reveal_link = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'sigil' => 'differential-reveal-all',
|
||||
'mustcapture' => true,
|
||||
'class' => 'button differential-toc-reveal-all',
|
||||
),
|
||||
pht('Show All Context'));
|
||||
|
||||
$buttons = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'differential-toc-buttons grouped',
|
||||
),
|
||||
$reveal_link);
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setRowClasses($rowc)
|
||||
->setHeaders(
|
||||
array(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
pht('Path'),
|
||||
pht('Coverage (All)'),
|
||||
pht('Coverage (Touched)'),
|
||||
pht('Packages'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'center',
|
||||
'differential-toc-char center',
|
||||
'differential-toc-prop center',
|
||||
'differential-toc-ftype center',
|
||||
'differential-toc-file wide',
|
||||
'differential-toc-cov',
|
||||
'differential-toc-cov',
|
||||
null,
|
||||
))
|
||||
->setColumnVisibility(
|
||||
array(
|
||||
$any_context,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
$any_coverage,
|
||||
$any_coverage,
|
||||
$any_packages,
|
||||
))
|
||||
->setDeviceVisibility(
|
||||
array(
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
));
|
||||
|
||||
$anchor = id(new PhabricatorAnchorView())
|
||||
->setAnchorName('toc')
|
||||
->setNavigationMarker(true);
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Table of Contents'))
|
||||
->setTable($table)
|
||||
->appendChild($anchor)
|
||||
->appendChild($buttons);
|
||||
}
|
||||
|
||||
}
|
|
@ -33,6 +33,7 @@ final class PhabricatorUSEnglishTranslation
|
|||
'%d diff(s)' => array('%d diff', '%d diffs'),
|
||||
|
||||
'%d Answer(s)' => array('%d Answer', '%d Answers'),
|
||||
'Show %d Comment(s)' => array('Show %d Comment', 'Show %d Comments'),
|
||||
|
||||
'%s DIFF LINK(S)' => array('DIFF LINK', 'DIFF LINKS'),
|
||||
'You successfully created %d diff(s).' => array(
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
* @task apocalypse In Case Of Apocalypse
|
||||
* @task validation Validation
|
||||
* @task ratelimit Rate Limiting
|
||||
* @task phases Startup Phase Timers
|
||||
*/
|
||||
final class PhabricatorStartup {
|
||||
|
||||
|
@ -43,6 +44,7 @@ final class PhabricatorStartup {
|
|||
private static $capturingOutput;
|
||||
private static $rawInput;
|
||||
private static $oldMemoryLimit;
|
||||
private static $phases;
|
||||
|
||||
// TODO: For now, disable rate limiting entirely by default. We need to
|
||||
// iterate on it a bit for Conduit, some of the specific score levels, and
|
||||
|
@ -89,10 +91,14 @@ final class PhabricatorStartup {
|
|||
|
||||
|
||||
/**
|
||||
* @param float Request start time, from `microtime(true)`.
|
||||
* @task hook
|
||||
*/
|
||||
public static function didStartup() {
|
||||
self::$startTime = microtime(true);
|
||||
public static function didStartup($start_time) {
|
||||
self::$startTime = $start_time;
|
||||
|
||||
self::$phases = array();
|
||||
|
||||
self::$accessLog = null;
|
||||
|
||||
static $registered;
|
||||
|
@ -854,4 +860,58 @@ final class PhabricatorStartup {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/* -( Startup Timers )----------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Record the beginning of a new startup phase.
|
||||
*
|
||||
* For phases which occur before @{class:PhabricatorStartup} loads, save the
|
||||
* time and record it with @{method:recordStartupPhase} after the class is
|
||||
* available.
|
||||
*
|
||||
* @param string Phase name.
|
||||
* @task phases
|
||||
*/
|
||||
public static function beginStartupPhase($phase) {
|
||||
self::recordStartupPhase($phase, microtime(true));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record the start time of a previously executed startup phase.
|
||||
*
|
||||
* For startup phases which occur after @{class:PhabricatorStartup} loads,
|
||||
* use @{method:beginStartupPhase} instead. This method can be used to
|
||||
* record a time before the class loads, then hand it over once the class
|
||||
* becomes available.
|
||||
*
|
||||
* @param string Phase name.
|
||||
* @param float Phase start time, from `microtime(true)`.
|
||||
* @task phases
|
||||
*/
|
||||
public static function recordStartupPhase($phase, $time) {
|
||||
self::$phases[$phase] = $time;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get information about startup phase timings.
|
||||
*
|
||||
* Sometimes, performance problems can occur before we start the profiler.
|
||||
* Since the profiler can't examine these phases, it isn't useful in
|
||||
* understanding their performance costs.
|
||||
*
|
||||
* Instead, the startup process marks when it enters various phases using
|
||||
* @{method:beginStartupPhase}. A later call to this method can retrieve this
|
||||
* information, which can be examined to gain greater insight into where
|
||||
* time was spent. The output is still crude, but better than nothing.
|
||||
*
|
||||
* @task phases
|
||||
*/
|
||||
public static function getPhases() {
|
||||
return self::$phases;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
<?php
|
||||
|
||||
$phabricator_root = dirname(dirname(__FILE__));
|
||||
require_once $phabricator_root.'/support/PhabricatorStartup.php';
|
||||
|
||||
// If the preamble script exists, load it.
|
||||
$preamble_path = $phabricator_root.'/support/preamble.php';
|
||||
if (file_exists($preamble_path)) {
|
||||
require_once $preamble_path;
|
||||
}
|
||||
|
||||
PhabricatorStartup::didStartup();
|
||||
phabricator_startup();
|
||||
|
||||
try {
|
||||
PhabricatorStartup::beginStartupPhase('libraries');
|
||||
PhabricatorStartup::loadCoreLibraries();
|
||||
|
||||
PhabricatorStartup::beginStartupPhase('purge');
|
||||
PhabricatorCaches::destroyRequestCache();
|
||||
|
||||
PhabricatorStartup::beginStartupPhase('sink');
|
||||
$sink = new AphrontPHPHTTPSink();
|
||||
|
||||
try {
|
||||
PhabricatorStartup::beginStartupPhase('run');
|
||||
AphrontApplicationConfiguration::runHTTPRequest($sink);
|
||||
} catch (Exception $ex) {
|
||||
try {
|
||||
|
@ -36,3 +32,24 @@ try {
|
|||
} catch (Exception $ex) {
|
||||
PhabricatorStartup::didEncounterFatalException('Core Exception', $ex, false);
|
||||
}
|
||||
|
||||
function phabricator_startup() {
|
||||
// Load the PhabricatorStartup class itself.
|
||||
$t_startup = microtime(true);
|
||||
$root = dirname(dirname(__FILE__));
|
||||
require_once $root.'/support/PhabricatorStartup.php';
|
||||
|
||||
// If the preamble script exists, load it.
|
||||
$t_preamble = microtime(true);
|
||||
$preamble_path = $root.'/support/preamble.php';
|
||||
if (file_exists($preamble_path)) {
|
||||
require_once $preamble_path;
|
||||
}
|
||||
|
||||
$t_hook = microtime(true);
|
||||
PhabricatorStartup::didStartup($t_startup);
|
||||
|
||||
PhabricatorStartup::recordStartupPhase('startup.init', $t_startup);
|
||||
PhabricatorStartup::recordStartupPhase('preamble', $t_preamble);
|
||||
PhabricatorStartup::recordStartupPhase('hook', $t_hook);
|
||||
}
|
||||
|
|
|
@ -95,223 +95,6 @@
|
|||
top: 142px;
|
||||
}
|
||||
|
||||
/* files widget */
|
||||
|
||||
.conpherence-widget-pane #widgets-files .no-files {
|
||||
width: 200px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: {$greytext};
|
||||
}
|
||||
|
||||
.device .conpherence-widget-pane #widgets-files .no-files {
|
||||
width: 60px;
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-files .file-entry {
|
||||
padding: 8px 0;
|
||||
margin: 0 4px 0 8px;
|
||||
border-bottom: 1px solid {$thinblueborder};
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-files .file-entry a {
|
||||
color: {$darkbluetext};
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-files .file-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
float: left;
|
||||
font-size: 24px;
|
||||
color: {$blue};
|
||||
margin: 2px 0 2px 4px;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-files .file-title {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: -4px;
|
||||
left: 0;
|
||||
overflow-x: hidden;
|
||||
width: 180px;
|
||||
font-weight: bold;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.conpherence-widget-pane #widgets-files .file-uploaded-by {
|
||||
color: {$lightgreytext};
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 180px;
|
||||
font-size: {$smallestfontsize};
|
||||
}
|
||||
|
||||
.device .conpherence-widget-pane #widgets-files .file-title,
|
||||
.device .conpherence-widget-pane #widgets-files .file-uploaded-by {
|
||||
width: 82%;
|
||||
}
|
||||
.device .conpherence-widget-pane #widgets-files .divider {
|
||||
width: 80%;
|
||||
margin: 8px 0px 0px 10%;
|
||||
}
|
||||
|
||||
/* calendar widget */
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view {
|
||||
width: 240px;
|
||||
}
|
||||
.device-phone .conpherence-widget-pane #widgets-calendar
|
||||
.aphront-multi-column-view {
|
||||
display: none;
|
||||
}
|
||||
.device-tablet .conpherence-widget-pane #widgets-calendar
|
||||
.aphront-multi-column-view {
|
||||
width: 100%;
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column {
|
||||
background: white;
|
||||
border-right: 1px solid {$thinblueborder};
|
||||
text-align: center;
|
||||
}
|
||||
.device-phone .conpherence-widget-pane #widgets-calendar
|
||||
.aphront-multi-column-view .aphront-multi-column-column {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.device-phone .conpherence-widget-pane #widgets-calendar
|
||||
.aphront-multi-column-fluid .aphront-multi-column-5-up
|
||||
.aphront-multi-column-column-outer {
|
||||
width: 20%;
|
||||
margin-bottom: 0px;
|
||||
float: left;
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane .no-events {
|
||||
color: {$lightgreytext};
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column-last {
|
||||
border-right: 0;
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .day-column,
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .day-column-active {
|
||||
color: #bfbfbf;
|
||||
background-color: white;
|
||||
font-weight: bold;
|
||||
padding: 0px 0px 10px 0px;
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .day-column-active {
|
||||
color: black;
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .present ,
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .sporadic ,
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .away {
|
||||
height: 10px;
|
||||
margin: 5px 0px 5px 0px;
|
||||
width: 100%;
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .present {
|
||||
background-color: white;
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .sporadic {
|
||||
background-color: rgb(222, 226, 232);
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.aphront-multi-column-column .away {
|
||||
background-color: rgb(102, 204, 255);
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.day-name {
|
||||
padding: 5px 0px 0px 0px;
|
||||
font-size: {$smallerfontsize};
|
||||
}
|
||||
.conpherence-widget-pane #widgets-calendar .aphront-multi-column-view
|
||||
.day-number {
|
||||
font-size: {$biggestfontsize};
|
||||
padding: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .day-header {
|
||||
overflow: hidden;
|
||||
border-top: 1px solid {$thinblueborder};
|
||||
padding: 8px 8px 0px 8px;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .day-header.today .day-name,
|
||||
.conpherence-widget-pane #widgets-calendar .day-header.today .day-date {
|
||||
color: {$sky};
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .day-header .day-name {
|
||||
float: left;
|
||||
color: {$bluetext};
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-size: {$smallestfontsize};
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .day-header .day-date {
|
||||
float: right;
|
||||
color: {$lightbluetext};
|
||||
font-size: {$smallestfontsize};
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .top-border {
|
||||
border-top: 1px solid {$thinblueborder};
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .user-status {
|
||||
padding: 10px 0px 10px 0px;
|
||||
margin: 0px 0px 0px 10px;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .user-status .icon {
|
||||
border-radius: 8px;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
margin-top: 4px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .sporadic .icon {
|
||||
background-color: {$orange};
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .away .icon {
|
||||
background-color: {$red};
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .user-status .description {
|
||||
width: 195px;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0 0 0 16px;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane #widgets-calendar .user-status .participant {
|
||||
font-size: {$smallestfontsize};
|
||||
color: {$lightgreytext};
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.device .conpherence-widget-pane #widgets-calendar .user-status .description,
|
||||
.device .conpherence-widget-pane #widgets-calendar .user-status .participant {
|
||||
/* we keep these short so no need to change the width */
|
||||
}
|
||||
|
||||
.conpherence-widget-pane .widget-icon {
|
||||
display: block;
|
||||
height: 14px;
|
||||
|
|
|
@ -83,17 +83,13 @@
|
|||
}
|
||||
|
||||
.device-phone .differential-inline-comment .inline-head-right {
|
||||
float: none;
|
||||
margin: 12px 0 4px 4px;
|
||||
}
|
||||
|
||||
.device-phone .differential-inline-comment .inline-head-right .mml {
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
|
||||
.device-phone .differential-inline-comment .differential-inline-comment-head {
|
||||
padding: 0px 0px 4px 8px;
|
||||
}
|
||||
|
||||
|
||||
/* - Sythetic Comment ---------------------------------------------------------
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue