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

Add excel export to Maniphest

Summary:
Allow Maniphest result sets to be exported to Excel.

Spreadsheet_Excel_Writer is awful but comparatively easy to get working. There's
also a "PHPExcel" package but it has some autoload conflicts right now and this
seems good-enough.

Test Plan: Exported a bunch of tasks to Excel.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran, epriestley

Maniphest Tasks: T923

Differential Revision: https://secure.phabricator.com/D1721
This commit is contained in:
epriestley 2012-02-28 21:07:12 -08:00
parent 8a0a00f118
commit 280d7cd294
6 changed files with 273 additions and 23 deletions

View file

@ -410,6 +410,7 @@ phutil_register_library_map(array(
'ManiphestController' => 'applications/maniphest/controller/base',
'ManiphestDAO' => 'applications/maniphest/storage/base',
'ManiphestDefaultTaskExtensions' => 'applications/maniphest/extensions/task',
'ManiphestExportController' => 'applications/maniphest/controller/export',
'ManiphestReplyHandler' => 'applications/maniphest/replyhandler',
'ManiphestReportController' => 'applications/maniphest/controller/report',
'ManiphestTask' => 'applications/maniphest/storage/task',
@ -1210,6 +1211,7 @@ phutil_register_library_map(array(
'ManiphestController' => 'PhabricatorController',
'ManiphestDAO' => 'PhabricatorLiskDAO',
'ManiphestDefaultTaskExtensions' => 'ManiphestTaskExtensions',
'ManiphestExportController' => 'ManiphestController',
'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler',
'ManiphestReportController' => 'ManiphestController',
'ManiphestTask' => 'ManiphestDAO',

View file

@ -207,6 +207,7 @@ class AphrontDefaultApplicationConfiguration
'save/' => 'ManiphestTransactionSaveController',
'preview/(?P<id>\d+)/$' => 'ManiphestTransactionPreviewController',
),
'export/(?P<key>[^/]+)/$' => 'ManiphestExportController',
),
'/T(?P<id>\d+)$' => 'ManiphestTaskDetailController',

View file

@ -0,0 +1,201 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group maniphest
*/
final class ManiphestExportController extends ManiphestController {
private $key;
public function willProcessRequest(array $data) {
$this->key = $data['key'];
return $this;
}
/**
* @phutil-external-symbol class Spreadsheet_Excel_Writer
*/
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$ok = @include_once 'Spreadsheet/Excel/Writer.php';
if (!$ok) {
$dialog = new AphrontDialogView();
$dialog->setUser($user);
$dialog->setTitle('Excel Export Not Configured');
$dialog->appendChild(
'<p>This system does not have Spreadsheet_Excel_Writer installed. '.
'This software component is required to export tasks to Excel. Have '.
'your system administrator install it with:</p>'.
'<br />'.
'<p><code>$ sudo pear install Spreadsheet_Excel_Writer</code></p>');
$dialog->addCancelButton('/maniphest/');
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$query = id(new PhabricatorSearchQuery())->loadOneWhere(
'queryKey = %s',
$this->key);
if (!$query) {
return new Aphront404Response();
}
if (!$request->isDialogFormPost()) {
$dialog = new AphrontDialogView();
$dialog->setUser($user);
$dialog->setTitle('Export Tasks to Excel');
$dialog->appendChild(
'<p>Do you want to export the query results to Excel?</p>');
$dialog->addCancelButton('/maniphest/');
$dialog->addSubmitButton('Export to Excel');
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$query->setParameter('limit', null);
$query->setParameter('offset', null);
$query->setParameter('order', 'p');
$query->setParameter('group', 'n');
list($tasks, $handles) = ManiphestTaskListController::loadTasks($query);
// Ungroup tasks.
$tasks = array_mergev($tasks);
$all_projects = array_mergev(mpull($tasks, 'getProjectPHIDs'));
$project_handles = id(new PhabricatorObjectHandleData($all_projects))
->loadHandles();
$handles += $project_handles;
$workbook = new Spreadsheet_Excel_Writer();
$sheet = $workbook->addWorksheet('Exported Maniphest Tasks');
$date_format = $workbook->addFormat();
$date_format->setNumFormat('M/D/YYYY h:mm AM/PM');
$widths = array(
null,
20,
null,
15,
20,
20,
75,
40,
30,
400,
);
foreach ($widths as $col => $width) {
if ($width !== null) {
$sheet->setColumn($col, $col, $width);
}
}
$status_map = ManiphestTaskStatus::getTaskStatusMap();
$pri_map = ManiphestTaskPriority::getTaskPriorityMap();
$rows = array();
$rows[] = array(
'ID',
'Owner',
'Status',
'Priority',
'Date Created',
'Date Updated',
'Title',
'Projects',
'URI',
'Description',
);
$formats = array(
null,
null,
null,
null,
$date_format,
$date_format,
null,
null,
null,
null,
);
$header_format = $workbook->addFormat();
$header_format->setBold();
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()),
$task->getDescription(),
);
}
foreach ($rows as $row => $cols) {
foreach ($cols as $col => $spec) {
if ($row == 0) {
$fmt = $header_format;
} else {
$fmt = $formats[$col];
}
$sheet->write($row, $col, $spec, $fmt);
}
}
ob_start();
$workbook->close();
$data = ob_get_clean();
return id(new AphrontFileResponse())
->setMimeType('application/vnd.ms-excel')
->setDownload('maniphest_tasks_'.date('Ymd').'.xls')
->setContent($data);
}
private function computeExcelDate($epoch) {
$seconds_per_day = (60 * 60 * 24);
$offset = ($seconds_per_day * 25569);
return ($epoch + $offset) / $seconds_per_day;
}
}

View file

@ -0,0 +1,24 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/dialog');
phutil_require_module('phabricator', 'aphront/response/file');
phutil_require_module('phabricator', 'applications/maniphest/constants/priority');
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
phutil_require_module('phabricator', 'applications/maniphest/controller/tasklist');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/search/storage/query');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'view/dialog');
phutil_require_module('phutil', 'utils');
phutil_require_source('ManiphestExportController.php');

View file

@ -100,18 +100,27 @@ class ManiphestTaskListController extends ManiphestController {
$page = $request->getInt('page');
$page_size = self::DEFAULT_PAGE_SIZE;
list($tasks, $handles, $total_count) = $this->loadTasks(
$user_phids,
$project_phids,
$task_ids,
$query = new PhabricatorSearchQuery();
$query->setQuery('<<maniphest>>');
$query->setParameters(
array(
'status' => $status_map,
'group' => $grouping,
'order' => $order,
'offset' => $page,
'limit' => $page_size,
'view' => $this->view,
'userPHIDs' => $user_phids,
'projectPHIDs' => $project_phids,
'taskIDs' => $task_ids,
'group' => $grouping,
'order' => $order,
'offset' => $page,
'limit' => $page_size,
'status' => $status_map,
));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$query->save();
unset($unguarded);
list($tasks, $handles, $total_count) = self::loadTasks($query);
$form = id(new AphrontFormView())
->setUser($user)
->setAction($request->getRequestURI());
@ -231,7 +240,7 @@ class ManiphestTaskListController extends ManiphestController {
}
$selector->appendChild($this->renderBatchEditor());
$selector->appendChild($this->renderBatchEditor($query));
$selector = phabricator_render_form(
$user,
@ -252,17 +261,17 @@ class ManiphestTaskListController extends ManiphestController {
));
}
private function loadTasks(
array $user_phids,
array $project_phids,
array $task_ids,
array $dict) {
public static function loadTasks(PhabricatorSearchQuery $search_query) {
$user_phids = $search_query->getParameter('userPHIDs', array());
$project_phids = $search_query->getParameter('projectPHIDs', array());
$task_ids = $search_query->getParameter('taskIDs', array());
$query = new ManiphestTaskQuery();
$query->withProjects($project_phids);
$query->withTaskIDs($task_ids);
$status = $dict['status'];
$status = $search_query->getParameter('status', 'all');
if (!empty($status['open']) && !empty($status['closed'])) {
$query->withStatus(ManiphestTaskQuery::STATUS_ANY);
} else if (!empty($status['open'])) {
@ -271,7 +280,7 @@ class ManiphestTaskListController extends ManiphestController {
$query->withStatus(ManiphestTaskQuery::STATUS_CLOSED);
}
switch ($this->view) {
switch ($search_query->getParameter('view')) {
case 'action':
$query->withOwners($user_phids);
break;
@ -299,7 +308,7 @@ class ManiphestTaskListController extends ManiphestController {
$query->setOrderBy(
idx(
$order_map,
$dict['order'],
$search_query->getParameter('order'),
ManiphestTaskQuery::ORDER_MODIFIED));
$group_map = array(
@ -310,12 +319,12 @@ class ManiphestTaskListController extends ManiphestController {
$query->setGroupBy(
idx(
$group_map,
$dict['group'],
$search_query->getParameter('group'),
ManiphestTaskQuery::GROUP_NONE));
$query->setCalculateRows(true);
$query->setLimit($dict['limit']);
$query->setOffset($dict['offset']);
$query->setLimit($search_query->getParameter('limit'));
$query->setOffset($search_query->getParameter('offset'));
$data = $query->execute();
$total_row_count = $query->getRowCount();
@ -325,7 +334,7 @@ class ManiphestTaskListController extends ManiphestController {
$handles = id(new PhabricatorObjectHandleData($handle_phids))
->loadHandles();
switch ($dict['group']) {
switch ($search_query->getParameter('group')) {
case 'priority':
$data = mgroup($data, 'getPriority');
krsort($data);
@ -471,7 +480,7 @@ class ManiphestTaskListController extends ManiphestController {
return array($group_by, $group_control);
}
private function renderBatchEditor() {
private function renderBatchEditor(PhabricatorSearchQuery $search_query) {
Javelin::initBehavior(
'maniphest-batch-selector',
array(
@ -510,6 +519,14 @@ class ManiphestTaskListController extends ManiphestController {
),
'Batch Edit Selected Tasks &raquo;');
$export = javelin_render_tag(
'a',
array(
'href' => '/maniphest/export/'.$search_query->getQueryKey().'/',
'class' => 'grey button',
),
'Export Tasks to Excel...');
return
'<div class="maniphest-batch-editor">'.
'<div class="batch-editor-header">Batch Task Editor</div>'.
@ -519,6 +536,9 @@ class ManiphestTaskListController extends ManiphestController {
$select_all.
$select_none.
'</td>'.
'<td>'.
$export.
'</td>'.
'<td id="batch-select-status-cell">'.
'0 Selected Tasks'.
'</td>'.

View file

@ -7,12 +7,14 @@
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'aphront/writeguard');
phutil_require_module('phabricator', 'applications/maniphest/constants/priority');
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
phutil_require_module('phabricator', 'applications/maniphest/query');
phutil_require_module('phabricator', 'applications/maniphest/view/tasklist');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/search/storage/query');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');