Page MenuHomePhabricator

D18919.id45350.diff
No OneTemporary

D18919.id45350.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2833,12 +2833,14 @@
'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php',
'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php',
'PhabricatorEpochEditField' => 'applications/transactions/editfield/PhabricatorEpochEditField.php',
+ 'PhabricatorEpochExportField' => 'infrastructure/export/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',
'PhabricatorExecFutureFileUploadSource' => 'applications/files/uploadsource/PhabricatorExecFutureFileUploadSource.php',
+ 'PhabricatorExportField' => 'infrastructure/export/PhabricatorExportField.php',
'PhabricatorExtendedPolicyInterface' => 'applications/policy/interface/PhabricatorExtendedPolicyInterface.php',
'PhabricatorExtendingPhabricatorConfigOptions' => 'applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php',
'PhabricatorExtensionsSetupCheck' => 'applications/config/check/PhabricatorExtensionsSetupCheck.php',
@@ -3057,6 +3059,7 @@
'PhabricatorHomeProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeProfileMenuItem.php',
'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php',
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
+ 'PhabricatorIDExportField' => 'infrastructure/export/PhabricatorIDExportField.php',
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
'PhabricatorIDsSearchField' => 'applications/search/field/PhabricatorIDsSearchField.php',
'PhabricatorIconDatasource' => 'applications/files/typeahead/PhabricatorIconDatasource.php',
@@ -3408,6 +3411,7 @@
'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php',
'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php',
'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php',
+ 'PhabricatorPHIDExportField' => 'infrastructure/export/PhabricatorPHIDExportField.php',
'PhabricatorPHIDInterface' => 'applications/phid/interface/PhabricatorPHIDInterface.php',
'PhabricatorPHIDListEditField' => 'applications/transactions/editfield/PhabricatorPHIDListEditField.php',
'PhabricatorPHIDListEditType' => 'applications/transactions/edittype/PhabricatorPHIDListEditType.php',
@@ -4174,6 +4178,7 @@
'PhabricatorStorageSchemaSpec' => 'infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php',
'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php',
'PhabricatorStringConfigType' => 'applications/config/type/PhabricatorStringConfigType.php',
+ 'PhabricatorStringExportField' => 'infrastructure/export/PhabricatorStringExportField.php',
'PhabricatorStringListConfigType' => 'applications/config/type/PhabricatorStringListConfigType.php',
'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php',
'PhabricatorStringSetting' => 'applications/settings/setting/PhabricatorStringSetting.php',
@@ -8250,12 +8255,14 @@
'PhabricatorEnv' => 'Phobject',
'PhabricatorEnvTestCase' => 'PhabricatorTestCase',
'PhabricatorEpochEditField' => 'PhabricatorEditField',
+ 'PhabricatorEpochExportField' => 'PhabricatorExportField',
'PhabricatorEvent' => 'PhutilEvent',
'PhabricatorEventEngine' => 'Phobject',
'PhabricatorEventListener' => 'PhutilEventListener',
'PhabricatorEventType' => 'PhutilEventType',
'PhabricatorExampleEventListener' => 'PhabricatorEventListener',
'PhabricatorExecFutureFileUploadSource' => 'PhabricatorFileUploadSource',
+ 'PhabricatorExportField' => 'Phobject',
'PhabricatorExtendingPhabricatorConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorExtensionsSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorExternalAccount' => array(
@@ -8515,6 +8522,7 @@
'PhabricatorHomeProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorHovercardEngineExtension' => 'Phobject',
'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule',
+ 'PhabricatorIDExportField' => 'PhabricatorExportField',
'PhabricatorIDsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorIDsSearchField' => 'PhabricatorSearchField',
'PhabricatorIconDatasource' => 'PhabricatorTypeaheadDatasource',
@@ -8905,6 +8913,7 @@
'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPHID' => 'Phobject',
'PhabricatorPHIDConstants' => 'Phobject',
+ 'PhabricatorPHIDExportField' => 'PhabricatorExportField',
'PhabricatorPHIDListEditField' => 'PhabricatorEditField',
'PhabricatorPHIDListEditType' => 'PhabricatorEditType',
'PhabricatorPHIDResolver' => 'Phobject',
@@ -9844,6 +9853,7 @@
'PhabricatorStorageSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorStorageSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorStringConfigType' => 'PhabricatorTextConfigType',
+ 'PhabricatorStringExportField' => 'PhabricatorExportField',
'PhabricatorStringListConfigType' => 'PhabricatorTextListConfigType',
'PhabricatorStringListEditField' => 'PhabricatorEditField',
'PhabricatorStringSetting' => 'PhabricatorSetting',
diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php
--- a/src/applications/base/PhabricatorApplication.php
+++ b/src/applications/base/PhabricatorApplication.php
@@ -623,7 +623,7 @@
}
protected function getQueryRoutePattern($base = null) {
- return $base.'(?:query/(?P<queryKey>[^/]+)/)?';
+ return $base.'(?:query/(?P<queryKey>[^/]+)/(?:(?P<queryAction>[^/]+)/))?';
}
protected function getProfileMenuRouting($controller) {
diff --git a/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php b/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php
--- a/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php
+++ b/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php
@@ -47,6 +47,87 @@
);
}
+ protected function newExportFields() {
+ return array(
+ id(new PhabricatorIDExportField())
+ ->setKey('id')
+ ->setLabel(pht('ID')),
+ id(new PhabricatorPHIDExportField())
+ ->setKey('phid')
+ ->setLabel(pht('PHID')),
+ id(new PhabricatorPHIDExportField())
+ ->setKey('repositoryPHID')
+ ->setLabel(pht('Repository PHID')),
+ id(new PhabricatorStringExportField())
+ ->setKey('repository')
+ ->setLabel(pht('Repository')),
+ id(new PhabricatorPHIDExportField())
+ ->setKey('pullerPHID')
+ ->setLabel(pht('Puller PHID')),
+ id(new PhabricatorStringExportField())
+ ->setKey('puller')
+ ->setLabel(pht('Puller')),
+ id(new PhabricatorStringExportField())
+ ->setKey('protocol')
+ ->setLabel(pht('Protocol')),
+ id(new PhabricatorStringExportField())
+ ->setKey('result')
+ ->setLabel(pht('Result')),
+ id(new PhabricatorStringExportField())
+ ->setKey('code')
+ ->setLabel(pht('Code')),
+ id(new PhabricatorEpochExportField())
+ ->setKey('date')
+ ->setLabel(pht('Date')),
+ );
+ }
+
+ public function newExport(array $events) {
+ $viewer = $this->requireViewer();
+
+ $phids = array();
+ foreach ($events as $event) {
+ if ($event->getPullerPHID()) {
+ $phids[] = $event->getPullerPHID();
+ }
+ }
+ $handles = $viewer->loadHandles($phids);
+
+ $export = array();
+ foreach ($events as $event) {
+ $repository = $event->getRepository();
+ if ($repository) {
+ $repository_phid = $repository->getPHID();
+ $repository_name = $repository->getDisplayName();
+ } else {
+ $repository_phid = null;
+ $repository_name = null;
+ }
+
+ $puller_phid = $event->getPullerPHID();
+ if ($puller_phid) {
+ $puller_name = $handles[$puller_phid]->getName();
+ } else {
+ $puller_name = null;
+ }
+
+ $export[] = array(
+ 'id' => $event->getID(),
+ 'phid' => $event->getPHID(),
+ 'repositoryPHID' => $repository_phid,
+ 'repository' => $repository_name,
+ 'pullerPHID' => $puller_phid,
+ 'puller' => $puller_name,
+ 'protocol' => $event->getRemoteProtocol(),
+ 'result' => $event->getResultType(),
+ 'code' => $event->getResultCode(),
+ 'date' => $event->getEpoch(),
+ );
+ }
+
+ return $export;
+ }
+
protected function getURI($path) {
return '/diffusion/pulllog/'.$path;
}
diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php
--- a/src/applications/search/controller/PhabricatorApplicationSearchController.php
+++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php
@@ -66,6 +66,11 @@
public function processRequest() {
$this->validateDelegatingController();
+ $query_action = $this->getRequest()->getURIData('queryAction');
+ if ($query_action == 'export') {
+ return $this->processExportRequest();
+ }
+
$key = $this->getQueryKey();
if ($key == 'edit') {
return $this->processEditRequest();
@@ -374,6 +379,96 @@
->appendChild($body);
}
+ private function processExportRequest() {
+ $viewer = $this->getViewer();
+ $engine = $this->getSearchEngine();
+ $request = $this->getRequest();
+
+ if (!$this->canExport()) {
+ return new Aphront404Response();
+ }
+
+ $query_key = $this->getQueryKey();
+ 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();
+ if (!$saved_query) {
+ return new Aphront404Response();
+ }
+ }
+
+ $cancel_uri = $engine->getQueryResultsPageURI($query_key);
+
+ $named_query = idx($engine->loadEnabledNamedQueries(), $query_key);
+
+ if ($named_query) {
+ $filename = $named_query->getQueryName();
+ } else {
+ $filename = $engine->getResultTypeDescription();
+ }
+ $filename = phutil_utf8_strtolower($filename);
+ $filename = PhabricatorFile::normalizeFileName($filename);
+
+ if ($request->isFormPost()) {
+ $query = $engine->buildQueryFromSavedQuery($saved_query);
+
+ // 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);
+
+ $objects = $engine->executeQuery($query, $pager);
+
+ $extension = 'json';
+ $mime_type = 'application/json';
+ $filename = $filename.'.'.$extension;
+
+ $result = $engine->newExport($objects);
+ $result = id(new PhutilJSON())
+ ->encodeAsList($result);
+
+ $file = PhabricatorFile::newFromFileData(
+ $result,
+ array(
+ 'name' => $filename,
+ 'authorPHID' => $viewer->getPHID(),
+ 'ttl.relative' => phutil_units('15 minutes in seconds'),
+ 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
+ 'mime-type' => $mime_type,
+ ));
+
+ return $this->newDialog()
+ ->setTitle(pht('Download Results'))
+ ->appendParagraph(
+ pht('Click the download button to download the exported data.'))
+ ->addCancelButton($cancel_uri, pht('Done'))
+ ->setSubmitURI($file->getDownloadURI())
+ ->setDisableWorkflowOnSubmit(true)
+ ->addSubmitButton(pht('Download Results'));
+ }
+
+ $export_form = id(new AphrontFormView())
+ ->setViewer($viewer)
+ ->appendControl(
+ id(new AphrontFormSelectControl())
+ ->setName('format')
+ ->setLabel(pht('Format'))
+ ->setOptions(
+ array(
+ 'json' => 'JSON',
+ )));
+
+ return $this->newDialog()
+ ->setTitle(pht('Export Results'))
+ ->appendForm($export_form)
+ ->addCancelButton($cancel_uri)
+ ->addSubmitButton(pht('Continue'));
+ }
+
private function processEditRequest() {
$parent = $this->getDelegatingController();
$request = $this->getRequest();
@@ -720,7 +815,6 @@
$viewer);
if ($can_use && $is_installed) {
- $dashboard_uri = '/dashboard/install/';
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-dashboard')
->setName(pht('Add to Dashboard'))
@@ -728,6 +822,15 @@
->setHref("/dashboard/panel/install/{$engine_class}/{$query_key}/");
}
+ if ($this->canExport()) {
+ $export_uri = $engine->getExportURI($query_key);
+ $actions[] = id(new PhabricatorActionView())
+ ->setIcon('fa-download')
+ ->setName(pht('Export Results'))
+ ->setWorkflow(true)
+ ->setHref($export_uri);
+ }
+
if ($is_dev) {
$engine = $this->getSearchEngine();
$nux_uri = $engine->getQueryBaseURI();
@@ -753,4 +856,22 @@
return $actions;
}
+ private function canExport() {
+ $engine = $this->getSearchEngine();
+ if (!$engine->canExport()) {
+ return false;
+ }
+
+ // Don't allow logged-out users to perform exports. There's no technical
+ // or policy reason they can't, but we don't normally give them access
+ // to write files or jobs. For now, just err on the side of caution.
+
+ $viewer = $this->getViewer();
+ if (!$viewer->getPHID()) {
+ return false;
+ }
+
+ return true;
+ }
+
}
diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
--- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
+++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
@@ -413,6 +413,10 @@
return $this->getURI('');
}
+ public function getExportURI($query_key) {
+ return $this->getURI('query/'.$query_key.'/export/');
+ }
+
/**
* Return the URI to a path within the application. Used to construct default
@@ -1441,4 +1445,17 @@
return array();
}
+
+/* -( Export )------------------------------------------------------------- */
+
+
+ public function canExport() {
+ $fields = $this->newExportFields();
+ return (bool)$fields;
+ }
+
+ protected function newExportFields() {
+ return array();
+ }
+
}
diff --git a/src/infrastructure/export/PhabricatorEpochExportField.php b/src/infrastructure/export/PhabricatorEpochExportField.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/export/PhabricatorEpochExportField.php
@@ -0,0 +1,4 @@
+<?php
+
+final class PhabricatorEpochExportField
+ extends PhabricatorExportField {}
diff --git a/src/infrastructure/export/PhabricatorExportField.php b/src/infrastructure/export/PhabricatorExportField.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/export/PhabricatorExportField.php
@@ -0,0 +1,27 @@
+<?php
+
+abstract class PhabricatorExportField
+ extends Phobject {
+
+ private $key;
+ private $label;
+
+ public function setKey($key) {
+ $this->key = $key;
+ return $this;
+ }
+
+ public function getKey() {
+ return $this->key;
+ }
+
+ public function setLabel($label) {
+ $this->label = $label;
+ return $this;
+ }
+
+ public function getLabel() {
+ return $this->label;
+ }
+
+}
diff --git a/src/infrastructure/export/PhabricatorIDExportField.php b/src/infrastructure/export/PhabricatorIDExportField.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/export/PhabricatorIDExportField.php
@@ -0,0 +1,4 @@
+<?php
+
+final class PhabricatorIDExportField
+ extends PhabricatorExportField {}
diff --git a/src/infrastructure/export/PhabricatorPHIDExportField.php b/src/infrastructure/export/PhabricatorPHIDExportField.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/export/PhabricatorPHIDExportField.php
@@ -0,0 +1,4 @@
+<?php
+
+final class PhabricatorPHIDExportField
+ extends PhabricatorExportField {}
diff --git a/src/infrastructure/export/PhabricatorStringExportField.php b/src/infrastructure/export/PhabricatorStringExportField.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/export/PhabricatorStringExportField.php
@@ -0,0 +1,4 @@
+<?php
+
+final class PhabricatorStringExportField
+ extends PhabricatorExportField {}

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 30, 12:52 AM (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7726582
Default Alt Text
D18919.id45350.diff (17 KB)

Event Timeline