public function processRequest()
 {
     $drequest = $this->getDiffusionRequest();
     $request = $this->getRequest();
     $user = $request->getUser();
     if (!$request->isAjax()) {
         // This request came out of the dropdown menu, either "View Standalone"
         // or "View Raw File".
         $view = $request->getStr('view');
         if ($view == 'r') {
             $uri = $drequest->generateURI(array('action' => 'browse', 'params' => array('view' => 'raw')));
         } else {
             $uri = $drequest->generateURI(array('action' => 'change'));
         }
         return id(new AphrontRedirectResponse())->setURI($uri);
     }
     $diff_query = DiffusionDiffQuery::newFromDiffusionRequest($drequest);
     $changeset = $diff_query->loadChangeset();
     if (!$changeset) {
         return new Aphront404Response();
     }
     $parser = new DifferentialChangesetParser();
     $parser->setUser($user);
     $parser->setChangeset($changeset);
     $parser->setRenderingReference($diff_query->getRenderingReference());
     $parser->setMarkupEngine(PhabricatorMarkupEngine::newDiffusionMarkupEngine());
     $pquery = new DiffusionPathIDQuery(array($changeset->getFilename()));
     $ids = $pquery->loadPathIDs();
     $path_id = $ids[$changeset->getFilename()];
     $parser->setLeftSideCommentMapping($path_id, false);
     $parser->setRightSideCommentMapping($path_id, true);
     $parser->setWhitespaceMode(DifferentialChangesetParser::WHITESPACE_SHOW_ALL);
     $inlines = id(new PhabricatorAuditInlineComment())->loadAllWhere('commitPHID = %s AND pathID = %d AND
     (authorPHID = %s OR auditCommentID IS NOT NULL)', $drequest->loadCommit()->getPHID(), $path_id, $user->getPHID());
     if ($inlines) {
         foreach ($inlines as $inline) {
             $parser->parseInlineComment($inline);
         }
         $phids = mpull($inlines, 'getAuthorPHID');
         $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
         $parser->setHandles($handles);
     }
     $spec = $request->getStr('range');
     list($range_s, $range_e, $mask) = DifferentialChangesetParser::parseRangeSpecification($spec);
     $output = $parser->render($range_s, $range_e, $mask);
     return id(new PhabricatorChangesetResponse())->setRenderedChangeset($output);
 }
 protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commit = $drequest->getCommit();
     $subpath = $repository->getDetail('svn-subpath');
     if ($subpath && strncmp($subpath, $path, strlen($subpath))) {
         // If we have a subpath and the path isn't a child of it, it (almost
         // certainly) won't exist since we don't track commits which affect
         // it. (Even if it exists, return a consistent result.)
         $this->reason = self::REASON_IS_UNTRACKED_PARENT;
         return array();
     }
     $conn_r = $repository->establishConnection('r');
     $parent_path = DiffusionPathIDQuery::getParentPath($path);
     $path_query = new DiffusionPathIDQuery(array($path, $parent_path));
     $path_map = $path_query->loadPathIDs();
     $path_id = $path_map[$path];
     $parent_path_id = $path_map[$parent_path];
     if (empty($path_id)) {
         $this->reason = self::REASON_IS_NONEXISTENT;
         return array();
     }
     if ($commit) {
         $slice_clause = 'AND svnCommit <= ' . (int) $commit;
     } else {
         $slice_clause = '';
     }
     $index = queryfx_all($conn_r, 'SELECT pathID, max(svnCommit) maxCommit FROM %T WHERE
     repositoryID = %d AND parentID = %d
     %Q GROUP BY pathID', PhabricatorRepository::TABLE_FILESYSTEM, $repository->getID(), $path_id, $slice_clause);
     if (!$index) {
         if ($path == '/') {
             $this->reason = self::REASON_IS_EMPTY;
         } else {
             // NOTE: The parent path ID is included so this query can take
             // advantage of the table's primary key; it is uniquely determined by
             // the pathID but if we don't do the lookup ourselves MySQL doesn't have
             // the information it needs to avoid a table scan.
             $reasons = queryfx_all($conn_r, 'SELECT * FROM %T WHERE repositoryID = %d
           AND parentID = %d
           AND pathID = %d
         %Q ORDER BY svnCommit DESC LIMIT 2', PhabricatorRepository::TABLE_FILESYSTEM, $repository->getID(), $parent_path_id, $path_id, $slice_clause);
             $reason = reset($reasons);
             if (!$reason) {
                 $this->reason = self::REASON_IS_NONEXISTENT;
             } else {
                 $file_type = $reason['fileType'];
                 if (empty($reason['existed'])) {
                     $this->reason = self::REASON_IS_DELETED;
                     $this->deletedAtCommit = $reason['svnCommit'];
                     if (!empty($reasons[1])) {
                         $this->existedAtCommit = $reasons[1]['svnCommit'];
                     }
                 } else {
                     if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                         $this->reason = self::REASON_IS_EMPTY;
                     } else {
                         $this->reason = self::REASON_IS_FILE;
                     }
                 }
             }
         }
         return array();
     }
     if ($this->shouldOnlyTestValidity()) {
         return true;
     }
     $sql = array();
     foreach ($index as $row) {
         $sql[] = '(' . (int) $row['pathID'] . ', ' . (int) $row['maxCommit'] . ')';
     }
     $browse = queryfx_all($conn_r, 'SELECT *, p.path pathName
     FROM %T f JOIN %T p ON f.pathID = p.id
     WHERE repositoryID = %d
       AND parentID = %d
       AND existed = 1
     AND (pathID, svnCommit) in (%Q)
     ORDER BY pathName', PhabricatorRepository::TABLE_FILESYSTEM, PhabricatorRepository::TABLE_PATH, $repository->getID(), $path_id, implode(', ', $sql));
     $loadable_commits = array();
     foreach ($browse as $key => $file) {
         // We need to strip out directories because we don't store last-modified
         // in the filesystem table.
         if ($file['fileType'] != DifferentialChangeType::FILE_DIRECTORY) {
             $loadable_commits[] = $file['svnCommit'];
             $browse[$key]['hasCommit'] = true;
         }
     }
     $commits = array();
     $commit_data = array();
     if ($loadable_commits) {
         // NOTE: Even though these are integers, use '%Ls' because MySQL doesn't
         // use the second part of the key otherwise!
         $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere('repositoryID = %d AND commitIdentifier IN (%Ls)', $repository->getID(), $loadable_commits);
         $commits = mpull($commits, null, 'getCommitIdentifier');
         if ($commits) {
             $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere('commitID in (%Ld)', mpull($commits, 'getID'));
             $commit_data = mpull($commit_data, null, 'getCommitID');
         } else {
             $commit_data = array();
         }
     }
     $path_normal = DiffusionPathIDQuery::normalizePath($path);
     $results = array();
     foreach ($browse as $file) {
         $full_path = $file['pathName'];
         $file_path = ltrim(substr($full_path, strlen($path_normal)), '/');
         $full_path = ltrim($full_path, '/');
         $result = new DiffusionRepositoryPath();
         $result->setPath($file_path);
         $result->setFullPath($full_path);
         //      $result->setHash($hash);
         $result->setFileType($file['fileType']);
         //      $result->setFileSize($size);
         if (!empty($file['hasCommit'])) {
             $commit = idx($commits, $file['svnCommit']);
             if ($commit) {
                 $data = idx($commit_data, $commit->getID());
                 $result->setLastModifiedCommit($commit);
                 $result->setLastCommitData($data);
             }
         }
         $results[] = $result;
     }
     if (empty($results)) {
         $this->reason = self::REASON_IS_EMPTY;
     }
     return $results;
 }
 public function processRequest()
 {
     $drequest = $this->getDiffusionRequest();
     $request = $this->getRequest();
     $user = $request->getUser();
     $callsign = $drequest->getRepository()->getCallsign();
     $content = array();
     $content[] = $this->buildCrumbs(array('commit' => true));
     $repository = $drequest->getRepository();
     $commit = $drequest->loadCommit();
     if (!$commit) {
         // TODO: Make more user-friendly.
         throw new Exception('This commit has not parsed yet.');
     }
     $commit_data = $drequest->loadCommitData();
     $commit->attachCommitData($commit_data);
     $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
     if ($is_foreign) {
         $subpath = $commit_data->getCommitDetail('svn-subpath');
         $error_panel = new AphrontErrorView();
         $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE);
         $error_panel->setTitle('Commit Not Tracked');
         $error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING);
         $error_panel->appendChild("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('" . phutil_escape_html($subpath) . "'), so no information is available.");
         $content[] = $error_panel;
     } else {
         $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
         require_celerity_resource('diffusion-commit-view-css');
         require_celerity_resource('phabricator-remarkup-css');
         $parent_query = DiffusionCommitParentsQuery::newFromDiffusionRequest($drequest);
         $headsup_panel = new AphrontHeadsupView();
         $headsup_panel->setHeader('Commit Detail');
         $headsup_panel->setActionList($this->renderHeadsupActionList($commit));
         $headsup_panel->setProperties($this->getCommitProperties($commit, $commit_data, $parent_query->loadParents()));
         $headsup_panel->appendChild('<div class="diffusion-commit-message phabricator-remarkup">' . $engine->markupText($commit_data->getCommitMessage()) . '</div>');
         $content[] = $headsup_panel;
     }
     $query = new PhabricatorAuditQuery();
     $query->withCommitPHIDs(array($commit->getPHID()));
     $audit_requests = $query->execute();
     $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
     $content[] = $this->buildAuditTable($commit, $audit_requests);
     $content[] = $this->buildComments($commit);
     $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
     $changes = $change_query->loadChanges();
     $content[] = $this->buildMergesTable($commit);
     $original_changes_count = count($changes);
     if ($request->getStr('show_all') !== 'true' && $original_changes_count > self::CHANGES_LIMIT) {
         $changes = array_slice($changes, 0, self::CHANGES_LIMIT);
     }
     $change_table = new DiffusionCommitChangeTableView();
     $change_table->setDiffusionRequest($drequest);
     $change_table->setPathChanges($changes);
     $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) {
         $error_panel = new AphrontErrorView();
         $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE);
         $error_panel->setTitle('Bad Commit');
         $error_panel->appendChild(phutil_escape_html($bad_commit['description']));
         $content[] = $error_panel;
     } else {
         if ($is_foreign) {
             // Don't render anything else.
         } else {
             if (!count($changes)) {
                 $no_changes = new AphrontErrorView();
                 $no_changes->setWidth(AphrontErrorView::WIDTH_WIDE);
                 $no_changes->setSeverity(AphrontErrorView::SEVERITY_WARNING);
                 $no_changes->setTitle('Not Yet Parsed');
                 // TODO: This can also happen with weird SVN changes that don't do
                 // anything (or only alter properties?), although the real no-changes case
                 // is extremely rare and might be impossible to produce organically. We
                 // should probably write some kind of "Nothing Happened!" change into the
                 // DB once we parse these changes so we can distinguish between
                 // "not parsed yet" and "no changes".
                 $no_changes->appendChild("This commit hasn't been fully parsed yet (or doesn't affect any " . "paths).");
                 $content[] = $no_changes;
             } else {
                 $change_panel = new AphrontPanelView();
                 $change_panel->setHeader("Changes (" . number_format($count) . ")");
                 if ($count !== $original_changes_count) {
                     $show_all_button = phutil_render_tag('a', array('class' => 'button green', 'href' => '?show_all=true'), phutil_escape_html('Show All Changes'));
                     $warning_view = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle(sprintf("Showing only the first %d changes out of %s!", self::CHANGES_LIMIT, number_format($original_changes_count)));
                     $change_panel->appendChild($warning_view);
                     $change_panel->addButton($show_all_button);
                 }
                 $change_panel->appendChild($change_table);
                 $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).
                 $pquery = new DiffusionPathIDQuery(mpull($changesets, 'getFilename'));
                 $path_ids = $pquery->loadPathIDs();
                 foreach ($changesets as $changeset) {
                     $changeset->makeEphemeral();
                     $changeset->setID($path_ids[$changeset->getFilename()]);
                 }
                 $change_list = new DifferentialChangesetListView();
                 $change_list->setChangesets($changesets);
                 $change_list->setRenderingReferences($references);
                 $change_list->setRenderURI('/diffusion/' . $callsign . '/diff/');
                 $change_list->setRepository($repository);
                 $change_list->setUser($user);
                 $change_list->setStandaloneURI('/diffusion/' . $callsign . '/diff/');
                 $change_list->setRawFileURIs(null, '/diffusion/' . $callsign . '/diff/?view=r');
                 $change_list->setInlineCommentControllerURI('/diffusion/inline/' . phutil_escape_uri($commit->getPHID()) . '/');
                 // TODO: This is pretty awkward, unify the CSS between Diffusion and
                 // Differential better.
                 require_celerity_resource('differential-core-view-css');
                 $change_list = '<div class="differential-primary-pane">' . $change_list->render() . '</div>';
                 $content[] = $change_list;
             }
         }
     }
     $content[] = $this->buildAddCommentView($commit, $audit_requests);
     return $this->buildStandardPageResponse($content, array('title' => 'r' . $callsign . $commit->getCommitIdentifier()));
 }
 public function handleRequest(AphrontRequest $request)
 {
     $response = $this->loadDiffusionContext();
     if ($response) {
         return $response;
     }
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     if (!$request->isAjax()) {
         // This request came out of the dropdown menu, either "View Standalone"
         // or "View Raw File".
         $view = $request->getStr('view');
         if ($view == 'r') {
             $uri = $drequest->generateURI(array('action' => 'browse', 'params' => array('view' => 'raw')));
         } else {
             $uri = $drequest->generateURI(array('action' => 'change'));
         }
         return id(new AphrontRedirectResponse())->setURI($uri);
     }
     $data = $this->callConduitWithDiffusionRequest('diffusion.diffquery', array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath()));
     $drequest->updateSymbolicCommit($data['effectiveCommit']);
     $raw_changes = ArcanistDiffChange::newFromConduit($data['changes']);
     $diff = DifferentialDiff::newEphemeralFromRawChanges($raw_changes);
     $changesets = $diff->getChangesets();
     $changeset = reset($changesets);
     if (!$changeset) {
         return new Aphront404Response();
     }
     $parser = new DifferentialChangesetParser();
     $parser->setUser($viewer);
     $parser->setChangeset($changeset);
     $parser->setRenderingReference($drequest->generateURI(array('action' => 'rendering-ref')));
     $parser->readParametersFromRequest($request);
     $coverage = $drequest->loadCoverage();
     if ($coverage) {
         $parser->setCoverage($coverage);
     }
     $commit = $drequest->loadCommit();
     $pquery = new DiffusionPathIDQuery(array($changeset->getFilename()));
     $ids = $pquery->loadPathIDs();
     $path_id = $ids[$changeset->getFilename()];
     $parser->setLeftSideCommentMapping($path_id, false);
     $parser->setRightSideCommentMapping($path_id, true);
     $parser->setCanMarkDone($commit->getAuthorPHID() && $viewer->getPHID() == $commit->getAuthorPHID());
     $parser->setObjectOwnerPHID($commit->getAuthorPHID());
     $parser->setWhitespaceMode(DifferentialChangesetParser::WHITESPACE_SHOW_ALL);
     $inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments($viewer, $commit->getPHID(), $path_id);
     if ($inlines) {
         foreach ($inlines as $inline) {
             $parser->parseInlineComment($inline);
         }
         $phids = mpull($inlines, 'getAuthorPHID');
         $handles = $this->loadViewerHandles($phids);
         $parser->setHandles($handles);
     }
     $engine = new PhabricatorMarkupEngine();
     $engine->setViewer($viewer);
     foreach ($inlines as $inline) {
         $engine->addObject($inline, PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
     }
     $engine->process();
     $parser->setMarkupEngine($engine);
     $spec = $request->getStr('range');
     list($range_s, $range_e, $mask) = DifferentialChangesetParser::parseRangeSpecification($spec);
     $parser->setRange($range_s, $range_e);
     $parser->setMask($mask);
     return id(new PhabricatorChangesetResponse())->setRenderedChangeset($parser->renderChangeset())->setUndoTemplates($parser->getRenderer()->renderUndoTemplates());
 }