public function processRequest()
 {
     $drequest = $this->getDiffusionRequest();
     $request = $this->getRequest();
     $user = $request->getUser();
     if ($request->getStr('diff')) {
         return $this->buildRawDiffResponse($drequest);
     }
     $repository = $drequest->getRepository();
     $callsign = $repository->getCallsign();
     $content = array();
     $commit = id(new DiffusionCommitQuery())->setViewer($request->getUser())->withRepository($repository)->withIdentifiers(array($drequest->getCommit()))->needCommitData(true)->needAuditRequests(true)->executeOne();
     $crumbs = $this->buildCrumbs(array('commit' => true));
     if (!$commit) {
         $exists = $this->callConduitWithDiffusionRequest('diffusion.existsquery', array('commit' => $drequest->getCommit()));
         if (!$exists) {
             return new Aphront404Response();
         }
         $error = id(new AphrontErrorView())->setTitle(pht('Commit Still Parsing'))->appendChild(pht('Failed to load the commit because the commit has not been ' . 'parsed yet.'));
         return $this->buildApplicationPage(array($crumbs, $error), array('title' => pht('Commit Still Parsing'), 'device' => false));
     }
     $top_anchor = id(new PhabricatorAnchorView())->setAnchorName('top')->setNavigationMarker(true);
     $audit_requests = $commit->getAudits();
     $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
     $commit_data = $commit->getCommitData();
     $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
     $changesets = null;
     if ($is_foreign) {
         $subpath = $commit_data->getCommitDetail('svn-subpath');
         $error_panel = new AphrontErrorView();
         $error_panel->setTitle(pht('Commit Not Tracked'));
         $error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING);
         $error_panel->appendChild(pht("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('%s'), so no " . "information is available.", $subpath));
         $content[] = $error_panel;
         $content[] = $top_anchor;
     } else {
         $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
         $engine->setConfig('viewer', $user);
         require_celerity_resource('phabricator-remarkup-css');
         $parents = $this->callConduitWithDiffusionRequest('diffusion.commitparentsquery', array('commit' => $drequest->getCommit()));
         if ($parents) {
             $parents = id(new DiffusionCommitQuery())->setViewer($user)->withRepository($repository)->withIdentifiers($parents)->execute();
         }
         $headsup_view = id(new PHUIHeaderView())->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
         $headsup_actions = $this->renderHeadsupActionList($commit, $repository);
         $commit_properties = $this->loadCommitProperties($commit, $commit_data, $parents, $audit_requests);
         $property_list = id(new PHUIPropertyListView())->setHasKeyboardShortcuts(true)->setUser($user)->setObject($commit);
         foreach ($commit_properties as $key => $value) {
             $property_list->addProperty($key, $value);
         }
         $message = $commit_data->getCommitMessage();
         $revision = $commit->getCommitIdentifier();
         $message = $this->linkBugtraq($message);
         $message = $engine->markupText($message);
         $property_list->invokeWillRenderEvent();
         $property_list->setActionList($headsup_actions);
         $detail_list = new PHUIPropertyListView();
         $detail_list->addSectionHeader(pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
         $detail_list->addTextContent(phutil_tag('div', array('class' => 'diffusion-commit-message phabricator-remarkup'), $message));
         $content[] = $top_anchor;
         $object_box = id(new PHUIObjectBoxView())->setHeader($headsup_view)->addPropertyList($property_list)->addPropertyList($detail_list);
         $content[] = $object_box;
     }
     $content[] = $this->buildComments($commit);
     $hard_limit = 1000;
     if ($commit->isImported()) {
         $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
         $change_query->setLimit($hard_limit + 1);
         $changes = $change_query->loadChanges();
     } else {
         $changes = array();
     }
     $was_limited = count($changes) > $hard_limit;
     if ($was_limited) {
         $changes = array_slice($changes, 0, $hard_limit);
     }
     $content[] = $this->buildMergesTable($commit);
     $highlighted_audits = $commit->getAuthorityAudits($user, $this->auditAuthorityPHIDs);
     $owners_paths = array();
     if ($highlighted_audits) {
         $packages = id(new PhabricatorOwnersPackage())->loadAllWhere('phid IN (%Ls)', mpull($highlighted_audits, 'getAuditorPHID'));
         if ($packages) {
             $owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere('repositoryPHID = %s AND packageID IN (%Ld)', $repository->getPHID(), mpull($packages, 'getID'));
         }
     }
     $change_table = new DiffusionCommitChangeTableView();
     $change_table->setDiffusionRequest($drequest);
     $change_table->setPathChanges($changes);
     $change_table->setOwnersPaths($owners_paths);
     $count = count($changes);
     $bad_commit = null;
     if ($count == 0) {
         $bad_commit = queryfx_one(id(new PhabricatorRepository())->establishConnection('r'), 'SELECT * FROM %T WHERE fullCommitName = %s', PhabricatorRepository::TABLE_BADCOMMIT, 'r' . $callsign . $commit->getCommitIdentifier());
     }
     if ($bad_commit) {
         $content[] = $this->renderStatusMessage(pht('Bad Commit'), $bad_commit['description']);
     } else {
         if ($is_foreign) {
             // Don't render anything else.
         } else {
             if (!$commit->isImported()) {
                 $content[] = $this->renderStatusMessage(pht('Still Importing...'), pht('This commit is still importing. Changes will be visible once ' . 'the import finishes.'));
             } else {
                 if (!count($changes)) {
                     $content[] = $this->renderStatusMessage(pht('Empty Commit'), pht('This commit is empty and does not affect any paths.'));
                 } else {
                     if ($was_limited) {
                         $content[] = $this->renderStatusMessage(pht('Enormous Commit'), pht('This commit is enormous, and affects more than %d files. ' . 'Changes are not shown.', $hard_limit));
                     } else {
                         // The user has clicked "Show All Changes", and we should show all the
                         // changes inline even if there are more than the soft limit.
                         $show_all_details = $request->getBool('show_all');
                         $change_panel = new PHUIObjectBoxView();
                         $header = new PHUIHeaderView();
                         $header->setHeader('Changes (' . number_format($count) . ')');
                         $change_panel->setID('toc');
                         if ($count > self::CHANGES_LIMIT && !$show_all_details) {
                             $icon = id(new PHUIIconView())->setIconFont('fa-files-o');
                             $button = id(new PHUIButtonView())->setText(pht('Show All Changes'))->setHref('?show_all=true')->setTag('a')->setIcon($icon);
                             $warning_view = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle('Very Large Commit')->appendChild(pht('This commit is very large. Load each file individually.'));
                             $change_panel->setErrorView($warning_view);
                             $header->addActionLink($button);
                         }
                         $change_panel->appendChild($change_table);
                         $change_panel->setHeader($header);
                         $content[] = $change_panel;
                         $changesets = DiffusionPathChange::convertToDifferentialChangesets($changes);
                         $vcs = $repository->getVersionControlSystem();
                         switch ($vcs) {
                             case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                                 $vcs_supports_directory_changes = true;
                                 break;
                             case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                             case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
                                 $vcs_supports_directory_changes = false;
                                 break;
                             default:
                                 throw new Exception('Unknown VCS.');
                         }
                         $references = array();
                         foreach ($changesets as $key => $changeset) {
                             $file_type = $changeset->getFileType();
                             if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                                 if (!$vcs_supports_directory_changes) {
                                     unset($changesets[$key]);
                                     continue;
                                 }
                             }
                             $references[$key] = $drequest->generateURI(array('action' => 'rendering-ref', 'path' => $changeset->getFilename()));
                         }
                         // TODO: Some parts of the views still rely on properties of the
                         // DifferentialChangeset. Make the objects ephemeral to make sure we don't
                         // accidentally save them, and then set their ID to the appropriate ID for
                         // this application (the path IDs).
                         $path_ids = array_flip(mpull($changes, 'getPath'));
                         foreach ($changesets as $changeset) {
                             $changeset->makeEphemeral();
                             $changeset->setID($path_ids[$changeset->getFilename()]);
                         }
                         if ($count <= self::CHANGES_LIMIT || $show_all_details) {
                             $visible_changesets = $changesets;
                         } else {
                             $visible_changesets = array();
                             $inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments($user, $commit->getPHID());
                             $path_ids = mpull($inlines, null, 'getPathID');
                             foreach ($changesets as $key => $changeset) {
                                 if (array_key_exists($changeset->getID(), $path_ids)) {
                                     $visible_changesets[$key] = $changeset;
                                 }
                             }
                         }
                         $change_list_title = DiffusionView::nameCommit($repository, $commit->getCommitIdentifier());
                         $change_list = new DifferentialChangesetListView();
                         $change_list->setTitle($change_list_title);
                         $change_list->setChangesets($changesets);
                         $change_list->setVisibleChangesets($visible_changesets);
                         $change_list->setRenderingReferences($references);
                         $change_list->setRenderURI('/diffusion/' . $callsign . '/diff/');
                         $change_list->setRepository($repository);
                         $change_list->setUser($user);
                         // TODO: Try to setBranch() to something reasonable here?
                         $change_list->setStandaloneURI('/diffusion/' . $callsign . '/diff/');
                         $change_list->setRawFileURIs(null, '/diffusion/' . $callsign . '/diff/?view=r');
                         $change_list->setInlineCommentControllerURI('/diffusion/inline/edit/' . phutil_escape_uri($commit->getPHID()) . '/');
                         $change_references = array();
                         foreach ($changesets as $key => $changeset) {
                             $change_references[$changeset->getID()] = $references[$key];
                         }
                         $change_table->setRenderingReferences($change_references);
                         $content[] = $change_list->render();
                     }
                 }
             }
         }
     }
     $content[] = $this->renderAddCommentPanel($commit, $audit_requests);
     $commit_id = 'r' . $callsign . $commit->getCommitIdentifier();
     $short_name = DiffusionView::nameCommit($repository, $commit->getCommitIdentifier());
     $prefs = $user->loadPreferences();
     $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
     $pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
     $show_filetree = $prefs->getPreference($pref_filetree);
     $collapsed = $prefs->getPreference($pref_collapse);
     if ($changesets && $show_filetree) {
         $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())->setAnchorName('top')->setTitle($short_name)->setBaseURI(new PhutilURI('/' . $commit_id))->build($changesets)->setCrumbs($crumbs)->setCollapsed((bool) $collapsed)->appendChild($content);
         $content = $nav;
     } else {
         $content = array($crumbs, $content);
     }
     return $this->buildApplicationPage($content, array('title' => $commit_id, 'pageObjects' => array($commit->getPHID()), 'device' => false));
 }
 public function renderBurn()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $handle = null;
     $project_phid = $request->getStr('project');
     if ($project_phid) {
         $phids = array($project_phid);
         $handles = $this->loadViewerHandles($phids);
         $handle = $handles[$project_phid];
     }
     $table = new ManiphestTransaction();
     $conn = $table->establishConnection('r');
     $joins = '';
     if ($project_phid) {
         $joins = qsprintf($conn, 'JOIN %T t ON x.objectPHID = t.phid
       JOIN %T p ON p.src = t.phid AND p.type = %d AND p.dst = %s', id(new ManiphestTask())->getTableName(), PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, $project_phid);
     }
     $data = queryfx_all($conn, 'SELECT x.oldValue, x.newValue, x.dateCreated FROM %T x %Q
     WHERE transactionType = %s
     ORDER BY x.dateCreated ASC', $table->getTableName(), $joins, ManiphestTransaction::TYPE_STATUS);
     $stats = array();
     $day_buckets = array();
     $open_tasks = array();
     foreach ($data as $key => $row) {
         // NOTE: Hack to avoid json_decode().
         $oldv = trim($row['oldValue'], '"');
         $newv = trim($row['newValue'], '"');
         if ($oldv == 'null') {
             $old_is_open = false;
         } else {
             $old_is_open = ManiphestTaskStatus::isOpenStatus($oldv);
         }
         $new_is_open = ManiphestTaskStatus::isOpenStatus($newv);
         $is_open = $new_is_open && !$old_is_open;
         $is_close = $old_is_open && !$new_is_open;
         $data[$key]['_is_open'] = $is_open;
         $data[$key]['_is_close'] = $is_close;
         if (!$is_open && !$is_close) {
             // This is either some kind of bogus event, or a resolution change
             // (e.g., resolved -> invalid). Just skip it.
             continue;
         }
         $day_bucket = phabricator_format_local_time($row['dateCreated'], $user, 'Yz');
         $day_buckets[$day_bucket] = $row['dateCreated'];
         if (empty($stats[$day_bucket])) {
             $stats[$day_bucket] = array('open' => 0, 'close' => 0);
         }
         $stats[$day_bucket][$is_close ? 'close' : 'open']++;
     }
     $template = array('open' => 0, 'close' => 0);
     $rows = array();
     $rowc = array();
     $last_month = null;
     $last_month_epoch = null;
     $last_week = null;
     $last_week_epoch = null;
     $week = null;
     $month = null;
     $last = last_key($stats) - 1;
     $period = $template;
     foreach ($stats as $bucket => $info) {
         $epoch = $day_buckets[$bucket];
         $week_bucket = phabricator_format_local_time($epoch, $user, 'YW');
         if ($week_bucket != $last_week) {
             if ($week) {
                 $rows[] = $this->formatBurnRow('Week of ' . phabricator_date($last_week_epoch, $user), $week);
                 $rowc[] = 'week';
             }
             $week = $template;
             $last_week = $week_bucket;
             $last_week_epoch = $epoch;
         }
         $month_bucket = phabricator_format_local_time($epoch, $user, 'Ym');
         if ($month_bucket != $last_month) {
             if ($month) {
                 $rows[] = $this->formatBurnRow(phabricator_format_local_time($last_month_epoch, $user, 'F, Y'), $month);
                 $rowc[] = 'month';
             }
             $month = $template;
             $last_month = $month_bucket;
             $last_month_epoch = $epoch;
         }
         $rows[] = $this->formatBurnRow(phabricator_date($epoch, $user), $info);
         $rowc[] = null;
         $week['open'] += $info['open'];
         $week['close'] += $info['close'];
         $month['open'] += $info['open'];
         $month['close'] += $info['close'];
         $period['open'] += $info['open'];
         $period['close'] += $info['close'];
     }
     if ($week) {
         $rows[] = $this->formatBurnRow(pht('Week To Date'), $week);
         $rowc[] = 'week';
     }
     if ($month) {
         $rows[] = $this->formatBurnRow(pht('Month To Date'), $month);
         $rowc[] = 'month';
     }
     $rows[] = $this->formatBurnRow(pht('All Time'), $period);
     $rowc[] = 'aggregate';
     $rows = array_reverse($rows);
     $rowc = array_reverse($rowc);
     $table = new AphrontTableView($rows);
     $table->setRowClasses($rowc);
     $table->setHeaders(array(pht('Period'), pht('Opened'), pht('Closed'), pht('Change')));
     $table->setColumnClasses(array('right wide', 'n', 'n', 'n'));
     if ($handle) {
         $inst = pht('NOTE: This table reflects tasks currently in ' . 'the project. If a task was opened in the past but added to ' . 'the project recently, it is counted on the day it was ' . 'opened, not the day it was categorized. If a task was part ' . 'of this project in the past but no longer is, it is not ' . 'counted at all.');
         $header = pht('Task Burn Rate for Project %s', $handle->renderLink());
         $caption = phutil_tag('p', array(), $inst);
     } else {
         $header = pht('Task Burn Rate for All Tasks');
         $caption = null;
     }
     if ($caption) {
         $caption = id(new AphrontErrorView())->appendChild($caption)->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
     }
     $panel = new PHUIObjectBoxView();
     $panel->setHeaderText($header);
     if ($caption) {
         $panel->setErrorView($caption);
     }
     $panel->appendChild($table);
     $tokens = array();
     if ($handle) {
         $tokens = array($handle);
     }
     $filter = $this->renderReportFilters($tokens, $has_window = false);
     $id = celerity_generate_unique_node_id();
     $chart = phutil_tag('div', array('id' => $id, 'style' => 'border: 1px solid #BFCFDA; ' . 'background-color: #fff; ' . 'margin: 8px 16px; ' . 'height: 400px; '), '');
     list($burn_x, $burn_y) = $this->buildSeries($data);
     require_celerity_resource('raphael-core');
     require_celerity_resource('raphael-g');
     require_celerity_resource('raphael-g-line');
     Javelin::initBehavior('line-chart', array('hardpoint' => $id, 'x' => array($burn_x), 'y' => array($burn_y), 'xformat' => 'epoch', 'yformat' => 'int'));
     return array($filter, $chart, $panel);
 }