mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-15 01:50:55 +01:00
(stable) Promote 2018 Week 5
This commit is contained in:
commit
4a338fd408
79 changed files with 2526 additions and 858 deletions
|
@ -1525,10 +1525,6 @@ phutil_register_library_map(array(
|
|||
'ManiphestEditProjectsCapability' => 'applications/maniphest/capability/ManiphestEditProjectsCapability.php',
|
||||
'ManiphestEditStatusCapability' => 'applications/maniphest/capability/ManiphestEditStatusCapability.php',
|
||||
'ManiphestEmailCommand' => 'applications/maniphest/command/ManiphestEmailCommand.php',
|
||||
'ManiphestExcelDefaultFormat' => 'applications/maniphest/export/ManiphestExcelDefaultFormat.php',
|
||||
'ManiphestExcelFormat' => 'applications/maniphest/export/ManiphestExcelFormat.php',
|
||||
'ManiphestExcelFormatTestCase' => 'applications/maniphest/export/__tests__/ManiphestExcelFormatTestCase.php',
|
||||
'ManiphestExportController' => 'applications/maniphest/controller/ManiphestExportController.php',
|
||||
'ManiphestGetTaskTransactionsConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestGetTaskTransactionsConduitAPIMethod.php',
|
||||
'ManiphestHovercardEngineExtension' => 'applications/maniphest/engineextension/ManiphestHovercardEngineExtension.php',
|
||||
'ManiphestInfoConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php',
|
||||
|
@ -2230,9 +2226,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBulkContentSource' => 'infrastructure/daemon/contentsource/PhabricatorBulkContentSource.php',
|
||||
'PhabricatorBulkEditGroup' => 'applications/transactions/bulk/PhabricatorBulkEditGroup.php',
|
||||
'PhabricatorBulkEngine' => 'applications/transactions/bulk/PhabricatorBulkEngine.php',
|
||||
'PhabricatorBulkManagementExportWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementExportWorkflow.php',
|
||||
'PhabricatorBulkManagementMakeSilentWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementMakeSilentWorkflow.php',
|
||||
'PhabricatorBulkManagementWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementWorkflow.php',
|
||||
'PhabricatorCSVExportFormat' => 'infrastructure/export/PhabricatorCSVExportFormat.php',
|
||||
'PhabricatorCSVExportFormat' => 'infrastructure/export/format/PhabricatorCSVExportFormat.php',
|
||||
'PhabricatorCacheDAO' => 'applications/cache/storage/PhabricatorCacheDAO.php',
|
||||
'PhabricatorCacheEngine' => 'applications/system/engine/PhabricatorCacheEngine.php',
|
||||
'PhabricatorCacheEngineExtension' => 'applications/system/engine/PhabricatorCacheEngineExtension.php',
|
||||
|
@ -2583,6 +2580,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCustomFieldEditEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldEditEngineExtension.php',
|
||||
'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php',
|
||||
'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php',
|
||||
'PhabricatorCustomFieldExportEngineExtension' => 'infrastructure/export/engine/PhabricatorCustomFieldExportEngineExtension.php',
|
||||
'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php',
|
||||
'PhabricatorCustomFieldHeraldAction' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldAction.php',
|
||||
'PhabricatorCustomFieldHeraldActionGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldActionGroup.php',
|
||||
|
@ -2840,15 +2838,20 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php',
|
||||
'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php',
|
||||
'PhabricatorEpochEditField' => 'applications/transactions/editfield/PhabricatorEpochEditField.php',
|
||||
'PhabricatorEpochExportField' => 'infrastructure/export/PhabricatorEpochExportField.php',
|
||||
'PhabricatorEpochExportField' => 'infrastructure/export/field/PhabricatorEpochExportField.php',
|
||||
'PhabricatorEvent' => 'infrastructure/events/PhabricatorEvent.php',
|
||||
'PhabricatorEventEngine' => 'infrastructure/events/PhabricatorEventEngine.php',
|
||||
'PhabricatorEventListener' => 'infrastructure/events/PhabricatorEventListener.php',
|
||||
'PhabricatorEventType' => 'infrastructure/events/constant/PhabricatorEventType.php',
|
||||
'PhabricatorExampleEventListener' => 'infrastructure/events/PhabricatorExampleEventListener.php',
|
||||
'PhabricatorExcelExportFormat' => 'infrastructure/export/format/PhabricatorExcelExportFormat.php',
|
||||
'PhabricatorExecFutureFileUploadSource' => 'applications/files/uploadsource/PhabricatorExecFutureFileUploadSource.php',
|
||||
'PhabricatorExportField' => 'infrastructure/export/PhabricatorExportField.php',
|
||||
'PhabricatorExportFormat' => 'infrastructure/export/PhabricatorExportFormat.php',
|
||||
'PhabricatorExportEngine' => 'infrastructure/export/engine/PhabricatorExportEngine.php',
|
||||
'PhabricatorExportEngineBulkJobType' => 'infrastructure/export/engine/PhabricatorExportEngineBulkJobType.php',
|
||||
'PhabricatorExportEngineExtension' => 'infrastructure/export/engine/PhabricatorExportEngineExtension.php',
|
||||
'PhabricatorExportField' => 'infrastructure/export/field/PhabricatorExportField.php',
|
||||
'PhabricatorExportFormat' => 'infrastructure/export/format/PhabricatorExportFormat.php',
|
||||
'PhabricatorExportFormatSetting' => 'infrastructure/export/engine/PhabricatorExportFormatSetting.php',
|
||||
'PhabricatorExtendedPolicyInterface' => 'applications/policy/interface/PhabricatorExtendedPolicyInterface.php',
|
||||
'PhabricatorExtendingPhabricatorConfigOptions' => 'applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php',
|
||||
'PhabricatorExtensionsSetupCheck' => 'applications/config/check/PhabricatorExtensionsSetupCheck.php',
|
||||
|
@ -3069,7 +3072,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorHomeProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeProfileMenuItem.php',
|
||||
'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php',
|
||||
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
|
||||
'PhabricatorIDExportField' => 'infrastructure/export/PhabricatorIDExportField.php',
|
||||
'PhabricatorIDExportField' => 'infrastructure/export/field/PhabricatorIDExportField.php',
|
||||
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
|
||||
'PhabricatorIDsSearchField' => 'applications/search/field/PhabricatorIDsSearchField.php',
|
||||
'PhabricatorIconDatasource' => 'applications/files/typeahead/PhabricatorIconDatasource.php',
|
||||
|
@ -3093,7 +3096,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php',
|
||||
'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php',
|
||||
'PhabricatorIntConfigType' => 'applications/config/type/PhabricatorIntConfigType.php',
|
||||
'PhabricatorIntExportField' => 'infrastructure/export/PhabricatorIntExportField.php',
|
||||
'PhabricatorIntExportField' => 'infrastructure/export/field/PhabricatorIntExportField.php',
|
||||
'PhabricatorInternalSetting' => 'applications/settings/setting/PhabricatorInternalSetting.php',
|
||||
'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php',
|
||||
'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php',
|
||||
|
@ -3103,7 +3106,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorIteratorFileUploadSource' => 'applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php',
|
||||
'PhabricatorJIRAAuthProvider' => 'applications/auth/provider/PhabricatorJIRAAuthProvider.php',
|
||||
'PhabricatorJSONConfigType' => 'applications/config/type/PhabricatorJSONConfigType.php',
|
||||
'PhabricatorJSONExportFormat' => 'infrastructure/export/PhabricatorJSONExportFormat.php',
|
||||
'PhabricatorJSONExportFormat' => 'infrastructure/export/format/PhabricatorJSONExportFormat.php',
|
||||
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php',
|
||||
'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php',
|
||||
'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
|
||||
|
@ -3126,9 +3129,11 @@ phutil_register_library_map(array(
|
|||
'PhabricatorLipsumManagementWorkflow' => 'applications/lipsum/management/PhabricatorLipsumManagementWorkflow.php',
|
||||
'PhabricatorLipsumMondrianArtist' => 'applications/lipsum/image/PhabricatorLipsumMondrianArtist.php',
|
||||
'PhabricatorLiskDAO' => 'infrastructure/storage/lisk/PhabricatorLiskDAO.php',
|
||||
'PhabricatorLiskExportEngineExtension' => 'infrastructure/export/engine/PhabricatorLiskExportEngineExtension.php',
|
||||
'PhabricatorLiskFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorLiskFulltextEngineExtension.php',
|
||||
'PhabricatorLiskSearchEngineExtension' => 'applications/search/engineextension/PhabricatorLiskSearchEngineExtension.php',
|
||||
'PhabricatorLiskSerializer' => 'infrastructure/storage/lisk/PhabricatorLiskSerializer.php',
|
||||
'PhabricatorListExportField' => 'infrastructure/export/field/PhabricatorListExportField.php',
|
||||
'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php',
|
||||
'PhabricatorLocalTimeTestCase' => 'view/__tests__/PhabricatorLocalTimeTestCase.php',
|
||||
'PhabricatorLocaleScopeGuard' => 'infrastructure/internationalization/scope/PhabricatorLocaleScopeGuard.php',
|
||||
|
@ -3423,10 +3428,11 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php',
|
||||
'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php',
|
||||
'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php',
|
||||
'PhabricatorPHIDExportField' => 'infrastructure/export/PhabricatorPHIDExportField.php',
|
||||
'PhabricatorPHIDExportField' => 'infrastructure/export/field/PhabricatorPHIDExportField.php',
|
||||
'PhabricatorPHIDInterface' => 'applications/phid/interface/PhabricatorPHIDInterface.php',
|
||||
'PhabricatorPHIDListEditField' => 'applications/transactions/editfield/PhabricatorPHIDListEditField.php',
|
||||
'PhabricatorPHIDListEditType' => 'applications/transactions/edittype/PhabricatorPHIDListEditType.php',
|
||||
'PhabricatorPHIDListExportField' => 'infrastructure/export/field/PhabricatorPHIDListExportField.php',
|
||||
'PhabricatorPHIDResolver' => 'applications/phid/resolver/PhabricatorPHIDResolver.php',
|
||||
'PhabricatorPHIDType' => 'applications/phid/type/PhabricatorPHIDType.php',
|
||||
'PhabricatorPHIDTypeTestCase' => 'applications/phid/type/__tests__/PhabricatorPHIDTypeTestCase.php',
|
||||
|
@ -3835,6 +3841,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php',
|
||||
'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
|
||||
'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
|
||||
'PhabricatorProjectsExportEngineExtension' => 'infrastructure/export/engine/PhabricatorProjectsExportEngineExtension.php',
|
||||
'PhabricatorProjectsFulltextEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php',
|
||||
'PhabricatorProjectsMembersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsMembersSearchEngineAttachment.php',
|
||||
'PhabricatorProjectsMembershipIndexEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsMembershipIndexEngineExtension.php',
|
||||
|
@ -4126,6 +4133,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSpacesController' => 'applications/spaces/controller/PhabricatorSpacesController.php',
|
||||
'PhabricatorSpacesDAO' => 'applications/spaces/storage/PhabricatorSpacesDAO.php',
|
||||
'PhabricatorSpacesEditController' => 'applications/spaces/controller/PhabricatorSpacesEditController.php',
|
||||
'PhabricatorSpacesExportEngineExtension' => 'infrastructure/export/engine/PhabricatorSpacesExportEngineExtension.php',
|
||||
'PhabricatorSpacesInterface' => 'applications/spaces/interface/PhabricatorSpacesInterface.php',
|
||||
'PhabricatorSpacesListController' => 'applications/spaces/controller/PhabricatorSpacesListController.php',
|
||||
'PhabricatorSpacesNamespace' => 'applications/spaces/storage/PhabricatorSpacesNamespace.php',
|
||||
|
@ -4189,9 +4197,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorStorageSchemaSpec' => 'infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php',
|
||||
'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php',
|
||||
'PhabricatorStringConfigType' => 'applications/config/type/PhabricatorStringConfigType.php',
|
||||
'PhabricatorStringExportField' => 'infrastructure/export/PhabricatorStringExportField.php',
|
||||
'PhabricatorStringExportField' => 'infrastructure/export/field/PhabricatorStringExportField.php',
|
||||
'PhabricatorStringListConfigType' => 'applications/config/type/PhabricatorStringListConfigType.php',
|
||||
'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php',
|
||||
'PhabricatorStringListExportField' => 'infrastructure/export/field/PhabricatorStringListExportField.php',
|
||||
'PhabricatorStringSetting' => 'applications/settings/setting/PhabricatorStringSetting.php',
|
||||
'PhabricatorSubmitEditField' => 'applications/transactions/editfield/PhabricatorSubmitEditField.php',
|
||||
'PhabricatorSubscribableInterface' => 'applications/subscriptions/interface/PhabricatorSubscribableInterface.php',
|
||||
|
@ -4206,6 +4215,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php',
|
||||
'PhabricatorSubscriptionsEditEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php',
|
||||
'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php',
|
||||
'PhabricatorSubscriptionsExportEngineExtension' => 'infrastructure/export/engine/PhabricatorSubscriptionsExportEngineExtension.php',
|
||||
'PhabricatorSubscriptionsFulltextEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php',
|
||||
'PhabricatorSubscriptionsHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php',
|
||||
'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php',
|
||||
|
@ -4253,7 +4263,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorTextAreaEditField' => 'applications/transactions/editfield/PhabricatorTextAreaEditField.php',
|
||||
'PhabricatorTextConfigType' => 'applications/config/type/PhabricatorTextConfigType.php',
|
||||
'PhabricatorTextEditField' => 'applications/transactions/editfield/PhabricatorTextEditField.php',
|
||||
'PhabricatorTextExportFormat' => 'infrastructure/export/PhabricatorTextExportFormat.php',
|
||||
'PhabricatorTextExportFormat' => 'infrastructure/export/format/PhabricatorTextExportFormat.php',
|
||||
'PhabricatorTextListConfigType' => 'applications/config/type/PhabricatorTextListConfigType.php',
|
||||
'PhabricatorTime' => 'infrastructure/time/PhabricatorTime.php',
|
||||
'PhabricatorTimeFormatSetting' => 'applications/settings/setting/PhabricatorTimeFormatSetting.php',
|
||||
|
@ -4318,6 +4328,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorUIExample' => 'applications/uiexample/examples/PhabricatorUIExample.php',
|
||||
'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php',
|
||||
'PhabricatorUIExamplesApplication' => 'applications/uiexample/application/PhabricatorUIExamplesApplication.php',
|
||||
'PhabricatorURIExportField' => 'infrastructure/export/field/PhabricatorURIExportField.php',
|
||||
'PhabricatorUSEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php',
|
||||
'PhabricatorUnifiedDiffsSetting' => 'applications/settings/setting/PhabricatorUnifiedDiffsSetting.php',
|
||||
'PhabricatorUnitTestContentSource' => 'infrastructure/contentsource/PhabricatorUnitTestContentSource.php',
|
||||
|
@ -4411,6 +4422,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorWorkerManagementWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php',
|
||||
'PhabricatorWorkerPermanentFailureException' => 'infrastructure/daemon/workers/exception/PhabricatorWorkerPermanentFailureException.php',
|
||||
'PhabricatorWorkerSchemaSpec' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerSchemaSpec.php',
|
||||
'PhabricatorWorkerSingleBulkJobType' => 'infrastructure/daemon/workers/bulk/PhabricatorWorkerSingleBulkJobType.php',
|
||||
'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php',
|
||||
'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTaskData.php',
|
||||
'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/PhabricatorWorkerTaskDetailController.php',
|
||||
|
@ -6768,10 +6780,6 @@ phutil_register_library_map(array(
|
|||
'ManiphestEditProjectsCapability' => 'PhabricatorPolicyCapability',
|
||||
'ManiphestEditStatusCapability' => 'PhabricatorPolicyCapability',
|
||||
'ManiphestEmailCommand' => 'MetaMTAEmailTransactionCommand',
|
||||
'ManiphestExcelDefaultFormat' => 'ManiphestExcelFormat',
|
||||
'ManiphestExcelFormat' => 'Phobject',
|
||||
'ManiphestExcelFormatTestCase' => 'PhabricatorTestCase',
|
||||
'ManiphestExportController' => 'ManiphestController',
|
||||
'ManiphestGetTaskTransactionsConduitAPIMethod' => 'ManiphestConduitAPIMethod',
|
||||
'ManiphestHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
||||
'ManiphestInfoConduitAPIMethod' => 'ManiphestConduitAPIMethod',
|
||||
|
@ -7572,6 +7580,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBulkContentSource' => 'PhabricatorContentSource',
|
||||
'PhabricatorBulkEditGroup' => 'Phobject',
|
||||
'PhabricatorBulkEngine' => 'Phobject',
|
||||
'PhabricatorBulkManagementExportWorkflow' => 'PhabricatorBulkManagementWorkflow',
|
||||
'PhabricatorBulkManagementMakeSilentWorkflow' => 'PhabricatorBulkManagementWorkflow',
|
||||
'PhabricatorBulkManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorCSVExportFormat' => 'PhabricatorExportFormat',
|
||||
|
@ -7991,6 +8000,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCustomFieldEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorCustomFieldEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorCustomFieldEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorCustomFieldExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorCustomFieldHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorCustomFieldHeraldActionGroup' => 'HeraldActionGroup',
|
||||
|
@ -8279,9 +8289,14 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEventListener' => 'PhutilEventListener',
|
||||
'PhabricatorEventType' => 'PhutilEventType',
|
||||
'PhabricatorExampleEventListener' => 'PhabricatorEventListener',
|
||||
'PhabricatorExcelExportFormat' => 'PhabricatorExportFormat',
|
||||
'PhabricatorExecFutureFileUploadSource' => 'PhabricatorFileUploadSource',
|
||||
'PhabricatorExportEngine' => 'Phobject',
|
||||
'PhabricatorExportEngineBulkJobType' => 'PhabricatorWorkerSingleBulkJobType',
|
||||
'PhabricatorExportEngineExtension' => 'Phobject',
|
||||
'PhabricatorExportField' => 'Phobject',
|
||||
'PhabricatorExportFormat' => 'Phobject',
|
||||
'PhabricatorExportFormatSetting' => 'PhabricatorInternalSetting',
|
||||
'PhabricatorExtendingPhabricatorConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorExtensionsSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorExternalAccount' => array(
|
||||
|
@ -8599,9 +8614,11 @@ phutil_register_library_map(array(
|
|||
'PhabricatorLipsumManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorLipsumMondrianArtist' => 'PhabricatorLipsumArtist',
|
||||
'PhabricatorLiskDAO' => 'LiskDAO',
|
||||
'PhabricatorLiskExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorLiskFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorLiskSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
||||
'PhabricatorLiskSerializer' => 'Phobject',
|
||||
'PhabricatorListExportField' => 'PhabricatorExportField',
|
||||
'PhabricatorLocalDiskFileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||
'PhabricatorLocalTimeTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorLocaleScopeGuard' => 'Phobject',
|
||||
|
@ -8939,6 +8956,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPHIDExportField' => 'PhabricatorExportField',
|
||||
'PhabricatorPHIDListEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorPHIDListEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorPHIDListExportField' => 'PhabricatorListExportField',
|
||||
'PhabricatorPHIDResolver' => 'Phobject',
|
||||
'PhabricatorPHIDType' => 'Phobject',
|
||||
'PhabricatorPHIDTypeTestCase' => 'PhutilTestCase',
|
||||
|
@ -9439,6 +9457,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension',
|
||||
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
|
||||
'PhabricatorProjectsExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
|
||||
|
@ -9806,6 +9825,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSpacesController' => 'PhabricatorController',
|
||||
'PhabricatorSpacesDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorSpacesEditController' => 'PhabricatorSpacesController',
|
||||
'PhabricatorSpacesExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorSpacesInterface' => 'PhabricatorPHIDInterface',
|
||||
'PhabricatorSpacesListController' => 'PhabricatorSpacesController',
|
||||
'PhabricatorSpacesNamespace' => array(
|
||||
|
@ -9879,6 +9899,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorStringExportField' => 'PhabricatorExportField',
|
||||
'PhabricatorStringListConfigType' => 'PhabricatorTextListConfigType',
|
||||
'PhabricatorStringListEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorStringListExportField' => 'PhabricatorListExportField',
|
||||
'PhabricatorStringSetting' => 'PhabricatorSetting',
|
||||
'PhabricatorSubmitEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorSubscribedToObjectEdgeType' => 'PhabricatorEdgeType',
|
||||
|
@ -9892,6 +9913,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSubscriptionsEditController' => 'PhabricatorController',
|
||||
'PhabricatorSubscriptionsEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorSubscriptionsEditor' => 'PhabricatorEditor',
|
||||
'PhabricatorSubscriptionsExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorSubscriptionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorSubscriptionsHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorSubscriptionsListController' => 'PhabricatorController',
|
||||
|
@ -10015,6 +10037,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorUIExample' => 'Phobject',
|
||||
'PhabricatorUIExampleRenderController' => 'PhabricatorController',
|
||||
'PhabricatorUIExamplesApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorURIExportField' => 'PhabricatorExportField',
|
||||
'PhabricatorUSEnglishTranslation' => 'PhutilTranslation',
|
||||
'PhabricatorUnifiedDiffsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorUnitTestContentSource' => 'PhabricatorContentSource',
|
||||
|
@ -10138,6 +10161,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorWorkerManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorWorkerPermanentFailureException' => 'Exception',
|
||||
'PhabricatorWorkerSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorWorkerSingleBulkJobType' => 'PhabricatorWorkerBulkJobType',
|
||||
'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
|
||||
'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
|
||||
'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController',
|
||||
|
|
|
@ -358,9 +358,17 @@ final class PhabricatorAuditEditor
|
|||
array $changes,
|
||||
PhutilMarkupEngine $engine) {
|
||||
|
||||
// we are only really trying to find unmentionable phids here...
|
||||
// don't bother with this outside initial commit (i.e. create)
|
||||
// transaction
|
||||
$actor = $this->getActor();
|
||||
$result = array();
|
||||
|
||||
// Some interactions (like "Fixes Txxx" interacting with Maniphest) have
|
||||
// already been processed, so we're only re-parsing them here to avoid
|
||||
// generating an extra redundant mention. Other interactions are being
|
||||
// processed for the first time.
|
||||
|
||||
// We're only recognizing magic in the commit message itself, not in
|
||||
// audit comments.
|
||||
|
||||
$is_commit = false;
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
|
@ -370,8 +378,6 @@ final class PhabricatorAuditEditor
|
|||
}
|
||||
}
|
||||
|
||||
// "result" is always an array....
|
||||
$result = array();
|
||||
if (!$is_commit) {
|
||||
return $result;
|
||||
}
|
||||
|
@ -403,6 +409,46 @@ final class PhabricatorAuditEditor
|
|||
->withNames($monograms)
|
||||
->execute();
|
||||
$phid_map[] = mpull($objects, 'getPHID', 'getPHID');
|
||||
|
||||
|
||||
$reverts_refs = id(new DifferentialCustomFieldRevertsParser())
|
||||
->parseCorpus($huge_block);
|
||||
$reverts = array_mergev(ipull($reverts_refs, 'monograms'));
|
||||
if ($reverts) {
|
||||
// Only allow commits to revert other commits in the same repository.
|
||||
$reverted_commits = id(new DiffusionCommitQuery())
|
||||
->setViewer($actor)
|
||||
->withRepository($object->getRepository())
|
||||
->withIdentifiers($reverts)
|
||||
->execute();
|
||||
|
||||
$reverted_revisions = id(new PhabricatorObjectQuery())
|
||||
->setViewer($actor)
|
||||
->withNames($reverts)
|
||||
->withTypes(
|
||||
array(
|
||||
DifferentialRevisionPHIDType::TYPECONST,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$reverted_phids =
|
||||
mpull($reverted_commits, 'getPHID', 'getPHID') +
|
||||
mpull($reverted_revisions, 'getPHID', 'getPHID');
|
||||
|
||||
// NOTE: Skip any write attempts if a user cleverly implies a commit
|
||||
// reverts itself, although this would be exceptionally clever in Git
|
||||
// or Mercurial.
|
||||
unset($reverted_phids[$object->getPHID()]);
|
||||
|
||||
$reverts_edge = DiffusionCommitRevertsCommitEdgeType::EDGECONST;
|
||||
$result[] = id(new PhabricatorAuditTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $reverts_edge)
|
||||
->setNewValue(array('+' => $reverted_phids));
|
||||
|
||||
$phid_map[] = $reverted_phids;
|
||||
}
|
||||
|
||||
$phid_map = array_mergev($phid_map);
|
||||
$this->setUnmentionablePHIDMap($phid_map);
|
||||
|
||||
|
|
|
@ -3,9 +3,26 @@
|
|||
final class ConduitPHIDParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
private $isNullable;
|
||||
|
||||
public function setIsNullable($is_nullable) {
|
||||
$this->isNullable = $is_nullable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsNullable() {
|
||||
return $this->isNullable;
|
||||
}
|
||||
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
if ($this->getIsNullable()) {
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
|
@ -17,8 +34,12 @@ final class ConduitPHIDParameterType
|
|||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
if ($this->getIsNullable()) {
|
||||
return 'phid|null';
|
||||
} else {
|
||||
return 'phid';
|
||||
}
|
||||
}
|
||||
|
||||
protected function getParameterFormatDescriptions() {
|
||||
return array(
|
||||
|
@ -27,9 +48,15 @@ final class ConduitPHIDParameterType
|
|||
}
|
||||
|
||||
protected function getParameterExamples() {
|
||||
return array(
|
||||
$examples = array(
|
||||
'"PHID-WXYZ-1111222233334444"',
|
||||
);
|
||||
|
||||
if ($this->getIsNullable()) {
|
||||
$examples[] = 'null';
|
||||
}
|
||||
|
||||
return $examples;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -71,18 +71,10 @@ final class PhabricatorDaemonBulkJobViewController
|
|||
$viewer = $this->getViewer();
|
||||
$curtain = $this->newCurtainView($job);
|
||||
|
||||
if ($job->isConfirming()) {
|
||||
$continue_uri = $job->getMonitorURI();
|
||||
} else {
|
||||
$continue_uri = $job->getDoneURI();
|
||||
foreach ($job->getCurtainActions($viewer) as $action) {
|
||||
$curtain->addAction($action);
|
||||
}
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($continue_uri)
|
||||
->setIcon('fa-arrow-circle-o-right')
|
||||
->setName(pht('Continue')));
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
|
|
|
@ -919,7 +919,44 @@ final class DifferentialTransactionEditor
|
|||
}
|
||||
}
|
||||
|
||||
$this->setUnmentionablePHIDMap(array_merge($task_phids, $rev_phids));
|
||||
$revert_refs = id(new DifferentialCustomFieldRevertsParser())
|
||||
->parseCorpus($content_block);
|
||||
|
||||
$revert_monograms = array();
|
||||
foreach ($revert_refs as $match) {
|
||||
foreach ($match['monograms'] as $monogram) {
|
||||
$revert_monograms[] = $monogram;
|
||||
}
|
||||
}
|
||||
|
||||
if ($revert_monograms) {
|
||||
$revert_objects = id(new PhabricatorObjectQuery())
|
||||
->setViewer($this->getActor())
|
||||
->withNames($revert_monograms)
|
||||
->withTypes(
|
||||
array(
|
||||
DifferentialRevisionPHIDType::TYPECONST,
|
||||
PhabricatorRepositoryCommitPHIDType::TYPECONST,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$revert_phids = mpull($revert_objects, 'getPHID', 'getPHID');
|
||||
|
||||
// Don't let an object revert itself, although other silly stuff like
|
||||
// cycles of objects reverting each other is not prevented.
|
||||
unset($revert_phids[$object->getPHID()]);
|
||||
|
||||
$revert_type = DiffusionCommitRevertsCommitEdgeType::EDGECONST;
|
||||
$edges[$revert_type] = $revert_phids;
|
||||
} else {
|
||||
$revert_phids = array();
|
||||
}
|
||||
|
||||
$this->setUnmentionablePHIDMap(
|
||||
array_merge(
|
||||
$task_phids,
|
||||
$rev_phids,
|
||||
$revert_phids));
|
||||
|
||||
$result = array();
|
||||
foreach ($edges as $type => $specs) {
|
||||
|
|
|
@ -121,7 +121,7 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
|
|||
$this->getEditRoutePattern('edit/') =>
|
||||
'DiffusionRepositoryEditController',
|
||||
'pushlog/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
|
||||
$this->getQueryRoutePattern() => 'DiffusionPushLogListController',
|
||||
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
|
||||
),
|
||||
'pulllog/' => array(
|
||||
|
|
|
@ -9,4 +9,9 @@ final class DiffusionPullLogListController
|
|||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
return parent::buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Pull Logs'), $this->getApplicationURI('pulllog/'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,4 +9,9 @@ final class DiffusionPushLogListController
|
|||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
return parent::buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Push Logs'), $this->getApplicationURI('pushlog/'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
|||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s reverting commit(s): %s.',
|
||||
'%s added %s reverting change(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
|
@ -31,7 +31,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s reverting commit(s): %s.',
|
||||
'%s removed %s reverting change(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
|
@ -46,7 +46,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited reverting commit(s), added %s: %s; removed %s: %s.',
|
||||
'%s edited reverting change(s), added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
|
@ -61,7 +61,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
|||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s reverting commit(s) for %s: %s.',
|
||||
'%s added %s reverting change(s) for %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
|
@ -75,7 +75,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s reverting commit(s) for %s: %s.',
|
||||
'%s removed %s reverting change(s) for %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
|
@ -92,7 +92,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited reverting commit(s) for %s, added %s: %s; removed %s: %s.',
|
||||
'%s edited reverting change(s) for %s, added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
|
|
|
@ -22,7 +22,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
|||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s reverted commit(s): %s.',
|
||||
'%s added %s reverted change(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
|
@ -34,7 +34,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s reverted commit(s): %s.',
|
||||
'%s removed %s reverted change(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
|
@ -49,7 +49,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited reverted commit(s), added %s: %s; removed %s: %s.',
|
||||
'%s edited reverted change(s), added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
|
@ -64,7 +64,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
|||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s reverted commit(s) for %s: %s.',
|
||||
'%s added %s reverted change(s) for %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
|
@ -78,7 +78,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s reverted commit(s) for %s: %s.',
|
||||
'%s removed %s reverted change(s) for %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
|
@ -95,7 +95,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited reverted commit(s) for %s, added %s: %s; removed %s: %s.',
|
||||
'%s edited reverted change(s) for %s, added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
|
|
|
@ -135,13 +135,16 @@ final class HeraldCommitAdapter
|
|||
}
|
||||
|
||||
public function loadAffectedPaths() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if ($this->affectedPaths === null) {
|
||||
$result = PhabricatorOwnerPathQuery::loadAffectedPaths(
|
||||
$this->getRepository(),
|
||||
$this->commit,
|
||||
PhabricatorUser::getOmnipotentUser());
|
||||
$viewer);
|
||||
$this->affectedPaths = $result;
|
||||
}
|
||||
|
||||
return $this->affectedPaths;
|
||||
}
|
||||
|
||||
|
@ -172,6 +175,8 @@ final class HeraldCommitAdapter
|
|||
}
|
||||
|
||||
public function loadDifferentialRevision() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if ($this->affectedRevision === null) {
|
||||
$this->affectedRevision = false;
|
||||
|
||||
|
@ -189,7 +194,7 @@ final class HeraldCommitAdapter
|
|||
|
||||
$revision = id(new DifferentialRevisionQuery())
|
||||
->withIDs(array($revision_id))
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->setViewer($viewer)
|
||||
->needReviewers(true)
|
||||
->executeOne();
|
||||
if ($revision) {
|
||||
|
@ -197,6 +202,7 @@ final class HeraldCommitAdapter
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->affectedRevision;
|
||||
}
|
||||
|
||||
|
@ -323,7 +329,7 @@ final class HeraldCommitAdapter
|
|||
}
|
||||
|
||||
private function callConduit($method, array $params) {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$drequest = DiffusionRequest::newFromDictionary(
|
||||
array(
|
||||
|
|
|
@ -26,6 +26,12 @@ final class DiffusionPullLogSearchEngine
|
|||
$query->withPullerPHIDs($map['pullerPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['createdStart'] || $map['createdEnd']) {
|
||||
$query->withEpochBetween(
|
||||
$map['createdStart'],
|
||||
$map['createdEnd']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
@ -44,17 +50,19 @@ final class DiffusionPullLogSearchEngine
|
|||
->setLabel(pht('Pullers'))
|
||||
->setDescription(
|
||||
pht('Search for pull logs by specific users.')),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Created After'))
|
||||
->setKey('createdStart'),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Created Before'))
|
||||
->setKey('createdEnd'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function newExportFields() {
|
||||
return array(
|
||||
id(new PhabricatorIDExportField())
|
||||
->setKey('id')
|
||||
->setLabel(pht('ID')),
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('phid')
|
||||
->setLabel(pht('PHID')),
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$fields = array(
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('repositoryPHID')
|
||||
->setLabel(pht('Repository PHID')),
|
||||
|
@ -80,9 +88,17 @@ final class DiffusionPullLogSearchEngine
|
|||
->setKey('date')
|
||||
->setLabel(pht('Date')),
|
||||
);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('remoteAddress')
|
||||
->setLabel(pht('Remote Address'));
|
||||
}
|
||||
|
||||
public function newExport(array $events) {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function newExportData(array $events) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$phids = array();
|
||||
|
@ -111,9 +127,7 @@ final class DiffusionPullLogSearchEngine
|
|||
$puller_name = null;
|
||||
}
|
||||
|
||||
$export[] = array(
|
||||
'id' => $event->getID(),
|
||||
'phid' => $event->getPHID(),
|
||||
$map = array(
|
||||
'repositoryPHID' => $repository_phid,
|
||||
'repository' => $repository_name,
|
||||
'pullerPHID' => $puller_phid,
|
||||
|
@ -123,6 +137,12 @@ final class DiffusionPullLogSearchEngine
|
|||
'code' => $event->getResultCode(),
|
||||
'date' => $event->getEpoch(),
|
||||
);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$map['remoteAddress'] = $event->getRemoteAddress();
|
||||
}
|
||||
|
||||
$export[] = $map;
|
||||
}
|
||||
|
||||
return $export;
|
||||
|
|
|
@ -22,24 +22,10 @@ final class DiffusionPullLogListView extends AphrontView {
|
|||
}
|
||||
$handles = $viewer->loadHandles($handle_phids);
|
||||
|
||||
// Figure out which repositories are editable. We only let you see remote
|
||||
// IPs if you have edit capability on a repository.
|
||||
$editable_repos = array();
|
||||
if ($events) {
|
||||
$editable_repos = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->withPHIDs(mpull($events, 'getRepositoryPHID'))
|
||||
->execute();
|
||||
$editable_repos = mpull($editable_repos, null, 'getPHID');
|
||||
}
|
||||
// Only administrators can view remote addresses.
|
||||
$remotes_visible = $viewer->getIsAdmin();
|
||||
|
||||
$rows = array();
|
||||
$any_host = false;
|
||||
foreach ($events as $event) {
|
||||
if ($event->getRepositoryPHID()) {
|
||||
$repository = $event->getRepository();
|
||||
|
@ -47,13 +33,10 @@ final class DiffusionPullLogListView extends AphrontView {
|
|||
$repository = null;
|
||||
}
|
||||
|
||||
// Reveal this if it's valid and the user can edit the repository. For
|
||||
// invalid requests you currently have to go fishing in the database.
|
||||
$remote_address = '-';
|
||||
if ($repository) {
|
||||
if (isset($editable_repos[$event->getRepositoryPHID()])) {
|
||||
if ($remotes_visible) {
|
||||
$remote_address = $event->getRemoteAddress();
|
||||
}
|
||||
} else {
|
||||
$remote_address = null;
|
||||
}
|
||||
|
||||
$event_id = $event->getID();
|
||||
|
@ -107,6 +90,13 @@ final class DiffusionPullLogListView extends AphrontView {
|
|||
'',
|
||||
'n',
|
||||
'right',
|
||||
))
|
||||
->setColumnVisibility(
|
||||
array(
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
$remotes_visible,
|
||||
));
|
||||
|
||||
return $table;
|
||||
|
|
|
@ -25,31 +25,21 @@ final class DiffusionPushLogListView extends AphrontView {
|
|||
|
||||
$handles = $viewer->loadHandles($handle_phids);
|
||||
|
||||
// Figure out which repositories are editable. We only let you see remote
|
||||
// IPs if you have edit capability on a repository.
|
||||
$editable_repos = array();
|
||||
if ($logs) {
|
||||
$editable_repos = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->withPHIDs(mpull($logs, 'getRepositoryPHID'))
|
||||
->execute();
|
||||
$editable_repos = mpull($editable_repos, null, 'getPHID');
|
||||
}
|
||||
// Only administrators can view remote addresses.
|
||||
$remotes_visible = $viewer->getIsAdmin();
|
||||
|
||||
$flag_map = PhabricatorRepositoryPushLog::getFlagDisplayNames();
|
||||
$reject_map = PhabricatorRepositoryPushLog::getRejectCodeDisplayNames();
|
||||
|
||||
$rows = array();
|
||||
$any_host = false;
|
||||
foreach ($logs as $log) {
|
||||
$repository = $log->getRepository();
|
||||
|
||||
// Reveal this if it's valid and the user can edit the repository.
|
||||
$remote_address = '-';
|
||||
if (isset($editable_repos[$log->getRepositoryPHID()])) {
|
||||
if ($remotes_visible) {
|
||||
$remote_address = $log->getPushEvent()->getRemoteAddress();
|
||||
} else {
|
||||
$remote_address = null;
|
||||
}
|
||||
|
||||
$event_id = $log->getPushEvent()->getID();
|
||||
|
@ -72,6 +62,23 @@ final class DiffusionPushLogListView extends AphrontView {
|
|||
$device = null;
|
||||
}
|
||||
|
||||
$flags = $log->getChangeFlags();
|
||||
$flag_names = array();
|
||||
foreach ($flag_map as $flag_key => $flag_name) {
|
||||
if (($flags & $flag_key) === $flag_key) {
|
||||
$flag_names[] = $flag_name;
|
||||
}
|
||||
}
|
||||
$flag_names = phutil_implode_html(
|
||||
phutil_tag('br'),
|
||||
$flag_names);
|
||||
|
||||
$reject_code = $log->getPushEvent()->getRejectCode();
|
||||
$reject_label = idx(
|
||||
$reject_map,
|
||||
$reject_code,
|
||||
pht('Unknown ("%s")', $reject_code));
|
||||
|
||||
$rows[] = array(
|
||||
phutil_tag(
|
||||
'a',
|
||||
|
@ -98,10 +105,8 @@ final class DiffusionPushLogListView extends AphrontView {
|
|||
'href' => $repository->getCommitURI($log->getRefNew()),
|
||||
),
|
||||
$log->getRefNewShort()),
|
||||
|
||||
// TODO: Make these human-readable.
|
||||
$log->getChangeFlags(),
|
||||
$log->getPushEvent()->getRejectCode(),
|
||||
$flag_names,
|
||||
$reject_label,
|
||||
$viewer->formatShortDateTime($log->getEpoch()),
|
||||
);
|
||||
}
|
||||
|
@ -120,7 +125,7 @@ final class DiffusionPushLogListView extends AphrontView {
|
|||
pht('Old'),
|
||||
pht('New'),
|
||||
pht('Flags'),
|
||||
pht('Code'),
|
||||
pht('Result'),
|
||||
pht('Date'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
|
@ -135,6 +140,8 @@ final class DiffusionPushLogListView extends AphrontView {
|
|||
'wide',
|
||||
'n',
|
||||
'n',
|
||||
'',
|
||||
'',
|
||||
'right',
|
||||
))
|
||||
->setColumnVisibility(
|
||||
|
@ -142,7 +149,7 @@ final class DiffusionPushLogListView extends AphrontView {
|
|||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
$remotes_visible,
|
||||
true,
|
||||
$any_host,
|
||||
));
|
||||
|
|
|
@ -272,8 +272,12 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
$file->setByteSize($length);
|
||||
|
||||
// NOTE: Once we receive the first chunk, we'll detect its MIME type and
|
||||
// update the parent file. This matters for large media files like video.
|
||||
// update the parent file if a MIME type hasn't been provided. This matters
|
||||
// for large media files like video.
|
||||
$mime_type = idx($params, 'mime-type');
|
||||
if (!strlen($mime_type)) {
|
||||
$file->setMimeType('application/octet-stream');
|
||||
}
|
||||
|
||||
$chunked_hash = idx($params, 'chunkedHash');
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ abstract class PhabricatorFileUploadSource
|
|||
private $name;
|
||||
private $relativeTTL;
|
||||
private $viewPolicy;
|
||||
private $mimeType;
|
||||
private $authorPHID;
|
||||
|
||||
private $rope;
|
||||
private $data;
|
||||
|
@ -51,6 +53,24 @@ abstract class PhabricatorFileUploadSource
|
|||
return $this->byteLimit;
|
||||
}
|
||||
|
||||
public function setMIMEType($mime_type) {
|
||||
$this->mimeType = $mime_type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMIMEType() {
|
||||
return $this->mimeType;
|
||||
}
|
||||
|
||||
public function setAuthorPHID($author_phid) {
|
||||
$this->authorPHID = $author_phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuthorPHID() {
|
||||
return $this->authorPHID;
|
||||
}
|
||||
|
||||
public function uploadFile() {
|
||||
if (!$this->shouldChunkFile()) {
|
||||
return $this->writeSingleFile();
|
||||
|
@ -245,6 +265,16 @@ abstract class PhabricatorFileUploadSource
|
|||
$parameters['ttl.relative'] = $ttl;
|
||||
}
|
||||
|
||||
$mime_type = $this->getMimeType();
|
||||
if ($mime_type !== null) {
|
||||
$parameters['mime-type'] = $mime_type;
|
||||
}
|
||||
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
if ($author_phid !== null) {
|
||||
$parameters['authorPHID'] = $author_phid;
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,14 +50,13 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication {
|
|||
return array(
|
||||
'/T(?P<id>[1-9]\d*)' => 'ManiphestTaskDetailController',
|
||||
'/maniphest/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'ManiphestTaskListController',
|
||||
$this->getQueryRoutePattern() => 'ManiphestTaskListController',
|
||||
'report/(?:(?P<view>\w+)/)?' => 'ManiphestReportController',
|
||||
$this->getBulkRoutePattern('bulk/') => 'ManiphestBulkEditController',
|
||||
'task/' => array(
|
||||
$this->getEditRoutePattern('edit/')
|
||||
=> 'ManiphestTaskEditController',
|
||||
),
|
||||
'export/(?P<key>[^/]+)/' => 'ManiphestExportController',
|
||||
'subpriority/' => 'ManiphestSubpriorityController',
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestExportController extends ManiphestController {
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel
|
||||
* @phutil-external-symbol class PHPExcel_IOFactory
|
||||
* @phutil-external-symbol class PHPExcel_Style_NumberFormat
|
||||
* @phutil-external-symbol class PHPExcel_Cell_DataType
|
||||
*/
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$key = $request->getURIData('key');
|
||||
|
||||
$ok = @include_once 'PHPExcel.php';
|
||||
if (!$ok) {
|
||||
$dialog = $this->newDialog();
|
||||
|
||||
$inst1 = pht(
|
||||
'This system does not have PHPExcel installed. This software '.
|
||||
'component is required to export tasks to Excel. Have your system '.
|
||||
'administrator install it from:');
|
||||
|
||||
$inst2 = pht(
|
||||
'Your PHP "%s" needs to be updated to include the '.
|
||||
'PHPExcel Classes directory.',
|
||||
'include_path');
|
||||
|
||||
$dialog->setTitle(pht('Excel Export Not Configured'));
|
||||
$dialog->appendChild(hsprintf(
|
||||
'<p>%s</p>'.
|
||||
'<br />'.
|
||||
'<p>'.
|
||||
'<a href="https://github.com/PHPOffice/PHPExcel">'.
|
||||
'https://github.com/PHPOffice/PHPExcel'.
|
||||
'</a>'.
|
||||
'</p>'.
|
||||
'<br />'.
|
||||
'<p>%s</p>',
|
||||
$inst1,
|
||||
$inst2));
|
||||
|
||||
$dialog->addCancelButton('/maniphest/');
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
}
|
||||
|
||||
// TODO: PHPExcel has a dependency on the PHP zip extension. We should test
|
||||
// for that here, since it fatals if we don't have the ZipArchive class.
|
||||
|
||||
$saved = id(new PhabricatorSavedQueryQuery())
|
||||
->setViewer($viewer)
|
||||
->withQueryKeys(array($key))
|
||||
->executeOne();
|
||||
if (!$saved) {
|
||||
$engine = id(new ManiphestTaskSearchEngine())
|
||||
->setViewer($viewer);
|
||||
if ($engine->isBuiltinQuery($key)) {
|
||||
$saved = $engine->buildSavedQueryFromBuiltin($key);
|
||||
}
|
||||
if (!$saved) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
}
|
||||
|
||||
$formats = ManiphestExcelFormat::loadAllFormats();
|
||||
$export_formats = array();
|
||||
foreach ($formats as $format_class => $format_object) {
|
||||
$export_formats[$format_class] = $format_object->getName();
|
||||
}
|
||||
|
||||
if (!$request->isDialogFormPost()) {
|
||||
$dialog = new AphrontDialogView();
|
||||
$dialog->setUser($viewer);
|
||||
|
||||
$dialog->setTitle(pht('Export Tasks to Excel'));
|
||||
$dialog->appendChild(
|
||||
phutil_tag(
|
||||
'p',
|
||||
array(),
|
||||
pht('Do you want to export the query results to Excel?')));
|
||||
|
||||
$form = id(new PHUIFormLayoutView())
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Format:'))
|
||||
->setName('excel-format')
|
||||
->setOptions($export_formats));
|
||||
|
||||
$dialog->appendChild($form);
|
||||
|
||||
$dialog->addCancelButton('/maniphest/');
|
||||
$dialog->addSubmitButton(pht('Export to Excel'));
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
}
|
||||
|
||||
$format = idx($formats, $request->getStr('excel-format'));
|
||||
if ($format === null) {
|
||||
throw new Exception(pht('Excel format object not found.'));
|
||||
}
|
||||
|
||||
$saved->makeEphemeral();
|
||||
$saved->setParameter('limit', PHP_INT_MAX);
|
||||
|
||||
$engine = id(new ManiphestTaskSearchEngine())
|
||||
->setViewer($viewer);
|
||||
|
||||
$query = $engine->buildQueryFromSavedQuery($saved);
|
||||
$query->setViewer($viewer);
|
||||
$tasks = $query->execute();
|
||||
|
||||
$all_projects = array_mergev(mpull($tasks, 'getProjectPHIDs'));
|
||||
$all_assigned = mpull($tasks, 'getOwnerPHID');
|
||||
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array_merge($all_projects, $all_assigned))
|
||||
->execute();
|
||||
|
||||
$workbook = new PHPExcel();
|
||||
$format->buildWorkbook($workbook, $tasks, $handles, $viewer);
|
||||
$writer = PHPExcel_IOFactory::createWriter($workbook, 'Excel2007');
|
||||
|
||||
ob_start();
|
||||
$writer->save('php://output');
|
||||
$data = ob_get_clean();
|
||||
|
||||
$mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
|
||||
return id(new AphrontFileResponse())
|
||||
->setMimeType($mime)
|
||||
->setDownload($format->getFileName().'.xlsx')
|
||||
->setContent($data);
|
||||
}
|
||||
|
||||
}
|
|
@ -196,6 +196,7 @@ EODOCS
|
|||
pht('New task owner, or `null` to unassign.'))
|
||||
->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE)
|
||||
->setIsCopyable(true)
|
||||
->setIsNullable(true)
|
||||
->setSingleValue($object->getOwnerPHID())
|
||||
->setCommentActionLabel(pht('Assign / Claim'))
|
||||
->setCommentActionValue($owner_value),
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestExcelDefaultFormat extends ManiphestExcelFormat {
|
||||
|
||||
public function getName() {
|
||||
return pht('Default');
|
||||
}
|
||||
|
||||
public function getFileName() {
|
||||
return 'maniphest_tasks_'.date('Ymd');
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel
|
||||
* @phutil-external-symbol class PHPExcel_IOFactory
|
||||
* @phutil-external-symbol class PHPExcel_Style_NumberFormat
|
||||
* @phutil-external-symbol class PHPExcel_Cell_DataType
|
||||
*/
|
||||
public function buildWorkbook(
|
||||
PHPExcel $workbook,
|
||||
array $tasks,
|
||||
array $handles,
|
||||
PhabricatorUser $user) {
|
||||
|
||||
$sheet = $workbook->setActiveSheetIndex(0);
|
||||
$sheet->setTitle(pht('Tasks'));
|
||||
|
||||
$widths = array(
|
||||
null,
|
||||
15,
|
||||
null,
|
||||
10,
|
||||
15,
|
||||
15,
|
||||
60,
|
||||
30,
|
||||
20,
|
||||
100,
|
||||
);
|
||||
|
||||
foreach ($widths as $col => $width) {
|
||||
if ($width !== null) {
|
||||
$sheet->getColumnDimension($this->col($col))->setWidth($width);
|
||||
}
|
||||
}
|
||||
|
||||
$status_map = ManiphestTaskStatus::getTaskStatusMap();
|
||||
$pri_map = ManiphestTaskPriority::getTaskPriorityMap();
|
||||
|
||||
$date_format = null;
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array(
|
||||
pht('ID'),
|
||||
pht('Owner'),
|
||||
pht('Status'),
|
||||
pht('Priority'),
|
||||
pht('Date Created'),
|
||||
pht('Date Updated'),
|
||||
pht('Title'),
|
||||
pht('Tags'),
|
||||
pht('URI'),
|
||||
pht('Description'),
|
||||
);
|
||||
|
||||
$is_date = array(
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
$header_format = array(
|
||||
'font' => array(
|
||||
'bold' => true,
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$task_owner = null;
|
||||
if ($task->getOwnerPHID()) {
|
||||
$task_owner = $handles[$task->getOwnerPHID()]->getName();
|
||||
}
|
||||
|
||||
$projects = array();
|
||||
foreach ($task->getProjectPHIDs() as $phid) {
|
||||
$projects[] = $handles[$phid]->getName();
|
||||
}
|
||||
$projects = implode(', ', $projects);
|
||||
|
||||
$rows[] = array(
|
||||
'T'.$task->getID(),
|
||||
$task_owner,
|
||||
idx($status_map, $task->getStatus(), '?'),
|
||||
idx($pri_map, $task->getPriority(), '?'),
|
||||
$this->computeExcelDate($task->getDateCreated()),
|
||||
$this->computeExcelDate($task->getDateModified()),
|
||||
$task->getTitle(),
|
||||
$projects,
|
||||
PhabricatorEnv::getProductionURI('/T'.$task->getID()),
|
||||
id(new PhutilUTF8StringTruncator())
|
||||
->setMaximumBytes(512)
|
||||
->truncateString($task->getDescription()),
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($rows as $row => $cols) {
|
||||
foreach ($cols as $col => $spec) {
|
||||
$cell_name = $this->col($col).($row + 1);
|
||||
$cell = $sheet
|
||||
->setCellValue($cell_name, $spec, $return_cell = true);
|
||||
|
||||
if ($row == 0) {
|
||||
$sheet->getStyle($cell_name)->applyFromArray($header_format);
|
||||
}
|
||||
|
||||
if ($is_date[$col]) {
|
||||
$code = PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2;
|
||||
$sheet
|
||||
->getStyle($cell_name)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($code);
|
||||
} else {
|
||||
$cell->setDataType(PHPExcel_Cell_DataType::TYPE_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function col($n) {
|
||||
return chr(ord('A') + $n);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
abstract class ManiphestExcelFormat extends Phobject {
|
||||
|
||||
final public static function loadAllFormats() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setSortMethod('getOrder')
|
||||
->execute();
|
||||
}
|
||||
|
||||
abstract public function getName();
|
||||
abstract public function getFileName();
|
||||
|
||||
public function getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function computeExcelDate($epoch) {
|
||||
$seconds_per_day = (60 * 60 * 24);
|
||||
$offset = ($seconds_per_day * 25569);
|
||||
|
||||
return ($epoch + $offset) / $seconds_per_day;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel
|
||||
*/
|
||||
abstract public function buildWorkbook(
|
||||
PHPExcel $workbook,
|
||||
array $tasks,
|
||||
array $handles,
|
||||
PhabricatorUser $user);
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestExcelFormatTestCase extends PhabricatorTestCase {
|
||||
|
||||
public function testLoadAllFormats() {
|
||||
ManiphestExcelFormat::loadAllFormats();
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -432,4 +432,111 @@ final class ManiphestTaskSearchEngine
|
|||
return $view;
|
||||
}
|
||||
|
||||
|
||||
protected function newExportFields() {
|
||||
$fields = array(
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('monogram')
|
||||
->setLabel(pht('Monogram')),
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('authorPHID')
|
||||
->setLabel(pht('Author PHID')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('author')
|
||||
->setLabel(pht('Author')),
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('ownerPHID')
|
||||
->setLabel(pht('Owner PHID')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('owner')
|
||||
->setLabel(pht('Owner')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('status')
|
||||
->setLabel(pht('Status')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('statusName')
|
||||
->setLabel(pht('Status Name')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('priority')
|
||||
->setLabel(pht('Priority')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('priorityName')
|
||||
->setLabel(pht('Priority Name')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('subtype')
|
||||
->setLabel('Subtype'),
|
||||
id(new PhabricatorURIExportField())
|
||||
->setKey('uri')
|
||||
->setLabel(pht('URI')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('title')
|
||||
->setLabel(pht('Title')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('description')
|
||||
->setLabel(pht('Description')),
|
||||
);
|
||||
|
||||
if (ManiphestTaskPoints::getIsEnabled()) {
|
||||
$fields[] = id(new PhabricatorIntExportField())
|
||||
->setKey('points')
|
||||
->setLabel('Points');
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function newExportData(array $tasks) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$phids = array();
|
||||
foreach ($tasks as $task) {
|
||||
$phids[] = $task->getAuthorPHID();
|
||||
$phids[] = $task->getOwnerPHID();
|
||||
}
|
||||
$handles = $viewer->loadHandles($phids);
|
||||
|
||||
$export = array();
|
||||
foreach ($tasks as $task) {
|
||||
|
||||
$author_phid = $task->getAuthorPHID();
|
||||
if ($author_phid) {
|
||||
$author_name = $handles[$author_phid]->getName();
|
||||
} else {
|
||||
$author_name = null;
|
||||
}
|
||||
|
||||
$owner_phid = $task->getOwnerPHID();
|
||||
if ($owner_phid) {
|
||||
$owner_name = $handles[$owner_phid]->getName();
|
||||
} else {
|
||||
$owner_name = null;
|
||||
}
|
||||
|
||||
$status_value = $task->getStatus();
|
||||
$status_name = ManiphestTaskStatus::getTaskStatusName($status_value);
|
||||
|
||||
$priority_value = $task->getPriority();
|
||||
$priority_name = ManiphestTaskPriority::getTaskPriorityName(
|
||||
$priority_value);
|
||||
|
||||
$export[] = array(
|
||||
'monogram' => $task->getMonogram(),
|
||||
'authorPHID' => $author_phid,
|
||||
'author' => $author_name,
|
||||
'ownerPHID' => $owner_phid,
|
||||
'owner' => $owner_name,
|
||||
'status' => $status_value,
|
||||
'statusName' => $status_name,
|
||||
'priority' => $priority_value,
|
||||
'priorityName' => $priority_name,
|
||||
'points' => $task->getPoints(),
|
||||
'subtype' => $task->getSubtype(),
|
||||
'title' => $task->getTitle(),
|
||||
'uri' => PhabricatorEnv::getProductionURI($task->getURI()),
|
||||
'description' => $task->getDescription(),
|
||||
);
|
||||
}
|
||||
|
||||
return $export;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -175,8 +175,7 @@ final class ManiphestTaskResultListView extends ManiphestView {
|
|||
}
|
||||
|
||||
if (!$user->isLoggedIn()) {
|
||||
// Don't show the batch editor or excel export for logged-out users.
|
||||
// Technically we //could// let them export, but ehh.
|
||||
// Don't show the batch editor for logged-out users.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -220,14 +219,6 @@ final class ManiphestTaskResultListView extends ManiphestView {
|
|||
),
|
||||
pht("Bulk Edit Selected \xC2\xBB"));
|
||||
|
||||
$export = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/maniphest/export/'.$saved_query->getQueryKey().'/',
|
||||
'class' => 'button button-grey',
|
||||
),
|
||||
pht('Export to Excel'));
|
||||
|
||||
$hidden = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
|
@ -239,14 +230,12 @@ final class ManiphestTaskResultListView extends ManiphestView {
|
|||
'<table class="maniphest-batch-editor-layout">'.
|
||||
'<tr>'.
|
||||
'<td>%s%s</td>'.
|
||||
'<td>%s</td>'.
|
||||
'<td id="batch-select-status-cell">%s</td>'.
|
||||
'<td class="batch-select-submit-cell">%s%s</td>'.
|
||||
'</tr>'.
|
||||
'</table>',
|
||||
$select_all,
|
||||
$select_none,
|
||||
$export,
|
||||
'',
|
||||
$submit,
|
||||
$hidden);
|
||||
|
|
|
@ -42,8 +42,9 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication {
|
|||
return array(
|
||||
'/people/' => array(
|
||||
$this->getQueryRoutePattern() => 'PhabricatorPeopleListController',
|
||||
'logs/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'PhabricatorPeopleLogsController',
|
||||
'logs/' => array(
|
||||
$this->getQueryRoutePattern() => 'PhabricatorPeopleLogsController',
|
||||
),
|
||||
'invite/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'PhabricatorPeopleInviteListController',
|
||||
|
|
|
@ -9,6 +9,8 @@ final class PhabricatorPeopleLogQuery
|
|||
private $sessionKeys;
|
||||
private $actions;
|
||||
private $remoteAddressPrefix;
|
||||
private $dateCreatedMin;
|
||||
private $dateCreatedMax;
|
||||
|
||||
public function withActorPHIDs(array $actor_phids) {
|
||||
$this->actorPHIDs = $actor_phids;
|
||||
|
@ -40,70 +42,81 @@ final class PhabricatorPeopleLogQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new PhabricatorUserLog();
|
||||
$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 withDateCreatedBetween($min, $max) {
|
||||
$this->dateCreatedMin = $min;
|
||||
$this->dateCreatedMax = $max;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
public function newResultObject() {
|
||||
return new PhabricatorUserLog();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->actorPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'actorPHID IN (%Ls)',
|
||||
$this->actorPHIDs);
|
||||
}
|
||||
|
||||
if ($this->userPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'userPHID IN (%Ls)',
|
||||
$this->userPHIDs);
|
||||
}
|
||||
|
||||
if ($this->relatedPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'actorPHID IN (%Ls) OR userPHID IN (%Ls)',
|
||||
$conn,
|
||||
'(actorPHID IN (%Ls) OR userPHID IN (%Ls))',
|
||||
$this->relatedPHIDs,
|
||||
$this->relatedPHIDs);
|
||||
}
|
||||
|
||||
if ($this->sessionKeys !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'session IN (%Ls)',
|
||||
$this->sessionKeys);
|
||||
}
|
||||
|
||||
if ($this->actions !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'action IN (%Ls)',
|
||||
$this->actions);
|
||||
}
|
||||
|
||||
if ($this->remoteAddressPrefix !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'remoteAddr LIKE %>',
|
||||
$this->remoteAddressPrefix);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
if ($this->dateCreatedMin !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'dateCreated >= %d',
|
||||
$this->dateCreatedMin);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
if ($this->dateCreatedMax !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'dateCreated <= %d',
|
||||
$this->dateCreatedMax);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
|
|
|
@ -15,34 +15,8 @@ final class PhabricatorPeopleLogSearchEngine
|
|||
return 500;
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
|
||||
$saved->setParameter(
|
||||
'userPHIDs',
|
||||
$this->readUsersFromRequest($request, 'users'));
|
||||
|
||||
$saved->setParameter(
|
||||
'actorPHIDs',
|
||||
$this->readUsersFromRequest($request, 'actors'));
|
||||
|
||||
$saved->setParameter(
|
||||
'actions',
|
||||
$this->readListFromRequest($request, 'actions'));
|
||||
|
||||
$saved->setParameter(
|
||||
'ip',
|
||||
$request->getStr('ip'));
|
||||
|
||||
$saved->setParameter(
|
||||
'sessions',
|
||||
$this->readListFromRequest($request, 'sessions'));
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$query = id(new PhabricatorPeopleLogQuery());
|
||||
public function newQuery() {
|
||||
$query = new PhabricatorPeopleLogQuery();
|
||||
|
||||
// NOTE: If the viewer isn't an administrator, always restrict the query to
|
||||
// related records. This echoes the policy logic of these logs. This is
|
||||
|
@ -54,82 +28,73 @@ final class PhabricatorPeopleLogSearchEngine
|
|||
$query->withRelatedPHIDs(array($viewer->getPHID()));
|
||||
}
|
||||
|
||||
$actor_phids = $saved->getParameter('actorPHIDs', array());
|
||||
if ($actor_phids) {
|
||||
$query->withActorPHIDs($actor_phids);
|
||||
return $query;
|
||||
}
|
||||
|
||||
$user_phids = $saved->getParameter('userPHIDs', array());
|
||||
if ($user_phids) {
|
||||
$query->withUserPHIDs($user_phids);
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['userPHIDs']) {
|
||||
$query->withUserPHIDs($map['userPHIDs']);
|
||||
}
|
||||
|
||||
$actions = $saved->getParameter('actions', array());
|
||||
if ($actions) {
|
||||
$query->withActions($actions);
|
||||
if ($map['actorPHIDs']) {
|
||||
$query->withActorPHIDs($map['actorPHIDs']);
|
||||
}
|
||||
|
||||
$remote_prefix = $saved->getParameter('ip');
|
||||
if (strlen($remote_prefix)) {
|
||||
$query->withRemoteAddressprefix($remote_prefix);
|
||||
if ($map['actions']) {
|
||||
$query->withActions($map['actions']);
|
||||
}
|
||||
|
||||
$sessions = $saved->getParameter('sessions', array());
|
||||
if ($sessions) {
|
||||
$query->withSessionKeys($sessions);
|
||||
if (strlen($map['ip'])) {
|
||||
$query->withRemoteAddressPrefix($map['ip']);
|
||||
}
|
||||
|
||||
if ($map['sessions']) {
|
||||
$query->withSessionKeys($map['sessions']);
|
||||
}
|
||||
|
||||
if ($map['createdStart'] || $map['createdEnd']) {
|
||||
$query->withDateCreatedBetween(
|
||||
$map['createdStart'],
|
||||
$map['createdEnd']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved) {
|
||||
|
||||
$actor_phids = $saved->getParameter('actorPHIDs', array());
|
||||
$user_phids = $saved->getParameter('userPHIDs', array());
|
||||
|
||||
$actions = $saved->getParameter('actions', array());
|
||||
$remote_prefix = $saved->getParameter('ip');
|
||||
$sessions = $saved->getParameter('sessions', array());
|
||||
|
||||
$actions = array_fuse($actions);
|
||||
$action_control = id(new AphrontFormCheckboxControl())
|
||||
->setLabel(pht('Actions'));
|
||||
$action_types = PhabricatorUserLog::getActionTypeMap();
|
||||
foreach ($action_types as $type => $label) {
|
||||
$action_control->addCheckbox(
|
||||
'actions[]',
|
||||
$type,
|
||||
$label,
|
||||
isset($actions[$label]));
|
||||
}
|
||||
|
||||
$form
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new PhabricatorPeopleDatasource())
|
||||
->setName('actors')
|
||||
->setLabel(pht('Actors'))
|
||||
->setValue($actor_phids))
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new PhabricatorPeopleDatasource())
|
||||
->setName('users')
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorUsersSearchField())
|
||||
->setKey('userPHIDs')
|
||||
->setAliases(array('users', 'user', 'userPHID'))
|
||||
->setLabel(pht('Users'))
|
||||
->setValue($user_phids))
|
||||
->appendChild($action_control)
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setDescription(pht('Search for activity affecting specific users.')),
|
||||
id(new PhabricatorUsersSearchField())
|
||||
->setKey('actorPHIDs')
|
||||
->setAliases(array('actors', 'actor', 'actorPHID'))
|
||||
->setLabel(pht('Actors'))
|
||||
->setDescription(pht('Search for activity by specific users.')),
|
||||
id(new PhabricatorSearchCheckboxesField())
|
||||
->setKey('actions')
|
||||
->setLabel(pht('Actions'))
|
||||
->setDescription(pht('Search for particular types of activity.'))
|
||||
->setOptions(PhabricatorUserLog::getActionTypeMap()),
|
||||
id(new PhabricatorSearchTextField())
|
||||
->setKey('ip')
|
||||
->setLabel(pht('Filter IP'))
|
||||
->setName('ip')
|
||||
->setValue($remote_prefix))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setDescription(pht('Search for actions by remote address.')),
|
||||
id(new PhabricatorSearchStringListField())
|
||||
->setKey('sessions')
|
||||
->setLabel(pht('Sessions'))
|
||||
->setName('sessions')
|
||||
->setValue(implode(', ', $sessions)));
|
||||
|
||||
->setDescription(pht('Search for activity in particular sessions.')),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Created After'))
|
||||
->setKey('createdStart'),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Created Before'))
|
||||
->setKey('createdEnd'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
|
@ -156,19 +121,6 @@ final class PhabricatorPeopleLogSearchEngine
|
|||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
protected function getRequiredHandlePHIDsForResultList(
|
||||
array $logs,
|
||||
PhabricatorSavedQuery $query) {
|
||||
|
||||
$phids = array();
|
||||
foreach ($logs as $log) {
|
||||
$phids[$log->getActorPHID()] = true;
|
||||
$phids[$log->getUserPHID()] = true;
|
||||
}
|
||||
|
||||
return array_keys($phids);
|
||||
}
|
||||
|
||||
protected function renderResultList(
|
||||
array $logs,
|
||||
PhabricatorSavedQuery $query,
|
||||
|
@ -179,16 +131,111 @@ final class PhabricatorPeopleLogSearchEngine
|
|||
|
||||
$table = id(new PhabricatorUserLogView())
|
||||
->setUser($viewer)
|
||||
->setLogs($logs)
|
||||
->setHandles($handles);
|
||||
->setLogs($logs);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$table->setSearchBaseURI($this->getApplicationURI('logs/'));
|
||||
}
|
||||
|
||||
$result = new PhabricatorApplicationSearchResultView();
|
||||
$result->setTable($table);
|
||||
|
||||
return $result;
|
||||
return id(new PhabricatorApplicationSearchResultView())
|
||||
->setTable($table);
|
||||
}
|
||||
|
||||
protected function newExportFields() {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$fields = array(
|
||||
$fields[] = id(new PhabricatorPHIDExportField())
|
||||
->setKey('actorPHID')
|
||||
->setLabel(pht('Actor PHID')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('actor')
|
||||
->setLabel(pht('Actor')),
|
||||
$fields[] = id(new PhabricatorPHIDExportField())
|
||||
->setKey('userPHID')
|
||||
->setLabel(pht('User PHID')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('user')
|
||||
->setLabel(pht('User')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('action')
|
||||
->setLabel(pht('Action')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('actionName')
|
||||
->setLabel(pht('Action Name')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('session')
|
||||
->setLabel(pht('Session')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('old')
|
||||
->setLabel(pht('Old Value')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('new')
|
||||
->setLabel(pht('New Value')),
|
||||
);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('remoteAddress')
|
||||
->setLabel(pht('Remote Address'));
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function newExportData(array $logs) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
|
||||
$phids = array();
|
||||
foreach ($logs as $log) {
|
||||
$phids[] = $log->getUserPHID();
|
||||
$phids[] = $log->getActorPHID();
|
||||
}
|
||||
$handles = $viewer->loadHandles($phids);
|
||||
|
||||
$action_map = PhabricatorUserLog::getActionTypeMap();
|
||||
|
||||
$export = array();
|
||||
foreach ($logs as $log) {
|
||||
|
||||
$user_phid = $log->getUserPHID();
|
||||
if ($user_phid) {
|
||||
$user_name = $handles[$user_phid]->getName();
|
||||
} else {
|
||||
$user_name = null;
|
||||
}
|
||||
|
||||
$actor_phid = $log->getActorPHID();
|
||||
if ($actor_phid) {
|
||||
$actor_name = $handles[$actor_phid]->getName();
|
||||
} else {
|
||||
$actor_name = null;
|
||||
}
|
||||
|
||||
$action = $log->getAction();
|
||||
$action_name = idx($action_map, $action, pht('Unknown ("%s")', $action));
|
||||
|
||||
$map = array(
|
||||
'actorPHID' => $actor_phid,
|
||||
'actor' => $actor_name,
|
||||
'userPHID' => $user_phid,
|
||||
'user' => $user_name,
|
||||
'action' => $action,
|
||||
'actionName' => $action_name,
|
||||
'session' => substr($log->getSession(), 0, 6),
|
||||
'old' => $log->getOldValue(),
|
||||
'new' => $log->getNewValue(),
|
||||
);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$map['remoteAddress'] = $log->getRemoteAddr();
|
||||
}
|
||||
|
||||
$export[] = $map;
|
||||
}
|
||||
|
||||
return $export;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -322,35 +322,23 @@ final class PhabricatorPeopleSearchEngine
|
|||
|
||||
protected function newExportFields() {
|
||||
return array(
|
||||
id(new PhabricatorIDExportField())
|
||||
->setKey('id')
|
||||
->setLabel(pht('ID')),
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('phid')
|
||||
->setLabel(pht('PHID')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('username')
|
||||
->setLabel(pht('Username')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('realName')
|
||||
->setLabel(pht('Real Name')),
|
||||
id(new PhabricatorEpochExportField())
|
||||
->setKey('created')
|
||||
->setLabel(pht('Date Created')),
|
||||
);
|
||||
}
|
||||
|
||||
public function newExport(array $users) {
|
||||
protected function newExportData(array $users) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$export = array();
|
||||
foreach ($users as $user) {
|
||||
$export[] = array(
|
||||
'id' => $user->getID(),
|
||||
'phid' => $user->getPHID(),
|
||||
'username' => $user->getUsername(),
|
||||
'realName' => $user->getRealName(),
|
||||
'created' => $user->getDateCreated(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
final class PhabricatorUserLogView extends AphrontView {
|
||||
|
||||
private $logs;
|
||||
private $handles;
|
||||
private $searchBaseURI;
|
||||
|
||||
public function setSearchBaseURI($search_base_uri) {
|
||||
|
@ -17,25 +16,46 @@ final class PhabricatorUserLogView extends AphrontView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setHandles(array $handles) {
|
||||
assert_instances_of($handles, 'PhabricatorObjectHandle');
|
||||
$this->handles = $handles;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$logs = $this->logs;
|
||||
$handles = $this->handles;
|
||||
$viewer = $this->getUser();
|
||||
|
||||
$phids = array();
|
||||
foreach ($logs as $log) {
|
||||
$phids[] = $log->getActorPHID();
|
||||
$phids[] = $log->getUserPHID();
|
||||
}
|
||||
$handles = $viewer->loadHandles($phids);
|
||||
|
||||
$action_map = PhabricatorUserLog::getActionTypeMap();
|
||||
$base_uri = $this->searchBaseURI;
|
||||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
|
||||
$rows = array();
|
||||
foreach ($logs as $log) {
|
||||
$ip = $log->getRemoteAddr();
|
||||
$session = substr($log->getSession(), 0, 6);
|
||||
|
||||
$actor_phid = $log->getActorPHID();
|
||||
$user_phid = $log->getUserPHID();
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$can_see_ip = true;
|
||||
} else if ($viewer_phid == $actor_phid) {
|
||||
// You can see the address if you took the action.
|
||||
$can_see_ip = true;
|
||||
} else if (!$actor_phid && ($viewer_phid == $user_phid)) {
|
||||
// You can see the address if it wasn't authenticated and applied
|
||||
// to you (partial login).
|
||||
$can_see_ip = true;
|
||||
} else {
|
||||
// You can't see the address when an administrator disables your
|
||||
// account, since it's their address.
|
||||
$can_see_ip = false;
|
||||
}
|
||||
|
||||
if ($can_see_ip) {
|
||||
$ip = $log->getRemoteAddr();
|
||||
if ($base_uri) {
|
||||
$ip = phutil_tag(
|
||||
'a',
|
||||
|
@ -44,18 +64,31 @@ final class PhabricatorUserLogView extends AphrontView {
|
|||
),
|
||||
$ip);
|
||||
}
|
||||
} else {
|
||||
$ip = null;
|
||||
}
|
||||
|
||||
$action = $log->getAction();
|
||||
$action_name = idx($action_map, $action, $action);
|
||||
|
||||
if ($actor_phid) {
|
||||
$actor_name = $handles[$actor_phid]->renderLink();
|
||||
} else {
|
||||
$actor_name = null;
|
||||
}
|
||||
|
||||
if ($user_phid) {
|
||||
$user_name = $handles[$user_phid]->renderLink();
|
||||
} else {
|
||||
$user_name = null;
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
phabricator_date($log->getDateCreated(), $viewer),
|
||||
phabricator_time($log->getDateCreated(), $viewer),
|
||||
$action_name,
|
||||
$log->getActorPHID()
|
||||
? $handles[$log->getActorPHID()]->getName()
|
||||
: null,
|
||||
$username = $handles[$log->getUserPHID()]->renderLink(),
|
||||
$actor_name,
|
||||
$user_name,
|
||||
$ip,
|
||||
$session,
|
||||
);
|
||||
|
|
|
@ -7,6 +7,8 @@ final class PhabricatorRepositoryPullEventQuery
|
|||
private $phids;
|
||||
private $repositoryPHIDs;
|
||||
private $pullerPHIDs;
|
||||
private $epochMin;
|
||||
private $epochMax;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -28,6 +30,12 @@ final class PhabricatorRepositoryPullEventQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withEpochBetween($min, $max) {
|
||||
$this->epochMin = $min;
|
||||
$this->epochMax = $max;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorRepositoryPullEvent();
|
||||
}
|
||||
|
@ -103,6 +111,20 @@ final class PhabricatorRepositoryPullEventQuery
|
|||
$this->pullerPHIDs);
|
||||
}
|
||||
|
||||
if ($this->epochMin !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'epoch >= %d',
|
||||
$this->epochMin);
|
||||
}
|
||||
|
||||
if ($this->epochMax !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'epoch <= %d',
|
||||
$this->epochMax);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ final class PhabricatorRepositoryPushLogQuery
|
|||
private $refTypes;
|
||||
private $newRefs;
|
||||
private $pushEventPHIDs;
|
||||
private $epochMin;
|
||||
private $epochMax;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -46,19 +48,18 @@ final class PhabricatorRepositoryPushLogQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withEpochBetween($min, $max) {
|
||||
$this->epochMin = $min;
|
||||
$this->epochMax = $max;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorRepositoryPushLog();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new PhabricatorRepositoryPushLog();
|
||||
$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 $logs) {
|
||||
|
@ -82,61 +83,73 @@ final class PhabricatorRepositoryPushLogQuery
|
|||
return $logs;
|
||||
}
|
||||
|
||||
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->phids) {
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->repositoryPHIDs) {
|
||||
if ($this->repositoryPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'repositoryPHID IN (%Ls)',
|
||||
$this->repositoryPHIDs);
|
||||
}
|
||||
|
||||
if ($this->pusherPHIDs) {
|
||||
if ($this->pusherPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'pusherPHID in (%Ls)',
|
||||
$this->pusherPHIDs);
|
||||
}
|
||||
|
||||
if ($this->pushEventPHIDs) {
|
||||
if ($this->pushEventPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'pushEventPHID in (%Ls)',
|
||||
$this->pushEventPHIDs);
|
||||
}
|
||||
|
||||
if ($this->refTypes) {
|
||||
if ($this->refTypes !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'refType IN (%Ls)',
|
||||
$this->refTypes);
|
||||
}
|
||||
|
||||
if ($this->newRefs) {
|
||||
if ($this->newRefs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'refNew IN (%Ls)',
|
||||
$this->newRefs);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
if ($this->epochMin !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'epoch >= %d',
|
||||
$this->epochMin);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
if ($this->epochMax !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'epoch <= %d',
|
||||
$this->epochMax);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
|
|
|
@ -26,6 +26,12 @@ final class PhabricatorRepositoryPushLogSearchEngine
|
|||
$query->withPusherPHIDs($map['pusherPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['createdStart'] || $map['createdEnd']) {
|
||||
$query->withEpochBetween(
|
||||
$map['createdStart'],
|
||||
$map['createdEnd']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
@ -44,6 +50,12 @@ final class PhabricatorRepositoryPushLogSearchEngine
|
|||
->setLabel(pht('Pushers'))
|
||||
->setDescription(
|
||||
pht('Search for pull logs by specific users.')),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Created After'))
|
||||
->setKey('createdStart'),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Created Before'))
|
||||
->setKey('createdEnd'),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -82,4 +94,146 @@ final class PhabricatorRepositoryPushLogSearchEngine
|
|||
->setTable($table);
|
||||
}
|
||||
|
||||
protected function newExportFields() {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$fields = array(
|
||||
$fields[] = id(new PhabricatorIDExportField())
|
||||
->setKey('pushID')
|
||||
->setLabel(pht('Push ID')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('protocol')
|
||||
->setLabel(pht('Protocol')),
|
||||
$fields[] = id(new PhabricatorPHIDExportField())
|
||||
->setKey('repositoryPHID')
|
||||
->setLabel(pht('Repository PHID')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('repository')
|
||||
->setLabel(pht('Repository')),
|
||||
$fields[] = id(new PhabricatorPHIDExportField())
|
||||
->setKey('pusherPHID')
|
||||
->setLabel(pht('Pusher PHID')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('pusher')
|
||||
->setLabel(pht('Pusher')),
|
||||
$fields[] = id(new PhabricatorPHIDExportField())
|
||||
->setKey('devicePHID')
|
||||
->setLabel(pht('Device PHID')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('device')
|
||||
->setLabel(pht('Device')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('type')
|
||||
->setLabel(pht('Ref Type')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('name')
|
||||
->setLabel(pht('Ref Name')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('old')
|
||||
->setLabel(pht('Ref Old')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('new')
|
||||
->setLabel(pht('Ref New')),
|
||||
$fields[] = id(new PhabricatorIntExportField())
|
||||
->setKey('flags')
|
||||
->setLabel(pht('Flags')),
|
||||
$fields[] = id(new PhabricatorStringListExportField())
|
||||
->setKey('flagNames')
|
||||
->setLabel(pht('Flag Names')),
|
||||
$fields[] = id(new PhabricatorIntExportField())
|
||||
->setKey('result')
|
||||
->setLabel(pht('Result')),
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('resultName')
|
||||
->setLabel(pht('Result Name')),
|
||||
);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('remoteAddress')
|
||||
->setLabel(pht('Remote Address'));
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function newExportData(array $logs) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$phids = array();
|
||||
foreach ($logs as $log) {
|
||||
$phids[] = $log->getPusherPHID();
|
||||
$phids[] = $log->getDevicePHID();
|
||||
$phids[] = $log->getPushEvent()->getRepositoryPHID();
|
||||
}
|
||||
$handles = $viewer->loadHandles($phids);
|
||||
|
||||
$flag_map = PhabricatorRepositoryPushLog::getFlagDisplayNames();
|
||||
$reject_map = PhabricatorRepositoryPushLog::getRejectCodeDisplayNames();
|
||||
|
||||
$export = array();
|
||||
foreach ($logs as $log) {
|
||||
$event = $log->getPushEvent();
|
||||
|
||||
$repository_phid = $event->getRepositoryPHID();
|
||||
if ($repository_phid) {
|
||||
$repository_name = $handles[$repository_phid]->getName();
|
||||
} else {
|
||||
$repository_name = null;
|
||||
}
|
||||
|
||||
$pusher_phid = $log->getPusherPHID();
|
||||
if ($pusher_phid) {
|
||||
$pusher_name = $handles[$pusher_phid]->getName();
|
||||
} else {
|
||||
$pusher_name = null;
|
||||
}
|
||||
|
||||
$device_phid = $log->getDevicePHID();
|
||||
if ($device_phid) {
|
||||
$device_name = $handles[$device_phid]->getName();
|
||||
} else {
|
||||
$device_name = null;
|
||||
}
|
||||
|
||||
$flags = $log->getChangeFlags();
|
||||
$flag_names = array();
|
||||
foreach ($flag_map as $flag_key => $flag_name) {
|
||||
if (($flags & $flag_key) === $flag_key) {
|
||||
$flag_names[] = $flag_name;
|
||||
}
|
||||
}
|
||||
|
||||
$result = $event->getRejectCode();
|
||||
$result_name = idx($reject_map, $result, pht('Unknown ("%s")', $result));
|
||||
|
||||
$map = array(
|
||||
'pushID' => $event->getID(),
|
||||
'protocol' => $event->getRemoteProtocol(),
|
||||
'repositoryPHID' => $repository_phid,
|
||||
'repository' => $repository_name,
|
||||
'pusherPHID' => $pusher_phid,
|
||||
'pusher' => $pusher_name,
|
||||
'devicePHID' => $device_phid,
|
||||
'device' => $device_name,
|
||||
'type' => $log->getRefType(),
|
||||
'name' => $log->getRefName(),
|
||||
'old' => $log->getRefOld(),
|
||||
'new' => $log->getRefNew(),
|
||||
'flags' => $flags,
|
||||
'flagNames' => $flag_names,
|
||||
'result' => $result,
|
||||
'resultName' => $result_name,
|
||||
);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$map['remoteAddress'] = $event->getRemoteAddress();
|
||||
}
|
||||
|
||||
$export[] = $map;
|
||||
}
|
||||
|
||||
return $export;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,28 @@ final class PhabricatorRepositoryPushLog
|
|||
->setPusherPHID($viewer->getPHID());
|
||||
}
|
||||
|
||||
public static function getFlagDisplayNames() {
|
||||
return array(
|
||||
self::CHANGEFLAG_ADD => pht('Create'),
|
||||
self::CHANGEFLAG_DELETE => pht('Delete'),
|
||||
self::CHANGEFLAG_APPEND => pht('Append'),
|
||||
self::CHANGEFLAG_REWRITE => pht('Rewrite'),
|
||||
self::CHANGEFLAG_DANGEROUS => pht('Dangerous'),
|
||||
self::CHANGEFLAG_ENORMOUS => pht('Enormous'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function getRejectCodeDisplayNames() {
|
||||
return array(
|
||||
self::REJECT_ACCEPT => pht('Accepted'),
|
||||
self::REJECT_DANGEROUS => pht('Rejected: Dangerous'),
|
||||
self::REJECT_HERALD => pht('Rejected: Herald'),
|
||||
self::REJECT_EXTERNAL => pht('Rejected: External Hook'),
|
||||
self::REJECT_BROKEN => pht('Rejected: Broken'),
|
||||
self::REJECT_ENORMOUS => pht('Rejected: Enormous'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function getHeraldChangeFlagConditionOptions() {
|
||||
return array(
|
||||
self::CHANGEFLAG_ADD =>
|
||||
|
@ -102,6 +124,9 @@ final class PhabricatorRepositoryPushLog
|
|||
'key_pusher' => array(
|
||||
'columns' => array('pusherPHID'),
|
||||
),
|
||||
'key_epoch' => array(
|
||||
'columns' => array('epoch'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ final class PhabricatorRepositoryCommitHeraldWorker
|
|||
protected function parseCommit(
|
||||
PhabricatorRepository $repository,
|
||||
PhabricatorRepositoryCommit $commit) {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
if ($this->shouldSkipImportStep()) {
|
||||
// This worker has no followup tasks, so we can just bail out
|
||||
|
@ -50,7 +51,7 @@ final class PhabricatorRepositoryCommitHeraldWorker
|
|||
id(new PhabricatorDiffusionApplication())->getPHID());
|
||||
|
||||
$editor = id(new PhabricatorAuditEditor())
|
||||
->setActor(PhabricatorUser::getOmnipotentUser())
|
||||
->setActor($viewer)
|
||||
->setActingAsPHID($acting_as_phid)
|
||||
->setContinueOnMissingFields(true)
|
||||
->setContinueOnNoEffect(true)
|
||||
|
@ -69,29 +70,6 @@ final class PhabricatorRepositoryCommitHeraldWorker
|
|||
'committerPHID' => $data->getCommitDetail('committerPHID'),
|
||||
));
|
||||
|
||||
$reverts_refs = id(new DifferentialCustomFieldRevertsParser())
|
||||
->parseCorpus($data->getCommitMessage());
|
||||
$reverts = array_mergev(ipull($reverts_refs, 'monograms'));
|
||||
|
||||
if ($reverts) {
|
||||
$reverted_commits = id(new DiffusionCommitQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withRepository($repository)
|
||||
->withIdentifiers($reverts)
|
||||
->execute();
|
||||
$reverted_commit_phids = mpull($reverted_commits, 'getPHID', 'getPHID');
|
||||
|
||||
// NOTE: Skip any write attempts if a user cleverly implies a commit
|
||||
// reverts itself.
|
||||
unset($reverted_commit_phids[$commit->getPHID()]);
|
||||
|
||||
$reverts_edge = DiffusionCommitRevertsCommitEdgeType::EDGECONST;
|
||||
$xactions[] = id(new PhabricatorAuditTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $reverts_edge)
|
||||
->setNewValue(array('+' => array_fuse($reverted_commit_phids)));
|
||||
}
|
||||
|
||||
try {
|
||||
$raw_patch = $this->loadRawPatchText($repository, $commit);
|
||||
} catch (Exception $ex) {
|
||||
|
|
|
@ -7,6 +7,7 @@ final class PhabricatorApplicationSearchController
|
|||
private $navigation;
|
||||
private $queryKey;
|
||||
private $preface;
|
||||
private $activeQuery;
|
||||
|
||||
public function setPreface($preface) {
|
||||
$this->preface = $preface;
|
||||
|
@ -45,6 +46,14 @@ final class PhabricatorApplicationSearchController
|
|||
return $this->searchEngine;
|
||||
}
|
||||
|
||||
protected function getActiveQuery() {
|
||||
if (!$this->activeQuery) {
|
||||
throw new Exception(pht('There is no active query yet.'));
|
||||
}
|
||||
|
||||
return $this->activeQuery;
|
||||
}
|
||||
|
||||
protected function validateDelegatingController() {
|
||||
$parent = $this->getDelegatingController();
|
||||
|
||||
|
@ -154,10 +163,12 @@ final class PhabricatorApplicationSearchController
|
|||
$saved_query = $engine->buildSavedQueryFromRequest($request);
|
||||
|
||||
// Save the query to generate a query key, so "Save Custom Query..." and
|
||||
// other features like Maniphest's "Export..." work correctly.
|
||||
// other features like "Bulk Edit" and "Export Data" work correctly.
|
||||
$engine->saveQuery($saved_query);
|
||||
}
|
||||
|
||||
$this->activeQuery = $saved_query;
|
||||
|
||||
$nav->selectFilter(
|
||||
'query/'.$saved_query->getQueryKey(),
|
||||
'query/advanced');
|
||||
|
@ -410,20 +421,72 @@ final class PhabricatorApplicationSearchController
|
|||
|
||||
if ($named_query) {
|
||||
$filename = $named_query->getQueryName();
|
||||
$sheet_title = $named_query->getQueryName();
|
||||
} else {
|
||||
$filename = $engine->getResultTypeDescription();
|
||||
$sheet_title = $engine->getResultTypeDescription();
|
||||
}
|
||||
$filename = phutil_utf8_strtolower($filename);
|
||||
$filename = PhabricatorFile::normalizeFileName($filename);
|
||||
|
||||
$formats = PhabricatorExportFormat::getAllEnabledExportFormats();
|
||||
$format_options = mpull($formats, 'getExportFormatName');
|
||||
$all_formats = PhabricatorExportFormat::getAllExportFormats();
|
||||
|
||||
$available_options = array();
|
||||
$unavailable_options = array();
|
||||
$formats = array();
|
||||
$unavailable_formats = array();
|
||||
foreach ($all_formats as $key => $format) {
|
||||
if ($format->isExportFormatEnabled()) {
|
||||
$available_options[$key] = $format->getExportFormatName();
|
||||
$formats[$key] = $format;
|
||||
} else {
|
||||
$unavailable_options[$key] = pht(
|
||||
'%s (Not Available)',
|
||||
$format->getExportFormatName());
|
||||
$unavailable_formats[$key] = $format;
|
||||
}
|
||||
}
|
||||
$format_options = $available_options + $unavailable_options;
|
||||
|
||||
// Try to default to the format the user used last time. If you just
|
||||
// exported to Excel, you probably want to export to Excel again.
|
||||
$format_key = $this->readExportFormatPreference();
|
||||
if (!isset($formats[$format_key])) {
|
||||
$format_key = head_key($format_options);
|
||||
}
|
||||
|
||||
// Check if this is a large result set or not. If we're exporting a
|
||||
// large amount of data, we'll build the actual export file in the daemons.
|
||||
|
||||
$threshold = 1000;
|
||||
$query = $engine->buildQueryFromSavedQuery($saved_query);
|
||||
$pager = $engine->newPagerForSavedQuery($saved_query);
|
||||
$pager->setPageSize($threshold + 1);
|
||||
$objects = $engine->executeQuery($query, $pager);
|
||||
$object_count = count($objects);
|
||||
$is_large_export = ($object_count > $threshold);
|
||||
|
||||
$errors = array();
|
||||
|
||||
$e_format = null;
|
||||
if ($request->isFormPost()) {
|
||||
$format_key = $request->getStr('format');
|
||||
|
||||
if (isset($unavailable_formats[$format_key])) {
|
||||
$unavailable = $unavailable_formats[$format_key];
|
||||
$instructions = $unavailable->getInstallInstructions();
|
||||
|
||||
$markup = id(new PHUIRemarkupView($viewer, $instructions))
|
||||
->setRemarkupOption(
|
||||
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS,
|
||||
false);
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Export Format Not Available'))
|
||||
->appendChild($markup)
|
||||
->addCancelButton($cancel_uri, pht('Done'));
|
||||
}
|
||||
|
||||
$format = idx($formats, $format_key);
|
||||
|
||||
if (!$format) {
|
||||
|
@ -432,54 +495,23 @@ final class PhabricatorApplicationSearchController
|
|||
}
|
||||
|
||||
if (!$errors) {
|
||||
$query = $engine->buildQueryFromSavedQuery($saved_query);
|
||||
$this->writeExportFormatPreference($format_key);
|
||||
|
||||
// NOTE: We aren't reading the pager from the request. Exports always
|
||||
// affect the entire result set.
|
||||
$pager = $engine->newPagerForSavedQuery($saved_query);
|
||||
$pager->setPageSize(0x7FFFFFFF);
|
||||
$export_engine = id(new PhabricatorExportEngine())
|
||||
->setViewer($viewer)
|
||||
->setSearchEngine($engine)
|
||||
->setSavedQuery($saved_query)
|
||||
->setTitle($sheet_title)
|
||||
->setFilename($filename)
|
||||
->setExportFormat($format);
|
||||
|
||||
$objects = $engine->executeQuery($query, $pager);
|
||||
if ($is_large_export) {
|
||||
$job = $export_engine->newBulkJob($request);
|
||||
|
||||
$extension = $format->getFileExtension();
|
||||
$mime_type = $format->getMIMEContentType();
|
||||
$filename = $filename.'.'.$extension;
|
||||
|
||||
$format = clone $format;
|
||||
$format->setViewer($viewer);
|
||||
|
||||
$export_data = $engine->newExport($objects);
|
||||
|
||||
if (count($export_data) !== count($objects)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Search engine exported the wrong number of objects, expected '.
|
||||
'%s but got %s.',
|
||||
phutil_count($objects),
|
||||
phutil_count($export_data)));
|
||||
}
|
||||
|
||||
$objects = array_values($objects);
|
||||
$export_data = array_values($export_data);
|
||||
|
||||
$field_list = $engine->newExportFieldList();
|
||||
$field_list = mpull($field_list, null, 'getKey');
|
||||
|
||||
for ($ii = 0; $ii < count($objects); $ii++) {
|
||||
$format->addObject($objects[$ii], $field_list, $export_data[$ii]);
|
||||
}
|
||||
|
||||
$export_result = $format->newFileData();
|
||||
|
||||
$file = PhabricatorFile::newFromFileData(
|
||||
$export_result,
|
||||
array(
|
||||
'name' => $filename,
|
||||
'authorPHID' => $viewer->getPHID(),
|
||||
'ttl.relative' => phutil_units('15 minutes in seconds'),
|
||||
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
||||
'mime-type' => $mime_type,
|
||||
));
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($job->getMonitorURI());
|
||||
} else {
|
||||
$file = $export_engine->exportFile();
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Download Results'))
|
||||
|
@ -491,6 +523,7 @@ final class PhabricatorApplicationSearchController
|
|||
->addSubmitButton(pht('Download Data'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$export_form = id(new AphrontFormView())
|
||||
->setViewer($viewer)
|
||||
|
@ -499,6 +532,7 @@ final class PhabricatorApplicationSearchController
|
|||
->setName('format')
|
||||
->setLabel(pht('Format'))
|
||||
->setError($e_format)
|
||||
->setValue($format_key)
|
||||
->setOptions($format_options));
|
||||
|
||||
return $this->newDialog()
|
||||
|
@ -844,10 +878,8 @@ final class PhabricatorApplicationSearchController
|
|||
|
||||
$engine = $this->getSearchEngine();
|
||||
$engine_class = get_class($engine);
|
||||
$query_key = $this->getQueryKey();
|
||||
if (!$query_key) {
|
||||
$query_key = $engine->getDefaultQueryKey();
|
||||
}
|
||||
|
||||
$query_key = $this->getActiveQuery()->getQueryKey();
|
||||
|
||||
$can_use = $engine->canUseInPanelContext();
|
||||
$is_installed = PhabricatorApplication::isClassInstalledForViewer(
|
||||
|
@ -914,4 +946,32 @@ final class PhabricatorApplicationSearchController
|
|||
return true;
|
||||
}
|
||||
|
||||
private function readExportFormatPreference() {
|
||||
$viewer = $this->getViewer();
|
||||
$export_key = PhabricatorPolicyFavoritesSetting::SETTINGKEY;
|
||||
return $viewer->getUserSetting($export_key);
|
||||
}
|
||||
|
||||
private function writeExportFormatPreference($value) {
|
||||
$viewer = $this->getViewer();
|
||||
$request = $this->getRequest();
|
||||
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$export_key = PhabricatorPolicyFavoritesSetting::SETTINGKEY;
|
||||
$preferences = PhabricatorUserPreferences::loadUserPreferences($viewer);
|
||||
|
||||
$editor = id(new PhabricatorUserPreferencesEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = $preferences->newTransaction($export_key, $value);
|
||||
$editor->applyTransactions($preferences, $xactions);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1455,11 +1455,145 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
}
|
||||
|
||||
final public function newExportFieldList() {
|
||||
return $this->newExportFields();
|
||||
$object = $this->newResultObject();
|
||||
|
||||
$builtin_fields = array(
|
||||
id(new PhabricatorIDExportField())
|
||||
->setKey('id')
|
||||
->setLabel(pht('ID')),
|
||||
);
|
||||
|
||||
if ($object->getConfigOption(LiskDAO::CONFIG_AUX_PHID)) {
|
||||
$builtin_fields[] = id(new PhabricatorPHIDExportField())
|
||||
->setKey('phid')
|
||||
->setLabel(pht('PHID'));
|
||||
}
|
||||
|
||||
$fields = mpull($builtin_fields, null, 'getKey');
|
||||
|
||||
$export_fields = $this->newExportFields();
|
||||
foreach ($export_fields as $export_field) {
|
||||
$key = $export_field->getKey();
|
||||
|
||||
if (isset($fields[$key])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Search engine ("%s") defines an export field with a key ("%s") '.
|
||||
'that collides with another field. Each field must have a '.
|
||||
'unique key.',
|
||||
get_class($this),
|
||||
$key));
|
||||
}
|
||||
|
||||
$fields[$key] = $export_field;
|
||||
}
|
||||
|
||||
$extensions = $this->newExportExtensions();
|
||||
foreach ($extensions as $extension) {
|
||||
$extension_fields = $extension->newExportFields();
|
||||
foreach ($extension_fields as $extension_field) {
|
||||
$key = $extension_field->getKey();
|
||||
|
||||
if (isset($fields[$key])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Export engine extension ("%s") defines an export field with '.
|
||||
'a key ("%s") that collides with another field. Each field '.
|
||||
'must have a unique key.',
|
||||
get_class($extension_field),
|
||||
$key));
|
||||
}
|
||||
|
||||
$fields[$key] = $extension_field;
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
final public function newExport(array $objects) {
|
||||
$object = $this->newResultObject();
|
||||
$has_phid = $object->getConfigOption(LiskDAO::CONFIG_AUX_PHID);
|
||||
|
||||
$objects = array_values($objects);
|
||||
$n = count($objects);
|
||||
|
||||
$maps = array();
|
||||
foreach ($objects as $object) {
|
||||
$map = array(
|
||||
'id' => $object->getID(),
|
||||
);
|
||||
|
||||
if ($has_phid) {
|
||||
$map['phid'] = $object->getPHID();
|
||||
}
|
||||
|
||||
$maps[] = $map;
|
||||
}
|
||||
|
||||
$export_data = $this->newExportData($objects);
|
||||
$export_data = array_values($export_data);
|
||||
if (count($export_data) !== count($objects)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Search engine ("%s") exported the wrong number of objects, '.
|
||||
'expected %s but got %s.',
|
||||
get_class($this),
|
||||
phutil_count($objects),
|
||||
phutil_count($export_data)));
|
||||
}
|
||||
|
||||
for ($ii = 0; $ii < $n; $ii++) {
|
||||
$maps[$ii] += $export_data[$ii];
|
||||
}
|
||||
|
||||
$extensions = $this->newExportExtensions();
|
||||
foreach ($extensions as $extension) {
|
||||
$extension_data = $extension->newExportData($objects);
|
||||
$extension_data = array_values($extension_data);
|
||||
if (count($export_data) !== count($objects)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Export engine extension ("%s") exported the wrong number of '.
|
||||
'objects, expected %s but got %s.',
|
||||
get_class($extension),
|
||||
phutil_count($objects),
|
||||
phutil_count($export_data)));
|
||||
}
|
||||
|
||||
for ($ii = 0; $ii < $n; $ii++) {
|
||||
$maps[$ii] += $extension_data[$ii];
|
||||
}
|
||||
}
|
||||
|
||||
return $maps;
|
||||
}
|
||||
|
||||
protected function newExportFields() {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function newExportData(array $objects) {
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
private function newExportExtensions() {
|
||||
$object = $this->newResultObject();
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$extensions = PhabricatorExportEngineExtension::getAllExtensions();
|
||||
|
||||
$supported = array();
|
||||
foreach ($extensions as $extension) {
|
||||
$extension = clone $extension;
|
||||
$extension->setViewer($viewer);
|
||||
|
||||
if ($extension->supportsObject($object)) {
|
||||
$supported[] = $extension;
|
||||
}
|
||||
}
|
||||
|
||||
return $supported;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,25 +26,9 @@ final class PhabricatorActivitySettingsPanel extends PhabricatorSettingsPanel {
|
|||
->withRelatedPHIDs(array($user->getPHID()))
|
||||
->executeWithCursorPager($pager);
|
||||
|
||||
$phids = array();
|
||||
foreach ($logs as $log) {
|
||||
$phids[] = $log->getUserPHID();
|
||||
$phids[] = $log->getActorPHID();
|
||||
}
|
||||
|
||||
if ($phids) {
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($phids)
|
||||
->execute();
|
||||
} else {
|
||||
$handles = array();
|
||||
}
|
||||
|
||||
$table = id(new PhabricatorUserLogView())
|
||||
->setUser($viewer)
|
||||
->setLogs($logs)
|
||||
->setHandles($handles);
|
||||
->setLogs($logs);
|
||||
|
||||
$panel = $this->newBox(pht('Account Activity Logs'), $table);
|
||||
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorBulkManagementExportWorkflow
|
||||
extends PhabricatorBulkManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('export')
|
||||
->setExamples('**export** [options]')
|
||||
->setSynopsis(
|
||||
pht('Export data to a flat file (JSON, CSV, Excel, etc).'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'class',
|
||||
'param' => 'class',
|
||||
'help' => pht(
|
||||
'SearchEngine class to export data from.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'format',
|
||||
'param' => 'format',
|
||||
'help' => pht('Export format.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'query',
|
||||
'param' => 'key',
|
||||
'help' => pht(
|
||||
'Export the data selected by this query.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'output',
|
||||
'param' => 'path',
|
||||
'help' => pht(
|
||||
'Write output to a file. If omitted, output will be sent to '.
|
||||
'stdout.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'overwrite',
|
||||
'help' => pht(
|
||||
'If the output file already exists, overwrite it instead of '.
|
||||
'raising an error.'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$class = $args->getArg('class');
|
||||
|
||||
if (!strlen($class)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specify a search engine class to export data from with '.
|
||||
'"--class".'));
|
||||
}
|
||||
|
||||
if (!is_subclass_of($class, 'PhabricatorApplicationSearchEngine')) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'SearchEngine class ("%s") is unknown.',
|
||||
$class));
|
||||
}
|
||||
|
||||
$engine = newv($class, array())
|
||||
->setViewer($viewer);
|
||||
|
||||
if (!$engine->canExport()) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'SearchEngine class ("%s") does not support data export.',
|
||||
$class));
|
||||
}
|
||||
|
||||
$query_key = $args->getArg('query');
|
||||
if (!strlen($query_key)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specify a query to export with "--query".'));
|
||||
}
|
||||
|
||||
if ($engine->isBuiltinQuery($query_key)) {
|
||||
$saved_query = $engine->buildSavedQueryFromBuiltin($query_key);
|
||||
} else if ($query_key) {
|
||||
$saved_query = id(new PhabricatorSavedQueryQuery())
|
||||
->setViewer($viewer)
|
||||
->withQueryKeys(array($query_key))
|
||||
->executeOne();
|
||||
} else {
|
||||
$saved_query = null;
|
||||
}
|
||||
|
||||
if (!$saved_query) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Failed to load saved query ("%s").',
|
||||
$query_key));
|
||||
}
|
||||
|
||||
$format_key = $args->getArg('format');
|
||||
if (!strlen($format_key)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specify an export format with "--format".'));
|
||||
}
|
||||
|
||||
$all_formats = PhabricatorExportFormat::getAllExportFormats();
|
||||
$format = idx($all_formats, $format_key);
|
||||
if (!$format) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Unknown export format ("%s"). Known formats are: %s.',
|
||||
$format_key,
|
||||
implode(', ', array_keys($all_formats))));
|
||||
}
|
||||
|
||||
if (!$format->isExportFormatEnabled()) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Export format ("%s") is not enabled.',
|
||||
$format_key));
|
||||
}
|
||||
|
||||
$is_overwrite = $args->getArg('overwrite');
|
||||
$output_path = $args->getArg('output');
|
||||
|
||||
if (!strlen($output_path) && $is_overwrite) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Flag "--overwrite" has no effect without "--output".'));
|
||||
}
|
||||
|
||||
if (!$is_overwrite) {
|
||||
if (Filesystem::pathExists($output_path)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Output path already exists. Use "--overwrite" to overwrite '.
|
||||
'it.'));
|
||||
}
|
||||
}
|
||||
|
||||
$export_engine = id(new PhabricatorExportEngine())
|
||||
->setViewer($viewer)
|
||||
->setTitle(pht('Export'))
|
||||
->setFilename(pht('export'))
|
||||
->setSearchEngine($engine)
|
||||
->setSavedQuery($saved_query)
|
||||
->setExportFormat($format);
|
||||
|
||||
$file = $export_engine->exportFile();
|
||||
|
||||
$iterator = $file->getFileDataIterator();
|
||||
|
||||
if (strlen($output_path)) {
|
||||
foreach ($iterator as $chunk) {
|
||||
Filesystem::appendFile($output_path, $chunk);
|
||||
}
|
||||
} else {
|
||||
foreach ($iterator as $chunk) {
|
||||
echo $chunk;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ abstract class PhabricatorPHIDListEditField
|
|||
|
||||
private $useEdgeTransactions;
|
||||
private $isSingleValue;
|
||||
private $isNullable;
|
||||
|
||||
public function setUseEdgeTransactions($use_edge_transactions) {
|
||||
$this->useEdgeTransactions = $use_edge_transactions;
|
||||
|
@ -30,13 +31,23 @@ abstract class PhabricatorPHIDListEditField
|
|||
return $this->isSingleValue;
|
||||
}
|
||||
|
||||
public function setIsNullable($is_nullable) {
|
||||
$this->isNullable = $is_nullable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsNullable() {
|
||||
return $this->isNullable;
|
||||
}
|
||||
|
||||
protected function newHTTPParameterType() {
|
||||
return new AphrontPHIDListHTTPParameterType();
|
||||
}
|
||||
|
||||
protected function newConduitParameterType() {
|
||||
if ($this->getIsSingleValue()) {
|
||||
return new ConduitPHIDParameterType();
|
||||
return id(new ConduitPHIDParameterType())
|
||||
->setIsNullable($this->getIsNullable());
|
||||
} else {
|
||||
return new ConduitPHIDListParameterType();
|
||||
}
|
||||
|
@ -99,7 +110,8 @@ abstract class PhabricatorPHIDListEditField
|
|||
}
|
||||
|
||||
return id(new PhabricatorDatasourceEditType())
|
||||
->setIsSingleValue($this->getIsSingleValue());
|
||||
->setIsSingleValue($this->getIsSingleValue())
|
||||
->setIsNullable($this->getIsNullable());
|
||||
}
|
||||
|
||||
protected function newBulkEditTypes() {
|
||||
|
|
|
@ -6,6 +6,7 @@ abstract class PhabricatorPHIDListEditType
|
|||
private $datasource;
|
||||
private $isSingleValue;
|
||||
private $defaultValue;
|
||||
private $isNullable;
|
||||
|
||||
public function setDatasource(PhabricatorTypeaheadDatasource $datasource) {
|
||||
$this->datasource = $datasource;
|
||||
|
@ -30,16 +31,17 @@ abstract class PhabricatorPHIDListEditType
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getDefaultValue() {
|
||||
return $this->defaultValue;
|
||||
public function setIsNullable($is_nullable) {
|
||||
$this->isNullable = $is_nullable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValueType() {
|
||||
if ($this->getIsSingleValue()) {
|
||||
return 'phid';
|
||||
} else {
|
||||
return 'list<phid>';
|
||||
public function getIsNullable() {
|
||||
return $this->isNullable;
|
||||
}
|
||||
|
||||
public function getDefaultValue() {
|
||||
return $this->defaultValue;
|
||||
}
|
||||
|
||||
protected function newConduitParameterType() {
|
||||
|
@ -49,7 +51,8 @@ abstract class PhabricatorPHIDListEditType
|
|||
}
|
||||
|
||||
if ($this->getIsSingleValue()) {
|
||||
return new ConduitPHIDParameterType();
|
||||
return id(new ConduitPHIDParameterType())
|
||||
->setIsNullable($this->getIsNullable());
|
||||
} else {
|
||||
return new ConduitPHIDListParameterType();
|
||||
}
|
||||
|
|
|
@ -458,6 +458,12 @@ abstract class PhabricatorApplicationTransaction
|
|||
case PhabricatorTransactions::TYPE_JOIN_POLICY:
|
||||
return 'fa-lock';
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
switch ($this->getMetadataValue('edge:type')) {
|
||||
case DiffusionCommitRevertedByCommitEdgeType::EDGECONST:
|
||||
return 'fa-undo';
|
||||
case DiffusionCommitRevertsCommitEdgeType::EDGECONST:
|
||||
return 'fa-ambulance';
|
||||
}
|
||||
return 'fa-link';
|
||||
case PhabricatorTransactions::TYPE_BUILDABLE:
|
||||
return 'fa-wrench';
|
||||
|
@ -496,6 +502,14 @@ abstract class PhabricatorApplicationTransaction
|
|||
return 'black';
|
||||
}
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
switch ($this->getMetadataValue('edge:type')) {
|
||||
case DiffusionCommitRevertedByCommitEdgeType::EDGECONST:
|
||||
return 'pink';
|
||||
case DiffusionCommitRevertsCommitEdgeType::EDGECONST:
|
||||
return 'sky';
|
||||
}
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_BUILDABLE:
|
||||
switch ($this->getNewValue()) {
|
||||
case HarbormasterBuildable::STATUS_PASSED:
|
||||
|
|
|
@ -35,6 +35,7 @@ abstract class PhabricatorCustomField extends Phobject {
|
|||
const ROLE_HERALD = 'herald';
|
||||
const ROLE_EDITENGINE = 'EditEngine';
|
||||
const ROLE_HERALDACTION = 'herald.action';
|
||||
const ROLE_EXPORT = 'export';
|
||||
|
||||
|
||||
/* -( Building Applications with Custom Fields )--------------------------- */
|
||||
|
@ -299,6 +300,8 @@ abstract class PhabricatorCustomField extends Phobject {
|
|||
case self::ROLE_EDITENGINE:
|
||||
return $this->shouldAppearInEditView() ||
|
||||
$this->shouldAppearInEditEngine();
|
||||
case self::ROLE_EXPORT:
|
||||
return $this->shouldAppearInDataExport();
|
||||
case self::ROLE_DEFAULT:
|
||||
return true;
|
||||
default:
|
||||
|
@ -1362,6 +1365,46 @@ abstract class PhabricatorCustomField extends Phobject {
|
|||
}
|
||||
|
||||
|
||||
/* -( Data Export )-------------------------------------------------------- */
|
||||
|
||||
|
||||
public function shouldAppearInDataExport() {
|
||||
if ($this->proxy) {
|
||||
return $this->proxy->shouldAppearInDataExport();
|
||||
}
|
||||
|
||||
try {
|
||||
$this->newExportFieldType();
|
||||
return true;
|
||||
} catch (PhabricatorCustomFieldImplementationIncompleteException $ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function newExportField() {
|
||||
if ($this->proxy) {
|
||||
return $this->proxy->newExportField();
|
||||
}
|
||||
|
||||
return $this->newExportFieldType()
|
||||
->setLabel($this->getFieldName());
|
||||
}
|
||||
|
||||
public function newExportData() {
|
||||
if ($this->proxy) {
|
||||
return $this->proxy->newExportData();
|
||||
}
|
||||
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
|
||||
}
|
||||
|
||||
protected function newExportFieldType() {
|
||||
if ($this->proxy) {
|
||||
return $this->proxy->newExportFieldType();
|
||||
}
|
||||
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
|
||||
}
|
||||
|
||||
|
||||
/* -( Conduit )------------------------------------------------------------ */
|
||||
|
||||
|
||||
|
|
|
@ -496,5 +496,8 @@ abstract class PhabricatorStandardCustomField
|
|||
return $this->getFieldValue();
|
||||
}
|
||||
|
||||
public function newExportData() {
|
||||
return $this->getFieldValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -124,4 +124,8 @@ final class PhabricatorStandardCustomFieldInt
|
|||
return new ConduitIntParameterType();
|
||||
}
|
||||
|
||||
protected function newExportFieldType() {
|
||||
return new PhabricatorIntExportField();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -76,4 +76,8 @@ final class PhabricatorStandardCustomFieldText
|
|||
return new ConduitStringParameterType();
|
||||
}
|
||||
|
||||
protected function newExportFieldType() {
|
||||
return new PhabricatorStringExportField();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,4 +25,24 @@ abstract class PhabricatorWorkerBulkJobType extends Phobject {
|
|||
->execute();
|
||||
}
|
||||
|
||||
public function getCurtainActions(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorWorkerBulkJob $job) {
|
||||
|
||||
if ($job->isConfirming()) {
|
||||
$continue_uri = $job->getMonitorURI();
|
||||
} else {
|
||||
$continue_uri = $job->getDoneURI();
|
||||
}
|
||||
|
||||
$continue = id(new PhabricatorActionView())
|
||||
->setHref($continue_uri)
|
||||
->setIcon('fa-arrow-circle-o-right')
|
||||
->setName(pht('Continue'));
|
||||
|
||||
return array(
|
||||
$continue,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -77,6 +77,10 @@ abstract class PhabricatorWorkerBulkJobWorker
|
|||
pht('Job actor does not have permission to edit job.'));
|
||||
}
|
||||
|
||||
// Allow the worker to fill user caches inline; bulk jobs occasionally
|
||||
// need to access user preferences.
|
||||
$actor->setAllowInlineCacheGeneration(true);
|
||||
|
||||
return $actor;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* An bulk job which can not be parallelized and executes only one task.
|
||||
*/
|
||||
abstract class PhabricatorWorkerSingleBulkJobType
|
||||
extends PhabricatorWorkerBulkJobType {
|
||||
|
||||
public function getDescriptionForConfirm(PhabricatorWorkerBulkJob $job) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getJobSize(PhabricatorWorkerBulkJob $job) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function createTasks(PhabricatorWorkerBulkJob $job) {
|
||||
$tasks = array();
|
||||
|
||||
$tasks[] = PhabricatorWorkerBulkTask::initializeNewTask(
|
||||
$job,
|
||||
$job->getPHID());
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
}
|
|
@ -180,6 +180,10 @@ final class PhabricatorWorkerBulkJob
|
|||
return $this->getJobImplementation()->getJobName($this);
|
||||
}
|
||||
|
||||
public function getCurtainActions(PhabricatorUser $viewer) {
|
||||
return $this->getJobImplementation()->getCurtainActions($viewer, $this);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEpochExportField
|
||||
extends PhabricatorExportField {
|
||||
|
||||
private $zone;
|
||||
|
||||
public function getTextValue($value) {
|
||||
if (!isset($this->zone)) {
|
||||
$this->zone = new DateTimeZone('UTC');
|
||||
}
|
||||
|
||||
try {
|
||||
$date = new DateTime('@'.$value);
|
||||
} catch (Exception $ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$date->setTimezone($this->zone);
|
||||
return $date->format('c');
|
||||
}
|
||||
|
||||
public function getNaturalValue($value) {
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorIntExportField
|
||||
extends PhabricatorExportField {
|
||||
|
||||
public function getNaturalValue($value) {
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorStringExportField
|
||||
extends PhabricatorExportField {}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCustomFieldExportEngineExtension
|
||||
extends PhabricatorExportEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'custom-field';
|
||||
|
||||
private $object;
|
||||
|
||||
public function supportsObject($object) {
|
||||
$this->object = $object;
|
||||
return ($object instanceof PhabricatorCustomFieldInterface);
|
||||
}
|
||||
|
||||
public function newExportFields() {
|
||||
$prototype = $this->object;
|
||||
|
||||
$fields = $this->newCustomFields($prototype);
|
||||
|
||||
$results = array();
|
||||
foreach ($fields as $field) {
|
||||
$field_key = $field->getModernFieldKey();
|
||||
|
||||
$results[] = $field->newExportField()
|
||||
->setKey($field_key);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function newExportData(array $objects) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$field_map = array();
|
||||
foreach ($objects as $object) {
|
||||
$object_phid = $object->getPHID();
|
||||
|
||||
$fields = PhabricatorCustomField::getObjectFields(
|
||||
$object,
|
||||
PhabricatorCustomField::ROLE_EXPORT);
|
||||
|
||||
$fields
|
||||
->setViewer($viewer)
|
||||
->readFieldsFromObject($object);
|
||||
|
||||
$field_map[$object_phid] = $fields;
|
||||
}
|
||||
|
||||
$all_fields = array();
|
||||
foreach ($field_map as $field_list) {
|
||||
foreach ($field_list->getFields() as $field) {
|
||||
$all_fields[] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
id(new PhabricatorCustomFieldStorageQuery())
|
||||
->addFields($all_fields)
|
||||
->execute();
|
||||
|
||||
$results = array();
|
||||
foreach ($objects as $object) {
|
||||
$object_phid = $object->getPHID();
|
||||
$object_fields = $field_map[$object_phid];
|
||||
|
||||
$map = array();
|
||||
foreach ($object_fields->getFields() as $field) {
|
||||
$key = $field->getModernFieldKey();
|
||||
$map[$key] = $field->newExportData();
|
||||
}
|
||||
|
||||
$results[] = $map;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function newCustomFields($object) {
|
||||
$fields = PhabricatorCustomField::getObjectFields(
|
||||
$object,
|
||||
PhabricatorCustomField::ROLE_EXPORT);
|
||||
$fields->setViewer($this->getViewer());
|
||||
|
||||
return $fields->getFields();
|
||||
}
|
||||
|
||||
}
|
168
src/infrastructure/export/engine/PhabricatorExportEngine.php
Normal file
168
src/infrastructure/export/engine/PhabricatorExportEngine.php
Normal file
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorExportEngine
|
||||
extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
private $searchEngine;
|
||||
private $savedQuery;
|
||||
private $exportFormat;
|
||||
private $filename;
|
||||
private $title;
|
||||
|
||||
public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
public function setSearchEngine(
|
||||
PhabricatorApplicationSearchEngine $search_engine) {
|
||||
$this->searchEngine = $search_engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSearchEngine() {
|
||||
return $this->searchEngine;
|
||||
}
|
||||
|
||||
public function setSavedQuery(PhabricatorSavedQuery $saved_query) {
|
||||
$this->savedQuery = $saved_query;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSavedQuery() {
|
||||
return $this->savedQuery;
|
||||
}
|
||||
|
||||
public function setExportFormat(
|
||||
PhabricatorExportFormat $export_format) {
|
||||
$this->exportFormat = $export_format;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExportFormat() {
|
||||
return $this->exportFormat;
|
||||
}
|
||||
|
||||
public function setFilename($filename) {
|
||||
$this->filename = $filename;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFilename() {
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function newBulkJob(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$engine = $this->getSearchEngine();
|
||||
$saved_query = $this->getSavedQuery();
|
||||
$format = $this->getExportFormat();
|
||||
|
||||
$params = array(
|
||||
'engineClass' => get_class($engine),
|
||||
'queryKey' => $saved_query->getQueryKey(),
|
||||
'formatKey' => $format->getExportFormatKey(),
|
||||
'title' => $this->getTitle(),
|
||||
'filename' => $this->getFilename(),
|
||||
);
|
||||
|
||||
$job = PhabricatorWorkerBulkJob::initializeNewJob(
|
||||
$viewer,
|
||||
new PhabricatorExportEngineBulkJobType(),
|
||||
$params);
|
||||
|
||||
// We queue these jobs directly into STATUS_WAITING without requiring
|
||||
// a confirmation from the user.
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$xactions[] = id(new PhabricatorWorkerBulkJobTransaction())
|
||||
->setTransactionType(PhabricatorWorkerBulkJobTransaction::TYPE_STATUS)
|
||||
->setNewValue(PhabricatorWorkerBulkJob::STATUS_WAITING);
|
||||
|
||||
$editor = id(new PhabricatorWorkerBulkJobEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnMissingFields(true)
|
||||
->applyTransactions($job, $xactions);
|
||||
|
||||
return $job;
|
||||
}
|
||||
|
||||
public function exportFile() {
|
||||
$viewer = $this->getViewer();
|
||||
$engine = $this->getSearchEngine();
|
||||
$saved_query = $this->getSavedQuery();
|
||||
$format = $this->getExportFormat();
|
||||
$title = $this->getTitle();
|
||||
$filename = $this->getFilename();
|
||||
|
||||
$query = $engine->buildQueryFromSavedQuery($saved_query);
|
||||
|
||||
$extension = $format->getFileExtension();
|
||||
$mime_type = $format->getMIMEContentType();
|
||||
$filename = $filename.'.'.$extension;
|
||||
|
||||
$format = id(clone $format)
|
||||
->setViewer($viewer)
|
||||
->setTitle($title);
|
||||
|
||||
$field_list = $engine->newExportFieldList();
|
||||
$field_list = mpull($field_list, null, 'getKey');
|
||||
$format->addHeaders($field_list);
|
||||
|
||||
// Iterate over the query results in large page so we don't have to hold
|
||||
// too much stuff in memory.
|
||||
$page_size = 1000;
|
||||
$page_cursor = null;
|
||||
do {
|
||||
$pager = $engine->newPagerForSavedQuery($saved_query);
|
||||
$pager->setPageSize($page_size);
|
||||
|
||||
if ($page_cursor !== null) {
|
||||
$pager->setAfterID($page_cursor);
|
||||
}
|
||||
|
||||
$objects = $engine->executeQuery($query, $pager);
|
||||
$objects = array_values($objects);
|
||||
$page_cursor = $pager->getNextPageID();
|
||||
|
||||
$export_data = $engine->newExport($objects);
|
||||
for ($ii = 0; $ii < count($objects); $ii++) {
|
||||
$format->addObject($objects[$ii], $field_list, $export_data[$ii]);
|
||||
}
|
||||
} while ($pager->getHasMoreResults());
|
||||
|
||||
$export_result = $format->newFileData();
|
||||
|
||||
// We have all the data in one big string and aren't actually
|
||||
// streaming it, but pretending that we are allows us to actviate
|
||||
// the chunk engine and store large files.
|
||||
$iterator = new ArrayIterator(array($export_result));
|
||||
|
||||
$source = id(new PhabricatorIteratorFileUploadSource())
|
||||
->setName($filename)
|
||||
->setViewPolicy(PhabricatorPolicies::POLICY_NOONE)
|
||||
->setMIMEType($mime_type)
|
||||
->setRelativeTTL(phutil_units('60 minutes in seconds'))
|
||||
->setAuthorPHID($viewer->getPHID())
|
||||
->setIterator($iterator);
|
||||
|
||||
return $source->uploadFile();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorExportEngineBulkJobType
|
||||
extends PhabricatorWorkerSingleBulkJobType {
|
||||
|
||||
public function getBulkJobTypeKey() {
|
||||
return 'export';
|
||||
}
|
||||
|
||||
public function getJobName(PhabricatorWorkerBulkJob $job) {
|
||||
return pht('Data Export');
|
||||
}
|
||||
|
||||
public function getCurtainActions(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorWorkerBulkJob $job) {
|
||||
$actions = array();
|
||||
|
||||
$file_phid = $job->getParameter('filePHID');
|
||||
if (!$file_phid) {
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setHref('#')
|
||||
->setIcon('fa-download')
|
||||
->setDisabled(true)
|
||||
->setName(pht('Exporting Data...'));
|
||||
} else {
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($file_phid))
|
||||
->executeOne();
|
||||
if (!$file) {
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setHref('#')
|
||||
->setIcon('fa-download')
|
||||
->setDisabled(true)
|
||||
->setName(pht('Temporary File Expired'));
|
||||
} else {
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setRenderAsForm(true)
|
||||
->setHref($file->getDownloadURI())
|
||||
->setIcon('fa-download')
|
||||
->setName(pht('Download Data Export'));
|
||||
}
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
|
||||
public function runTask(
|
||||
PhabricatorUser $actor,
|
||||
PhabricatorWorkerBulkJob $job,
|
||||
PhabricatorWorkerBulkTask $task) {
|
||||
|
||||
$engine_class = $job->getParameter('engineClass');
|
||||
if (!is_subclass_of($engine_class, 'PhabricatorApplicationSearchEngine')) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unknown search engine class "%s".',
|
||||
$engine_class));
|
||||
}
|
||||
|
||||
$engine = newv($engine_class, array())
|
||||
->setViewer($actor);
|
||||
|
||||
$query_key = $job->getParameter('queryKey');
|
||||
if ($engine->isBuiltinQuery($query_key)) {
|
||||
$saved_query = $engine->buildSavedQueryFromBuiltin($query_key);
|
||||
} else if ($query_key) {
|
||||
$saved_query = id(new PhabricatorSavedQueryQuery())
|
||||
->setViewer($actor)
|
||||
->withQueryKeys(array($query_key))
|
||||
->executeOne();
|
||||
} else {
|
||||
$saved_query = null;
|
||||
}
|
||||
|
||||
if (!$saved_query) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Failed to load saved query ("%s").',
|
||||
$query_key));
|
||||
}
|
||||
|
||||
$format_key = $job->getParameter('formatKey');
|
||||
|
||||
$all_formats = PhabricatorExportFormat::getAllExportFormats();
|
||||
$format = idx($all_formats, $format_key);
|
||||
if (!$format) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unknown export format ("%s").',
|
||||
$format_key));
|
||||
}
|
||||
|
||||
if (!$format->isExportFormatEnabled()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Export format ("%s") is not enabled.',
|
||||
$format_key));
|
||||
}
|
||||
|
||||
$export_engine = id(new PhabricatorExportEngine())
|
||||
->setViewer($actor)
|
||||
->setTitle($job->getParameter('title'))
|
||||
->setFilename($job->getParameter('filename'))
|
||||
->setSearchEngine($engine)
|
||||
->setSavedQuery($saved_query)
|
||||
->setExportFormat($format);
|
||||
|
||||
$file = $export_engine->exportFile();
|
||||
|
||||
$job
|
||||
->setParameter('filePHID', $file->getPHID())
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorExportEngineExtension extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
|
||||
final public function getExtensionKey() {
|
||||
return $this->getPhobjectClassConstant('EXTENSIONKEY');
|
||||
}
|
||||
|
||||
final public function setViewer($viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
abstract public function supportsObject($object);
|
||||
abstract public function newExportFields();
|
||||
abstract public function newExportData(array $objects);
|
||||
|
||||
final public static function getAllExtensions() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getExtensionKey')
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorExportFormatSetting
|
||||
extends PhabricatorInternalSetting {
|
||||
|
||||
const SETTINGKEY = 'export.format';
|
||||
|
||||
public function getSettingName() {
|
||||
return pht('Export Format');
|
||||
}
|
||||
|
||||
public function getSettingDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorLiskExportEngineExtension
|
||||
extends PhabricatorExportEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'lisk';
|
||||
|
||||
public function supportsObject($object) {
|
||||
if (!($object instanceof LiskDAO)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$object->getConfigOption(LiskDAO::CONFIG_TIMESTAMPS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function newExportFields() {
|
||||
return array(
|
||||
id(new PhabricatorEpochExportField())
|
||||
->setKey('dateCreated')
|
||||
->setLabel(pht('Created')),
|
||||
id(new PhabricatorEpochExportField())
|
||||
->setKey('dateModified')
|
||||
->setLabel(pht('Modified')),
|
||||
);
|
||||
}
|
||||
|
||||
public function newExportData(array $objects) {
|
||||
$map = array();
|
||||
foreach ($objects as $object) {
|
||||
$map[] = array(
|
||||
'dateCreated' => $object->getDateCreated(),
|
||||
'dateModified' => $object->getDateModified(),
|
||||
);
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorProjectsExportEngineExtension
|
||||
extends PhabricatorExportEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'projects';
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof PhabricatorProjectInterface);
|
||||
}
|
||||
|
||||
public function newExportFields() {
|
||||
return array(
|
||||
id(new PhabricatorPHIDListExportField())
|
||||
->setKey('tagPHIDs')
|
||||
->setLabel(pht('Tag PHIDs')),
|
||||
id(new PhabricatorStringListExportField())
|
||||
->setKey('tags')
|
||||
->setLabel(pht('Tags')),
|
||||
);
|
||||
}
|
||||
|
||||
public function newExportData(array $objects) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$object_phids = mpull($objects, 'getPHID');
|
||||
|
||||
$projects_query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs($object_phids)
|
||||
->withEdgeTypes(
|
||||
array(
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||
));
|
||||
$projects_query->execute();
|
||||
|
||||
$handles = $viewer->loadHandles($projects_query->getDestinationPHIDs());
|
||||
|
||||
$map = array();
|
||||
foreach ($objects as $object) {
|
||||
$object_phid = $object->getPHID();
|
||||
|
||||
$project_phids = $projects_query->getDestinationPHIDs(
|
||||
array($object_phid),
|
||||
array(PhabricatorProjectObjectHasProjectEdgeType::EDGECONST));
|
||||
|
||||
$handle_list = $handles->newSublist($project_phids);
|
||||
$handle_list = iterator_to_array($handle_list);
|
||||
$handle_names = mpull($handle_list, 'getName');
|
||||
$handle_names = array_values($handle_names);
|
||||
|
||||
$map[] = array(
|
||||
'tagPHIDs' => $project_phids,
|
||||
'tags' => $handle_names,
|
||||
);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSpacesExportEngineExtension
|
||||
extends PhabricatorExportEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'spaces';
|
||||
|
||||
public function supportsObject($object) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if (!PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($object instanceof PhabricatorSpacesInterface);
|
||||
}
|
||||
|
||||
public function newExportFields() {
|
||||
return array(
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('spacePHID')
|
||||
->setLabel(pht('Space PHID')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('space')
|
||||
->setLabel(pht('Space')),
|
||||
);
|
||||
}
|
||||
|
||||
public function newExportData(array $objects) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$space_phids = array();
|
||||
foreach ($objects as $object) {
|
||||
$space_phids[] = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
|
||||
$object);
|
||||
}
|
||||
$handles = $viewer->loadHandles($space_phids);
|
||||
|
||||
$map = array();
|
||||
foreach ($objects as $object) {
|
||||
$space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
|
||||
$object);
|
||||
|
||||
$map[] = array(
|
||||
'spacePHID' => $space_phid,
|
||||
'space' => $handles[$space_phid]->getName(),
|
||||
);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSubscriptionsExportEngineExtension
|
||||
extends PhabricatorExportEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'subscriptions';
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof PhabricatorSubscribableInterface);
|
||||
}
|
||||
|
||||
public function newExportFields() {
|
||||
return array(
|
||||
id(new PhabricatorPHIDListExportField())
|
||||
->setKey('subscriberPHIDs')
|
||||
->setLabel(pht('Subscriber PHIDs')),
|
||||
id(new PhabricatorStringListExportField())
|
||||
->setKey('subscribers')
|
||||
->setLabel(pht('Subscribers')),
|
||||
);
|
||||
}
|
||||
|
||||
public function newExportData(array $objects) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$object_phids = mpull($objects, 'getPHID');
|
||||
|
||||
$projects_query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs($object_phids)
|
||||
->withEdgeTypes(
|
||||
array(
|
||||
PhabricatorObjectHasSubscriberEdgeType::EDGECONST,
|
||||
));
|
||||
$projects_query->execute();
|
||||
|
||||
$handles = $viewer->loadHandles($projects_query->getDestinationPHIDs());
|
||||
|
||||
$map = array();
|
||||
foreach ($objects as $object) {
|
||||
$object_phid = $object->getPHID();
|
||||
|
||||
$project_phids = $projects_query->getDestinationPHIDs(
|
||||
array($object_phid),
|
||||
array(PhabricatorObjectHasSubscriberEdgeType::EDGECONST));
|
||||
|
||||
$handle_list = $handles->newSublist($project_phids);
|
||||
$handle_list = iterator_to_array($handle_list);
|
||||
$handle_names = mpull($handle_list, 'getName');
|
||||
$handle_names = array_values($handle_names);
|
||||
|
||||
$map[] = array(
|
||||
'subscriberPHIDs' => $project_phids,
|
||||
'subscribers' => $handle_names,
|
||||
);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEpochExportField
|
||||
extends PhabricatorExportField {
|
||||
|
||||
private $zone;
|
||||
|
||||
public function getTextValue($value) {
|
||||
if (!isset($this->zone)) {
|
||||
$this->zone = new DateTimeZone('UTC');
|
||||
}
|
||||
|
||||
try {
|
||||
$date = new DateTime('@'.$value);
|
||||
} catch (Exception $ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$date->setTimezone($this->zone);
|
||||
return $date->format('c');
|
||||
}
|
||||
|
||||
public function getNaturalValue($value) {
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
public function getPHPExcelValue($value) {
|
||||
$epoch = $this->getNaturalValue($value);
|
||||
|
||||
$seconds_per_day = phutil_units('1 day in seconds');
|
||||
$offset = ($seconds_per_day * 25569);
|
||||
|
||||
return ($epoch + $offset) / $seconds_per_day;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel_Style_NumberFormat
|
||||
*/
|
||||
public function formatPHPExcelCell($cell, $style) {
|
||||
$code = PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2;
|
||||
|
||||
$style
|
||||
->getNumberFormat()
|
||||
->setFormatCode($code);
|
||||
}
|
||||
|
||||
}
|
|
@ -25,11 +25,32 @@ abstract class PhabricatorExportField
|
|||
}
|
||||
|
||||
public function getTextValue($value) {
|
||||
return (string)$this->getNaturalValue($value);
|
||||
$natural_value = $this->getNaturalValue($value);
|
||||
|
||||
if ($natural_value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string)$natural_value;
|
||||
}
|
||||
|
||||
public function getNaturalValue($value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getPHPExcelValue($value) {
|
||||
return $this->getTextValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel_Cell_DataType
|
||||
*/
|
||||
public function formatPHPExcelCell($cell, $style) {
|
||||
$cell->setDataType(PHPExcel_Cell_DataType::TYPE_STRING);
|
||||
}
|
||||
|
||||
public function getCharacterWidth() {
|
||||
return 24;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,4 +7,8 @@ final class PhabricatorIDExportField
|
|||
return (int)$value;
|
||||
}
|
||||
|
||||
public function getCharacterWidth() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorIntExportField
|
||||
extends PhabricatorExportField {
|
||||
|
||||
public function getNaturalValue($value) {
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel_Cell_DataType
|
||||
*/
|
||||
public function formatPHPExcelCell($cell, $style) {
|
||||
$cell->setDataType(PHPExcel_Cell_DataType::TYPE_NUMERIC);
|
||||
}
|
||||
|
||||
public function getCharacterWidth() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorListExportField
|
||||
extends PhabricatorExportField {
|
||||
|
||||
public function getTextValue($value) {
|
||||
return implode("\n", $value);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPHIDExportField
|
||||
extends PhabricatorExportField {
|
||||
|
||||
public function getCharacterWidth() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPHIDListExportField
|
||||
extends PhabricatorListExportField {
|
||||
|
||||
public function getCharacterWidth() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorStringExportField
|
||||
extends PhabricatorExportField {
|
||||
|
||||
public function getNaturalValue($value) {
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!strlen($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorStringListExportField
|
||||
extends PhabricatorListExportField {}
|
|
@ -1,4 +1,4 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPHIDExportField
|
||||
final class PhabricatorURIExportField
|
||||
extends PhabricatorExportField {}
|
|
@ -23,21 +23,44 @@ final class PhabricatorCSVExportFormat
|
|||
return 'text/csv';
|
||||
}
|
||||
|
||||
public function addHeaders(array $fields) {
|
||||
$headers = mpull($fields, 'getLabel');
|
||||
$this->addRow($headers);
|
||||
}
|
||||
|
||||
public function addObject($object, array $fields, array $map) {
|
||||
$values = array();
|
||||
foreach ($fields as $key => $field) {
|
||||
$value = $map[$key];
|
||||
$value = $field->getTextValue($value);
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
$this->addRow($values);
|
||||
}
|
||||
|
||||
private function addRow(array $values) {
|
||||
$row = array();
|
||||
foreach ($values as $value) {
|
||||
|
||||
// Excel is extremely interested in executing arbitrary code it finds in
|
||||
// untrusted CSV files downloaded from the internet. When a cell looks
|
||||
// like it might be too tempting for Excel to ignore, mangle the value
|
||||
// to dissuade remote code execution. See T12800.
|
||||
|
||||
if (preg_match('/^\s*[+=@-]/', $value)) {
|
||||
$value = '(!) '.$value;
|
||||
}
|
||||
|
||||
if (preg_match('/\s|,|\"/', $value)) {
|
||||
$value = str_replace('"', '""', $value);
|
||||
$value = '"'.$value.'"';
|
||||
}
|
||||
|
||||
$values[] = $value;
|
||||
$row[] = $value;
|
||||
}
|
||||
|
||||
$this->rows[] = implode(',', $values);
|
||||
$this->rows[] = implode(',', $row);
|
||||
}
|
||||
|
||||
public function newFileData() {
|
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorExcelExportFormat
|
||||
extends PhabricatorExportFormat {
|
||||
|
||||
const EXPORTKEY = 'excel';
|
||||
|
||||
private $workbook;
|
||||
private $sheet;
|
||||
private $rowCursor;
|
||||
|
||||
public function getExportFormatName() {
|
||||
return pht('Excel (.xlsx)');
|
||||
}
|
||||
|
||||
public function isExportFormatEnabled() {
|
||||
// TODO: PHPExcel has a dependency on the PHP zip extension. We should test
|
||||
// for that here, since it fatals if we don't have the ZipArchive class.
|
||||
return @include_once 'PHPExcel.php';
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(<<<EOHELP
|
||||
Data can not be exported to Excel because the PHPExcel library is not
|
||||
installed. This software component is required for Phabricator to create
|
||||
Excel files.
|
||||
|
||||
You can install PHPExcel from GitHub:
|
||||
|
||||
> https://github.com/PHPOffice/PHPExcel
|
||||
|
||||
Briefly:
|
||||
|
||||
- Clone that repository somewhere on the sever
|
||||
(like `/path/to/example/PHPExcel`).
|
||||
- Update your PHP `%s` setting (in `php.ini`) to include the PHPExcel
|
||||
`Classes` directory (like `/path/to/example/PHPExcel/Classes`).
|
||||
EOHELP
|
||||
,
|
||||
'include_path');
|
||||
}
|
||||
|
||||
public function getFileExtension() {
|
||||
return 'xlsx';
|
||||
}
|
||||
|
||||
public function getMIMEContentType() {
|
||||
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel_Cell_DataType
|
||||
*/
|
||||
public function addHeaders(array $fields) {
|
||||
$sheet = $this->getSheet();
|
||||
|
||||
$header_format = array(
|
||||
'font' => array(
|
||||
'bold' => true,
|
||||
),
|
||||
);
|
||||
|
||||
$row = 1;
|
||||
$col = 0;
|
||||
foreach ($fields as $field) {
|
||||
$cell_value = $field->getLabel();
|
||||
|
||||
$cell_name = $this->getCellName($col, $row);
|
||||
|
||||
$cell = $sheet->setCellValue(
|
||||
$cell_name,
|
||||
$cell_value,
|
||||
$return_cell = true);
|
||||
|
||||
$sheet->getStyle($cell_name)->applyFromArray($header_format);
|
||||
$cell->setDataType(PHPExcel_Cell_DataType::TYPE_STRING);
|
||||
|
||||
$width = $field->getCharacterWidth();
|
||||
if ($width !== null) {
|
||||
$col_name = $this->getCellName($col);
|
||||
$sheet->getColumnDimension($col_name)
|
||||
->setWidth($width);
|
||||
}
|
||||
|
||||
$col++;
|
||||
}
|
||||
}
|
||||
|
||||
public function addObject($object, array $fields, array $map) {
|
||||
$sheet = $this->getSheet();
|
||||
|
||||
$col = 0;
|
||||
foreach ($fields as $key => $field) {
|
||||
$cell_value = $map[$key];
|
||||
$cell_value = $field->getPHPExcelValue($cell_value);
|
||||
|
||||
$cell_name = $this->getCellName($col, $this->rowCursor);
|
||||
|
||||
$cell = $sheet->setCellValue(
|
||||
$cell_name,
|
||||
$cell_value,
|
||||
$return_cell = true);
|
||||
|
||||
$style = $sheet->getStyle($cell_name);
|
||||
$field->formatPHPExcelCell($cell, $style);
|
||||
|
||||
$col++;
|
||||
}
|
||||
|
||||
$this->rowCursor++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel_IOFactory
|
||||
*/
|
||||
public function newFileData() {
|
||||
$workbook = $this->getWorkbook();
|
||||
$writer = PHPExcel_IOFactory::createWriter($workbook, 'Excel2007');
|
||||
|
||||
ob_start();
|
||||
$writer->save('php://output');
|
||||
$data = ob_get_clean();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getWorkbook() {
|
||||
if (!$this->workbook) {
|
||||
$this->workbook = $this->newWorkbook();
|
||||
}
|
||||
return $this->workbook;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel
|
||||
*/
|
||||
private function newWorkbook() {
|
||||
include_once 'PHPExcel.php';
|
||||
return new PHPExcel();
|
||||
}
|
||||
|
||||
private function getSheet() {
|
||||
if (!$this->sheet) {
|
||||
$workbook = $this->getWorkbook();
|
||||
|
||||
$sheet = $workbook->setActiveSheetIndex(0);
|
||||
$sheet->setTitle($this->getTitle());
|
||||
|
||||
$this->sheet = $sheet;
|
||||
|
||||
// The row cursor starts on the second row, after the header row.
|
||||
$this->rowCursor = 2;
|
||||
}
|
||||
|
||||
return $this->sheet;
|
||||
}
|
||||
|
||||
private function getCellName($col, $row = null) {
|
||||
$col_name = chr(ord('A') + $col);
|
||||
|
||||
if ($row === null) {
|
||||
return $col_name;
|
||||
}
|
||||
|
||||
return $col_name.$row;
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ abstract class PhabricatorExportFormat
|
|||
extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
private $title;
|
||||
|
||||
final public function getExportFormatKey() {
|
||||
return $this->getPhobjectClassConstant('EXPORTKEY');
|
||||
|
@ -18,10 +19,23 @@ abstract class PhabricatorExportFormat
|
|||
return $this->viewer;
|
||||
}
|
||||
|
||||
final public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getTitle() {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
abstract public function getExportFormatName();
|
||||
abstract public function getMIMEContentType();
|
||||
abstract public function getFileExtension();
|
||||
|
||||
public function addHeaders(array $fields) {
|
||||
return;
|
||||
}
|
||||
|
||||
abstract public function addObject($object, array $fields, array $map);
|
||||
abstract public function newFileData();
|
||||
|
||||
|
@ -36,16 +50,4 @@ abstract class PhabricatorExportFormat
|
|||
->execute();
|
||||
}
|
||||
|
||||
final public static function getAllEnabledExportFormats() {
|
||||
$formats = self::getAllExportFormats();
|
||||
|
||||
foreach ($formats as $key => $format) {
|
||||
if (!$format->isExportFormatEnabled()) {
|
||||
unset($formats[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $formats;
|
||||
}
|
||||
|
||||
}
|
|
@ -23,17 +23,29 @@ final class PhabricatorTextExportFormat
|
|||
return 'text/plain';
|
||||
}
|
||||
|
||||
public function addHeaders(array $fields) {
|
||||
$headers = mpull($fields, 'getLabel');
|
||||
$this->addRow($headers);
|
||||
}
|
||||
|
||||
public function addObject($object, array $fields, array $map) {
|
||||
$values = array();
|
||||
foreach ($fields as $key => $field) {
|
||||
$value = $map[$key];
|
||||
$value = $field->getTextValue($value);
|
||||
$value = addcslashes($value, "\0..\37\\\177..\377");
|
||||
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
$this->rows[] = implode("\t", $values);
|
||||
$this->addRow($values);
|
||||
}
|
||||
|
||||
private function addRow(array $values) {
|
||||
$row = array();
|
||||
foreach ($values as $value) {
|
||||
$row[] = addcslashes($value, "\0..\37\\\177..\377");
|
||||
}
|
||||
|
||||
$this->rows[] = implode("\t", $row);
|
||||
}
|
||||
|
||||
public function newFileData() {
|
|
@ -676,73 +676,73 @@ final class PhabricatorUSEnglishTranslation
|
|||
'%s edited commit(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited commits, added %3$s; removed %5$s.',
|
||||
|
||||
'%s added %s reverted commit(s): %s.' => array(
|
||||
'%s added %s reverted change(s): %s.' => array(
|
||||
array(
|
||||
'%s added a reverted commit: %3$s.',
|
||||
'%s added reverted commits: %3$s.',
|
||||
'%s added a reverted change: %3$s.',
|
||||
'%s added reverted changes: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %s reverted commit(s): %s.' => array(
|
||||
'%s removed %s reverted change(s): %s.' => array(
|
||||
array(
|
||||
'%s removed a reverted commit: %3$s.',
|
||||
'%s removed reverted commits: %3$s.',
|
||||
'%s removed a reverted change: %3$s.',
|
||||
'%s removed reverted changes: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s edited reverted commit(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited reverted commits, added %3$s; removed %5$s.',
|
||||
'%s edited reverted change(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited reverted changes, added %3$s; removed %5$s.',
|
||||
|
||||
'%s added %s reverted commit(s) for %s: %s.' => array(
|
||||
'%s added %s reverted change(s) for %s: %s.' => array(
|
||||
array(
|
||||
'%s added a reverted commit for %3$s: %4$s.',
|
||||
'%s added reverted commits for %3$s: %4$s.',
|
||||
'%s added a reverted change for %3$s: %4$s.',
|
||||
'%s added reverted changes for %3$s: %4$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %s reverted commit(s) for %s: %s.' => array(
|
||||
'%s removed %s reverted change(s) for %s: %s.' => array(
|
||||
array(
|
||||
'%s removed a reverted commit for %3$s: %4$s.',
|
||||
'%s removed reverted commits for %3$s: %4$s.',
|
||||
'%s removed a reverted change for %3$s: %4$s.',
|
||||
'%s removed reverted changes for %3$s: %4$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s edited reverted commit(s) for %s, added %s: %s; removed %s: %s.' =>
|
||||
'%s edited reverted commits for %2$s, added %4$s; removed %6$s.',
|
||||
'%s edited reverted change(s) for %s, added %s: %s; removed %s: %s.' =>
|
||||
'%s edited reverted changes for %2$s, added %4$s; removed %6$s.',
|
||||
|
||||
'%s added %s reverting commit(s): %s.' => array(
|
||||
'%s added %s reverting change(s): %s.' => array(
|
||||
array(
|
||||
'%s added a reverting commit: %3$s.',
|
||||
'%s added reverting commits: %3$s.',
|
||||
'%s added a reverting change: %3$s.',
|
||||
'%s added reverting changes: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %s reverting commit(s): %s.' => array(
|
||||
'%s removed %s reverting change(s): %s.' => array(
|
||||
array(
|
||||
'%s removed a reverting commit: %3$s.',
|
||||
'%s removed reverting commits: %3$s.',
|
||||
'%s removed a reverting change: %3$s.',
|
||||
'%s removed reverting changes: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s edited reverting commit(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited reverting commits, added %3$s; removed %5$s.',
|
||||
'%s edited reverting change(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited reverting changes, added %3$s; removed %5$s.',
|
||||
|
||||
'%s added %s reverting commit(s) for %s: %s.' => array(
|
||||
'%s added %s reverting change(s) for %s: %s.' => array(
|
||||
array(
|
||||
'%s added a reverting commit for %3$s: %4$s.',
|
||||
'%s added reverting commits for %3$s: %4$s.',
|
||||
'%s added a reverting change for %3$s: %4$s.',
|
||||
'%s added reverting changes for %3$s: %4$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %s reverting commit(s) for %s: %s.' => array(
|
||||
'%s removed %s reverting change(s) for %s: %s.' => array(
|
||||
array(
|
||||
'%s removed a reverting commit for %3$s: %4$s.',
|
||||
'%s removed reverting commits for %3$s: %4$s.',
|
||||
'%s removed a reverting change for %3$s: %4$s.',
|
||||
'%s removed reverting changes for %3$s: %4$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s edited reverting commit(s) for %s, added %s: %s; removed %s: %s.' =>
|
||||
'%s edited reverting commits for %s, added %4$s; removed %6$s.',
|
||||
'%s edited reverting change(s) for %s, added %s: %s; removed %s: %s.' =>
|
||||
'%s edited reverting changes for %s, added %4$s; removed %6$s.',
|
||||
|
||||
'%s changed project member(s), added %d: %s; removed %d: %s.' =>
|
||||
'%s changed project members, added %3$s; removed %5$s.',
|
||||
|
|
|
@ -301,18 +301,14 @@ final class PHUITimelineEventView extends AphrontView {
|
|||
|
||||
$menu = null;
|
||||
$items = array();
|
||||
$has_menu = false;
|
||||
if (!$this->getIsPreview() && !$this->getHideCommentOptions()) {
|
||||
foreach ($this->getEventGroup() as $event) {
|
||||
$items[] = $event->getMenuItems($this->anchor);
|
||||
if ($event->hasChildren()) {
|
||||
$has_menu = true;
|
||||
}
|
||||
}
|
||||
$items = array_mergev($items);
|
||||
}
|
||||
|
||||
if ($items || $has_menu) {
|
||||
if ($items) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-caret-down');
|
||||
$aural = javelin_tag(
|
||||
|
@ -351,6 +347,8 @@ final class PHUITimelineEventView extends AphrontView {
|
|||
));
|
||||
|
||||
$has_menu = true;
|
||||
} else {
|
||||
$has_menu = false;
|
||||
}
|
||||
|
||||
// Render "extra" information (timestamp, etc).
|
||||
|
|
|
@ -390,10 +390,6 @@
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.phui-timeline-menu .phui-icon-view {
|
||||
color: {$lightgreytext};
|
||||
}
|
||||
|
||||
a.phui-timeline-menu .phui-icon-view {
|
||||
color: {$bluetext};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue