 3da38c74da
			
		
	
	3da38c74da
	
	
	
		
			
			Summary: Clean up the error view styling.
Test Plan:
Tested as many as I could find, built additional tests in UIExamples
{F280452}
{F280453}
Reviewers: btrahan, epriestley
Reviewed By: epriestley
Subscribers: hach-que, Korvin, epriestley
Differential Revision: https://secure.phabricator.com/D11605
		
	
		
			
				
	
	
		
			913 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			913 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| final class DifferentialRevisionViewController extends DifferentialController {
 | |
| 
 | |
|   private $revisionID;
 | |
| 
 | |
|   public function shouldAllowPublic() {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   public function willProcessRequest(array $data) {
 | |
|     $this->revisionID = $data['id'];
 | |
|   }
 | |
| 
 | |
|   public function processRequest() {
 | |
| 
 | |
|     $request = $this->getRequest();
 | |
|     $user = $request->getUser();
 | |
|     $viewer_is_anonymous = !$user->isLoggedIn();
 | |
| 
 | |
|     $revision = id(new DifferentialRevisionQuery())
 | |
|       ->withIDs(array($this->revisionID))
 | |
|       ->setViewer($request->getUser())
 | |
|       ->needRelationships(true)
 | |
|       ->needReviewerStatus(true)
 | |
|       ->needReviewerAuthority(true)
 | |
|       ->executeOne();
 | |
| 
 | |
|     if (!$revision) {
 | |
|       return new Aphront404Response();
 | |
|     }
 | |
| 
 | |
|     $diffs = id(new DifferentialDiffQuery())
 | |
|       ->setViewer($request->getUser())
 | |
|       ->withRevisionIDs(array($this->revisionID))
 | |
|       ->needArcanistProjects(true)
 | |
|       ->execute();
 | |
|     $diffs = array_reverse($diffs, $preserve_keys = true);
 | |
| 
 | |
|     if (!$diffs) {
 | |
|       throw new Exception(
 | |
|         'This revision has no diffs. Something has gone quite wrong.');
 | |
|     }
 | |
| 
 | |
|     $revision->attachActiveDiff(last($diffs));
 | |
| 
 | |
|     $diff_vs = $request->getInt('vs');
 | |
| 
 | |
|     $target_id = $request->getInt('id');
 | |
|     $target = idx($diffs, $target_id, end($diffs));
 | |
| 
 | |
|     $target_manual = $target;
 | |
|     if (!$target_id) {
 | |
|       foreach ($diffs as $diff) {
 | |
|         if ($diff->getCreationMethod() != 'commit') {
 | |
|           $target_manual = $diff;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (empty($diffs[$diff_vs])) {
 | |
|       $diff_vs = null;
 | |
|     }
 | |
| 
 | |
|     $repository = null;
 | |
|     $repository_phid = $target->getRepositoryPHID();
 | |
|     if ($repository_phid) {
 | |
|       if ($repository_phid == $revision->getRepositoryPHID()) {
 | |
|         $repository = $revision->getRepository();
 | |
|       } else {
 | |
|         $repository = id(new PhabricatorRepositoryQuery())
 | |
|           ->setViewer($user)
 | |
|           ->withPHIDs(array($repository_phid))
 | |
|           ->executeOne();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     list($changesets, $vs_map, $vs_changesets, $rendering_references) =
 | |
|       $this->loadChangesetsAndVsMap(
 | |
|         $target,
 | |
|         idx($diffs, $diff_vs),
 | |
|         $repository);
 | |
| 
 | |
|     if ($request->getExists('download')) {
 | |
|       return $this->buildRawDiffResponse(
 | |
|         $revision,
 | |
|         $changesets,
 | |
|         $vs_changesets,
 | |
|         $vs_map,
 | |
|         $repository);
 | |
|     }
 | |
| 
 | |
|     $props = id(new DifferentialDiffProperty())->loadAllWhere(
 | |
|       'diffID = %d',
 | |
|       $target_manual->getID());
 | |
|     $props = mpull($props, 'getData', 'getName');
 | |
| 
 | |
|     $all_changesets = $changesets;
 | |
|     $inlines = $revision->loadInlineComments($all_changesets);
 | |
| 
 | |
|     $object_phids = array_merge(
 | |
|       $revision->getReviewers(),
 | |
|       $revision->getCCPHIDs(),
 | |
|       $revision->loadCommitPHIDs(),
 | |
|       array(
 | |
|         $revision->getAuthorPHID(),
 | |
|         $user->getPHID(),
 | |
|       ));
 | |
| 
 | |
|     foreach ($revision->getAttached() as $type => $phids) {
 | |
|       foreach ($phids as $phid => $info) {
 | |
|         $object_phids[] = $phid;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $field_list = PhabricatorCustomField::getObjectFields(
 | |
|       $revision,
 | |
|       PhabricatorCustomField::ROLE_VIEW);
 | |
| 
 | |
|     $field_list->setViewer($user);
 | |
|     $field_list->readFieldsFromStorage($revision);
 | |
| 
 | |
|     $warning_handle_map = array();
 | |
|     foreach ($field_list->getFields() as $key => $field) {
 | |
|       $req = $field->getRequiredHandlePHIDsForRevisionHeaderWarnings();
 | |
|       foreach ($req as $phid) {
 | |
|         $warning_handle_map[$key][] = $phid;
 | |
|         $object_phids[] = $phid;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $handles = $this->loadViewerHandles($object_phids);
 | |
| 
 | |
|     $request_uri = $request->getRequestURI();
 | |
| 
 | |
|     $limit = 100;
 | |
|     $large = $request->getStr('large');
 | |
|     if (count($changesets) > $limit && !$large) {
 | |
|       $count = count($changesets);
 | |
|       $warning = new PHUIErrorView();
 | |
|       $warning->setTitle('Very Large Diff');
 | |
|       $warning->setSeverity(PHUIErrorView::SEVERITY_WARNING);
 | |
|       $warning->appendChild(hsprintf(
 | |
|         '%s <strong>%s</strong>',
 | |
|         pht(
 | |
|           'This diff is very large and affects %s files. '.
 | |
|           'You may load each file individually or ',
 | |
|           new PhutilNumber($count)),
 | |
|         phutil_tag(
 | |
|           'a',
 | |
|           array(
 | |
|             'class' => 'button grey',
 | |
|             'href' => $request_uri
 | |
|               ->alter('large', 'true')
 | |
|               ->setFragment('toc'),
 | |
|           ),
 | |
|           pht('Show All Files Inline'))));
 | |
|       $warning = $warning->render();
 | |
| 
 | |
|       $my_inlines = id(new DifferentialInlineCommentQuery())
 | |
|         ->withDraftComments($user->getPHID(), $this->revisionID)
 | |
|         ->execute();
 | |
| 
 | |
|       $visible_changesets = array();
 | |
|       foreach ($inlines + $my_inlines as $inline) {
 | |
|         $changeset_id = $inline->getChangesetID();
 | |
|         if (isset($changesets[$changeset_id])) {
 | |
|           $visible_changesets[$changeset_id] = $changesets[$changeset_id];
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (!empty($props['arc:lint'])) {
 | |
|         $changeset_paths = mpull($changesets, null, 'getFilename');
 | |
|         foreach ($props['arc:lint'] as $lint) {
 | |
|           $changeset = idx($changeset_paths, $lint['path']);
 | |
|           if ($changeset) {
 | |
|             $visible_changesets[$changeset->getID()] = $changeset;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       $warning = null;
 | |
|       $visible_changesets = $changesets;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     // TODO: This should be in a DiffQuery or similar.
 | |
|     $need_props = array();
 | |
|     foreach ($field_list->getFields() as $field) {
 | |
|       foreach ($field->getRequiredDiffPropertiesForRevisionView() as $prop) {
 | |
|         $need_props[$prop] = $prop;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if ($need_props) {
 | |
|       $prop_diff = $revision->getActiveDiff();
 | |
|       $load_props = id(new DifferentialDiffProperty())->loadAllWhere(
 | |
|         'diffID = %d AND name IN (%Ls)',
 | |
|         $prop_diff->getID(),
 | |
|         $need_props);
 | |
|       $load_props = mpull($load_props, 'getData', 'getName');
 | |
|       foreach ($need_props as $need) {
 | |
|         $prop_diff->attachProperty($need, idx($load_props, $need));
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $commit_hashes = mpull($diffs, 'getSourceControlBaseRevision');
 | |
|     $local_commits = idx($props, 'local:commits', array());
 | |
|     foreach ($local_commits as $local_commit) {
 | |
|       $commit_hashes[] = idx($local_commit, 'tree');
 | |
|       $commit_hashes[] = idx($local_commit, 'local');
 | |
|     }
 | |
|     $commit_hashes = array_unique(array_filter($commit_hashes));
 | |
|     if ($commit_hashes) {
 | |
|       $commits_for_links = id(new DiffusionCommitQuery())
 | |
|         ->setViewer($user)
 | |
|         ->withIdentifiers($commit_hashes)
 | |
|         ->execute();
 | |
|       $commits_for_links = mpull(
 | |
|         $commits_for_links,
 | |
|         null,
 | |
|         'getCommitIdentifier');
 | |
|     } else {
 | |
|       $commits_for_links = array();
 | |
|     }
 | |
| 
 | |
|     $revision_detail = id(new DifferentialRevisionDetailView())
 | |
|       ->setUser($user)
 | |
|       ->setRevision($revision)
 | |
|       ->setDiff(end($diffs))
 | |
|       ->setCustomFields($field_list)
 | |
|       ->setURI($request->getRequestURI());
 | |
| 
 | |
|     $actions = $this->getRevisionActions($revision);
 | |
| 
 | |
|     $whitespace = $request->getStr(
 | |
|       'whitespace',
 | |
|       DifferentialChangesetParser::WHITESPACE_IGNORE_ALL);
 | |
| 
 | |
|     $arc_project = $target->getArcanistProject();
 | |
|     if ($arc_project) {
 | |
|       list($symbol_indexes, $project_phids) = $this->buildSymbolIndexes(
 | |
|         $arc_project,
 | |
|         $visible_changesets);
 | |
|     } else {
 | |
|       $symbol_indexes = array();
 | |
|       $project_phids = null;
 | |
|     }
 | |
| 
 | |
|     $revision_detail->setActions($actions);
 | |
|     $revision_detail->setUser($user);
 | |
| 
 | |
|     $revision_detail_box = $revision_detail->render();
 | |
| 
 | |
|     $revision_warnings = $this->buildRevisionWarnings(
 | |
|       $revision,
 | |
|       $field_list,
 | |
|       $warning_handle_map,
 | |
|       $handles);
 | |
|     if ($revision_warnings) {
 | |
|       $revision_warnings = id(new PHUIErrorView())
 | |
|         ->setSeverity(PHUIErrorView::SEVERITY_WARNING)
 | |
|         ->setErrors($revision_warnings);
 | |
|       $revision_detail_box->setErrorView($revision_warnings);
 | |
|     }
 | |
| 
 | |
|     $comment_view = $this->buildTransactions(
 | |
|       $revision,
 | |
|       $diff_vs ? $diffs[$diff_vs] : $target,
 | |
|       $target,
 | |
|       $all_changesets);
 | |
| 
 | |
|     if (!$viewer_is_anonymous) {
 | |
|       $comment_view->setQuoteRef('D'.$revision->getID());
 | |
|       $comment_view->setQuoteTargetID('comment-content');
 | |
|     }
 | |
| 
 | |
|     $wrap_id = celerity_generate_unique_node_id();
 | |
|     $comment_view = phutil_tag(
 | |
|       'div',
 | |
|       array(
 | |
|         'id' => $wrap_id,
 | |
|       ),
 | |
|       $comment_view);
 | |
| 
 | |
|     if ($arc_project) {
 | |
|       Javelin::initBehavior(
 | |
|         'repository-crossreference',
 | |
|         array(
 | |
|           'section' => $wrap_id,
 | |
|           'projects' => $project_phids,
 | |
|         ));
 | |
|     }
 | |
| 
 | |
|     $changeset_view = new DifferentialChangesetListView();
 | |
|     $changeset_view->setChangesets($changesets);
 | |
|     $changeset_view->setVisibleChangesets($visible_changesets);
 | |
| 
 | |
|     if (!$viewer_is_anonymous) {
 | |
|       $changeset_view->setInlineCommentControllerURI(
 | |
|         '/differential/comment/inline/edit/'.$revision->getID().'/');
 | |
|     }
 | |
| 
 | |
|     $changeset_view->setStandaloneURI('/differential/changeset/');
 | |
|     $changeset_view->setRawFileURIs(
 | |
|       '/differential/changeset/?view=old',
 | |
|       '/differential/changeset/?view=new');
 | |
| 
 | |
|     $changeset_view->setUser($user);
 | |
|     $changeset_view->setDiff($target);
 | |
|     $changeset_view->setRenderingReferences($rendering_references);
 | |
|     $changeset_view->setVsMap($vs_map);
 | |
|     $changeset_view->setWhitespace($whitespace);
 | |
|     if ($repository) {
 | |
|       $changeset_view->setRepository($repository);
 | |
|     }
 | |
|     $changeset_view->setSymbolIndexes($symbol_indexes);
 | |
|     $changeset_view->setTitle('Diff '.$target->getID());
 | |
| 
 | |
|     $diff_history = id(new DifferentialRevisionUpdateHistoryView())
 | |
|       ->setUser($user)
 | |
|       ->setDiffs($diffs)
 | |
|       ->setSelectedVersusDiffID($diff_vs)
 | |
|       ->setSelectedDiffID($target->getID())
 | |
|       ->setSelectedWhitespace($whitespace)
 | |
|       ->setCommitsForLinks($commits_for_links);
 | |
| 
 | |
|     $local_view = id(new DifferentialLocalCommitsView())
 | |
|       ->setUser($user)
 | |
|       ->setLocalCommits(idx($props, 'local:commits'))
 | |
|       ->setCommitsForLinks($commits_for_links);
 | |
| 
 | |
|     if ($repository) {
 | |
|       $other_revisions = $this->loadOtherRevisions(
 | |
|         $changesets,
 | |
|         $target,
 | |
|         $repository);
 | |
|     } else {
 | |
|       $other_revisions = array();
 | |
|     }
 | |
| 
 | |
|     $other_view = null;
 | |
|     if ($other_revisions) {
 | |
|       $other_view = $this->renderOtherRevisions($other_revisions);
 | |
|     }
 | |
| 
 | |
|     $toc_view = new DifferentialDiffTableOfContentsView();
 | |
|     $toc_view->setChangesets($changesets);
 | |
|     $toc_view->setVisibleChangesets($visible_changesets);
 | |
|     $toc_view->setRenderingReferences($rendering_references);
 | |
|     $toc_view->setUnitTestData(idx($props, 'arc:unit', array()));
 | |
|     if ($repository) {
 | |
|       $toc_view->setRepository($repository);
 | |
|     }
 | |
|     $toc_view->setDiff($target);
 | |
|     $toc_view->setUser($user);
 | |
|     $toc_view->setRevisionID($revision->getID());
 | |
|     $toc_view->setWhitespace($whitespace);
 | |
| 
 | |
|     $comment_form = null;
 | |
|     if (!$viewer_is_anonymous) {
 | |
|       $draft = id(new PhabricatorDraft())->loadOneWhere(
 | |
|         'authorPHID = %s AND draftKey = %s',
 | |
|         $user->getPHID(),
 | |
|         'differential-comment-'.$revision->getID());
 | |
| 
 | |
|       $reviewers = array();
 | |
|       $ccs = array();
 | |
|       if ($draft) {
 | |
|         $reviewers = idx($draft->getMetadata(), 'reviewers', array());
 | |
|         $ccs = idx($draft->getMetadata(), 'ccs', array());
 | |
|         if ($reviewers || $ccs) {
 | |
|           $handles = $this->loadViewerHandles(array_merge($reviewers, $ccs));
 | |
|           $reviewers = array_select_keys($handles, $reviewers);
 | |
|           $ccs = array_select_keys($handles, $ccs);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       $comment_form = new DifferentialAddCommentView();
 | |
|       $comment_form->setRevision($revision);
 | |
| 
 | |
|       $review_warnings = array();
 | |
|       foreach ($field_list->getFields() as $field) {
 | |
|         $review_warnings[] = $field->getWarningsForDetailView();
 | |
|       }
 | |
|       $review_warnings = array_mergev($review_warnings);
 | |
| 
 | |
|       if ($review_warnings) {
 | |
|         $review_warnings_panel = id(new PHUIErrorView())
 | |
|           ->setSeverity(PHUIErrorView::SEVERITY_WARNING)
 | |
|           ->setErrors($review_warnings);
 | |
|         $comment_form->setErrorView($review_warnings_panel);
 | |
|       }
 | |
| 
 | |
|       $comment_form->setActions($this->getRevisionCommentActions($revision));
 | |
|       $action_uri = $this->getApplicationURI(
 | |
|         'comment/save/'.$revision->getID().'/');
 | |
| 
 | |
|       $comment_form->setActionURI($action_uri);
 | |
|       $comment_form->setUser($user);
 | |
|       $comment_form->setDraft($draft);
 | |
|       $comment_form->setReviewers(mpull($reviewers, 'getFullName', 'getPHID'));
 | |
|       $comment_form->setCCs(mpull($ccs, 'getFullName', 'getPHID'));
 | |
| 
 | |
|       // TODO: This just makes the "Z" key work. Generalize this and remove
 | |
|       // it at some point.
 | |
|       $comment_form = phutil_tag(
 | |
|         'div',
 | |
|         array(
 | |
|           'class' => 'differential-add-comment-panel',
 | |
|         ),
 | |
|         $comment_form);
 | |
|     }
 | |
| 
 | |
|     $pane_id = celerity_generate_unique_node_id();
 | |
|     Javelin::initBehavior(
 | |
|       'differential-keyboard-navigation',
 | |
|       array(
 | |
|         'haunt' => $pane_id,
 | |
|       ));
 | |
|     Javelin::initBehavior('differential-user-select');
 | |
| 
 | |
|     $page_pane = id(new DifferentialPrimaryPaneView())
 | |
|       ->setID($pane_id)
 | |
|       ->appendChild(array(
 | |
|         $comment_view,
 | |
|         $diff_history,
 | |
|         $warning,
 | |
|         $local_view,
 | |
|         $toc_view,
 | |
|         $other_view,
 | |
|         $changeset_view,
 | |
|       ));
 | |
|     if ($comment_form) {
 | |
| 
 | |
|       $page_pane->appendChild($comment_form);
 | |
|     } else {
 | |
|       // TODO: For now, just use this to get "Login to Comment".
 | |
|       $page_pane->appendChild(
 | |
|         id(new PhabricatorApplicationTransactionCommentView())
 | |
|           ->setUser($user)
 | |
|           ->setRequestURI($request->getRequestURI()));
 | |
|     }
 | |
| 
 | |
|     $object_id = 'D'.$revision->getID();
 | |
| 
 | |
|     $content = array(
 | |
|       $revision_detail_box,
 | |
|       $page_pane,
 | |
|     );
 | |
| 
 | |
|     $crumbs = $this->buildApplicationCrumbs();
 | |
|     $crumbs->addTextCrumb($object_id, '/'.$object_id);
 | |
| 
 | |
|     $prefs = $user->loadPreferences();
 | |
| 
 | |
|     $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
 | |
|     if ($prefs->getPreference($pref_filetree)) {
 | |
|       $collapsed = $prefs->getPreference(
 | |
|         PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED,
 | |
|         false);
 | |
| 
 | |
|       $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
 | |
|         ->setTitle('D'.$revision->getID())
 | |
|         ->setBaseURI(new PhutilURI('/D'.$revision->getID()))
 | |
|         ->setCollapsed((bool)$collapsed)
 | |
|         ->build($changesets);
 | |
|       $nav->appendChild($content);
 | |
|       $nav->setCrumbs($crumbs);
 | |
|       $content = $nav;
 | |
|     } else {
 | |
|       array_unshift($content, $crumbs);
 | |
|     }
 | |
| 
 | |
|     return $this->buildApplicationPage(
 | |
|       $content,
 | |
|       array(
 | |
|         'title' => $object_id.' '.$revision->getTitle(),
 | |
|         'pageObjects' => array($revision->getPHID()),
 | |
|       ));
 | |
|   }
 | |
| 
 | |
|   private function getRevisionActions(DifferentialRevision $revision) {
 | |
|     $viewer = $this->getRequest()->getUser();
 | |
|     $revision_id = $revision->getID();
 | |
|     $revision_phid = $revision->getPHID();
 | |
| 
 | |
|     $can_edit = PhabricatorPolicyFilter::hasCapability(
 | |
|       $viewer,
 | |
|       $revision,
 | |
|       PhabricatorPolicyCapability::CAN_EDIT);
 | |
| 
 | |
|     $actions = array();
 | |
| 
 | |
|     $actions[] = id(new PhabricatorActionView())
 | |
|       ->setIcon('fa-pencil')
 | |
|       ->setHref("/differential/revision/edit/{$revision_id}/")
 | |
|       ->setName(pht('Edit Revision'))
 | |
|       ->setDisabled(!$can_edit)
 | |
|       ->setWorkflow(!$can_edit);
 | |
| 
 | |
|     $this->requireResource('phabricator-object-selector-css');
 | |
|     $this->requireResource('javelin-behavior-phabricator-object-selector');
 | |
| 
 | |
|     $actions[] = id(new PhabricatorActionView())
 | |
|       ->setIcon('fa-link')
 | |
|       ->setName(pht('Edit Dependencies'))
 | |
|       ->setHref("/search/attach/{$revision_phid}/DREV/dependencies/")
 | |
|       ->setWorkflow(true)
 | |
|       ->setDisabled(!$can_edit);
 | |
| 
 | |
|     $maniphest = 'PhabricatorManiphestApplication';
 | |
|     if (PhabricatorApplication::isClassInstalled($maniphest)) {
 | |
|       $actions[] = id(new PhabricatorActionView())
 | |
|         ->setIcon('fa-anchor')
 | |
|         ->setName(pht('Edit Maniphest Tasks'))
 | |
|         ->setHref("/search/attach/{$revision_phid}/TASK/")
 | |
|         ->setWorkflow(true)
 | |
|         ->setDisabled(!$can_edit);
 | |
|     }
 | |
| 
 | |
|     $request_uri = $this->getRequest()->getRequestURI();
 | |
|     $actions[] = id(new PhabricatorActionView())
 | |
|       ->setIcon('fa-download')
 | |
|       ->setName(pht('Download Raw Diff'))
 | |
|       ->setHref($request_uri->alter('download', 'true'));
 | |
| 
 | |
|     return $actions;
 | |
|   }
 | |
| 
 | |
|   private function getRevisionCommentActions(DifferentialRevision $revision) {
 | |
|     $actions = array(
 | |
|       DifferentialAction::ACTION_COMMENT => true,
 | |
|     );
 | |
| 
 | |
|     $viewer = $this->getRequest()->getUser();
 | |
|     $viewer_phid = $viewer->getPHID();
 | |
|     $viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
 | |
|     $viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
 | |
|     $status = $revision->getStatus();
 | |
| 
 | |
|     $viewer_has_accepted = false;
 | |
|     $viewer_has_rejected = false;
 | |
|     $status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED;
 | |
|     $status_rejected = DifferentialReviewerStatus::STATUS_REJECTED;
 | |
|     foreach ($revision->getReviewerStatus() as $reviewer) {
 | |
|       if ($reviewer->getReviewerPHID() == $viewer_phid) {
 | |
|         if ($reviewer->getStatus() == $status_accepted) {
 | |
|           $viewer_has_accepted = true;
 | |
|         }
 | |
|         if ($reviewer->getStatus() == $status_rejected) {
 | |
|           $viewer_has_rejected = true;
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $allow_self_accept = PhabricatorEnv::getEnvConfig(
 | |
|       'differential.allow-self-accept');
 | |
|     $always_allow_abandon = PhabricatorEnv::getEnvConfig(
 | |
|       'differential.always-allow-abandon');
 | |
|     $always_allow_close = PhabricatorEnv::getEnvConfig(
 | |
|       'differential.always-allow-close');
 | |
|     $allow_reopen = PhabricatorEnv::getEnvConfig(
 | |
|       'differential.allow-reopen');
 | |
| 
 | |
|     if ($viewer_is_owner) {
 | |
|       switch ($status) {
 | |
|         case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
 | |
|           $actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
 | |
|           $actions[DifferentialAction::ACTION_ABANDON] = true;
 | |
|           $actions[DifferentialAction::ACTION_RETHINK] = true;
 | |
|           break;
 | |
|         case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
 | |
|         case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
 | |
|           $actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
 | |
|           $actions[DifferentialAction::ACTION_ABANDON] = true;
 | |
|           $actions[DifferentialAction::ACTION_REQUEST] = true;
 | |
|           break;
 | |
|         case ArcanistDifferentialRevisionStatus::ACCEPTED:
 | |
|           $actions[DifferentialAction::ACTION_ABANDON] = true;
 | |
|           $actions[DifferentialAction::ACTION_REQUEST] = true;
 | |
|           $actions[DifferentialAction::ACTION_RETHINK] = true;
 | |
|           $actions[DifferentialAction::ACTION_CLOSE] = true;
 | |
|           break;
 | |
|         case ArcanistDifferentialRevisionStatus::CLOSED:
 | |
|           break;
 | |
|         case ArcanistDifferentialRevisionStatus::ABANDONED:
 | |
|           $actions[DifferentialAction::ACTION_RECLAIM] = true;
 | |
|           break;
 | |
|       }
 | |
|     } else {
 | |
|       switch ($status) {
 | |
|         case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
 | |
|           $actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
 | |
|           $actions[DifferentialAction::ACTION_ACCEPT] = true;
 | |
|           $actions[DifferentialAction::ACTION_REJECT] = true;
 | |
|           $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
 | |
|           break;
 | |
|         case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
 | |
|         case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
 | |
|           $actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
 | |
|           $actions[DifferentialAction::ACTION_ACCEPT] = true;
 | |
|           $actions[DifferentialAction::ACTION_REJECT] = !$viewer_has_rejected;
 | |
|           $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
 | |
|           break;
 | |
|         case ArcanistDifferentialRevisionStatus::ACCEPTED:
 | |
|           $actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
 | |
|           $actions[DifferentialAction::ACTION_ACCEPT] = !$viewer_has_accepted;
 | |
|           $actions[DifferentialAction::ACTION_REJECT] = true;
 | |
|           $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
 | |
|           break;
 | |
|         case ArcanistDifferentialRevisionStatus::CLOSED:
 | |
|         case ArcanistDifferentialRevisionStatus::ABANDONED:
 | |
|           break;
 | |
|       }
 | |
|       if ($status != ArcanistDifferentialRevisionStatus::CLOSED) {
 | |
|         $actions[DifferentialAction::ACTION_CLAIM] = true;
 | |
|         $actions[DifferentialAction::ACTION_CLOSE] = $always_allow_close;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
 | |
|     $actions[DifferentialAction::ACTION_ADDCCS] = true;
 | |
|     $actions[DifferentialAction::ACTION_REOPEN] = $allow_reopen &&
 | |
|       ($status == ArcanistDifferentialRevisionStatus::CLOSED);
 | |
| 
 | |
|     $actions = array_keys(array_filter($actions));
 | |
|     $actions_dict = array();
 | |
|     foreach ($actions as $action) {
 | |
|       $actions_dict[$action] = DifferentialAction::getActionVerb($action);
 | |
|     }
 | |
| 
 | |
|     return $actions_dict;
 | |
|   }
 | |
| 
 | |
|   private function loadChangesetsAndVsMap(
 | |
|     DifferentialDiff $target,
 | |
|     DifferentialDiff $diff_vs = null,
 | |
|     PhabricatorRepository $repository = null) {
 | |
| 
 | |
|     $load_diffs = array($target);
 | |
|     if ($diff_vs) {
 | |
|       $load_diffs[] = $diff_vs;
 | |
|     }
 | |
| 
 | |
|     $raw_changesets = id(new DifferentialChangesetQuery())
 | |
|       ->setViewer($this->getRequest()->getUser())
 | |
|       ->withDiffs($load_diffs)
 | |
|       ->execute();
 | |
|     $changeset_groups = mgroup($raw_changesets, 'getDiffID');
 | |
| 
 | |
|     $changesets = idx($changeset_groups, $target->getID(), array());
 | |
|     $changesets = mpull($changesets, null, 'getID');
 | |
| 
 | |
|     $refs          = array();
 | |
|     $vs_map        = array();
 | |
|     $vs_changesets = array();
 | |
|     if ($diff_vs) {
 | |
|       $vs_id                  = $diff_vs->getID();
 | |
|       $vs_changesets_path_map = array();
 | |
|       foreach (idx($changeset_groups, $vs_id, array()) as $changeset) {
 | |
|         $path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs);
 | |
|         $vs_changesets_path_map[$path] = $changeset;
 | |
|         $vs_changesets[$changeset->getID()] = $changeset;
 | |
|       }
 | |
|       foreach ($changesets as $key => $changeset) {
 | |
|         $path = $changeset->getAbsoluteRepositoryPath($repository, $target);
 | |
|         if (isset($vs_changesets_path_map[$path])) {
 | |
|           $vs_map[$changeset->getID()] =
 | |
|             $vs_changesets_path_map[$path]->getID();
 | |
|           $refs[$changeset->getID()] =
 | |
|             $changeset->getID().'/'.$vs_changesets_path_map[$path]->getID();
 | |
|           unset($vs_changesets_path_map[$path]);
 | |
|         } else {
 | |
|           $refs[$changeset->getID()] = $changeset->getID();
 | |
|         }
 | |
|       }
 | |
|       foreach ($vs_changesets_path_map as $path => $changeset) {
 | |
|         $changesets[$changeset->getID()] = $changeset;
 | |
|         $vs_map[$changeset->getID()]     = -1;
 | |
|         $refs[$changeset->getID()]       = $changeset->getID().'/-1';
 | |
|       }
 | |
|     } else {
 | |
|       foreach ($changesets as $changeset) {
 | |
|         $refs[$changeset->getID()] = $changeset->getID();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $changesets = msort($changesets, 'getSortKey');
 | |
| 
 | |
|     return array($changesets, $vs_map, $vs_changesets, $refs);
 | |
|   }
 | |
| 
 | |
|   private function buildSymbolIndexes(
 | |
|     PhabricatorRepositoryArcanistProject $arc_project,
 | |
|     array $visible_changesets) {
 | |
|     assert_instances_of($visible_changesets, 'DifferentialChangeset');
 | |
| 
 | |
|     $engine = PhabricatorSyntaxHighlighter::newEngine();
 | |
| 
 | |
|     $langs = $arc_project->getSymbolIndexLanguages();
 | |
|     if (!$langs) {
 | |
|       return array(array(), array());
 | |
|     }
 | |
| 
 | |
|     $symbol_indexes = array();
 | |
| 
 | |
|     $project_phids = array_merge(
 | |
|       array($arc_project->getPHID()),
 | |
|       nonempty($arc_project->getSymbolIndexProjects(), array()));
 | |
| 
 | |
|     $indexed_langs = array_fill_keys($langs, true);
 | |
|     foreach ($visible_changesets as $key => $changeset) {
 | |
|       $lang = $engine->getLanguageFromFilename($changeset->getFilename());
 | |
|       if (isset($indexed_langs[$lang])) {
 | |
|         $symbol_indexes[$key] = array(
 | |
|           'lang'      => $lang,
 | |
|           'projects'  => $project_phids,
 | |
|         );
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return array($symbol_indexes, $project_phids);
 | |
|   }
 | |
| 
 | |
|   private function loadOtherRevisions(
 | |
|     array $changesets,
 | |
|     DifferentialDiff $target,
 | |
|     PhabricatorRepository $repository) {
 | |
|     assert_instances_of($changesets, 'DifferentialChangeset');
 | |
| 
 | |
|     $paths = array();
 | |
|     foreach ($changesets as $changeset) {
 | |
|       $paths[] = $changeset->getAbsoluteRepositoryPath(
 | |
|         $repository,
 | |
|         $target);
 | |
|     }
 | |
| 
 | |
|     if (!$paths) {
 | |
|       return array();
 | |
|     }
 | |
| 
 | |
|     $path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
 | |
| 
 | |
|     if (!$path_map) {
 | |
|       return array();
 | |
|     }
 | |
| 
 | |
|     $query = id(new DifferentialRevisionQuery())
 | |
|       ->setViewer($this->getRequest()->getUser())
 | |
|       ->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
 | |
|       ->setOrder(DifferentialRevisionQuery::ORDER_PATH_MODIFIED)
 | |
|       ->setLimit(10)
 | |
|       ->needFlags(true)
 | |
|       ->needDrafts(true)
 | |
|       ->needRelationships(true);
 | |
| 
 | |
|     foreach ($path_map as $path => $path_id) {
 | |
|       $query->withPath($repository->getID(), $path_id);
 | |
|     }
 | |
| 
 | |
|     $results = $query->execute();
 | |
| 
 | |
|     // Strip out *this* revision.
 | |
|     foreach ($results as $key => $result) {
 | |
|       if ($result->getID() == $this->revisionID) {
 | |
|         unset($results[$key]);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return $results;
 | |
|   }
 | |
| 
 | |
|   private function renderOtherRevisions(array $revisions) {
 | |
|     assert_instances_of($revisions, 'DifferentialRevision');
 | |
| 
 | |
|     $user = $this->getRequest()->getUser();
 | |
| 
 | |
|     $view = id(new DifferentialRevisionListView())
 | |
|       ->setRevisions($revisions)
 | |
|       ->setUser($user);
 | |
| 
 | |
|     $phids = $view->getRequiredHandlePHIDs();
 | |
|     $handles = $this->loadViewerHandles($phids);
 | |
|     $view->setHandles($handles);
 | |
| 
 | |
|     return id(new PHUIObjectBoxView())
 | |
|       ->setHeaderText(pht('Open Revisions Affecting These Files'))
 | |
|       ->appendChild($view);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Note this code is somewhat similar to the buildPatch method in
 | |
|    * @{class:DifferentialReviewRequestMail}.
 | |
|    *
 | |
|    * @return @{class:AphrontRedirectResponse}
 | |
|    */
 | |
|   private function buildRawDiffResponse(
 | |
|     DifferentialRevision $revision,
 | |
|     array $changesets,
 | |
|     array $vs_changesets,
 | |
|     array $vs_map,
 | |
|     PhabricatorRepository $repository = null) {
 | |
| 
 | |
|     assert_instances_of($changesets,    'DifferentialChangeset');
 | |
|     assert_instances_of($vs_changesets, 'DifferentialChangeset');
 | |
| 
 | |
|     $viewer = $this->getRequest()->getUser();
 | |
| 
 | |
|     id(new DifferentialHunkQuery())
 | |
|       ->setViewer($viewer)
 | |
|       ->withChangesets($changesets)
 | |
|       ->needAttachToChangesets(true)
 | |
|       ->execute();
 | |
| 
 | |
|     $diff = new DifferentialDiff();
 | |
|     $diff->attachChangesets($changesets);
 | |
|     $raw_changes = $diff->buildChangesList();
 | |
|     $changes = array();
 | |
|     foreach ($raw_changes as $changedict) {
 | |
|       $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
 | |
|     }
 | |
| 
 | |
|     $loader = id(new PhabricatorFileBundleLoader())
 | |
|       ->setViewer($viewer);
 | |
| 
 | |
|     $bundle = ArcanistBundle::newFromChanges($changes);
 | |
|     $bundle->setLoadFileDataCallback(array($loader, 'loadFileData'));
 | |
| 
 | |
|     $vcs = $repository ? $repository->getVersionControlSystem() : null;
 | |
|     switch ($vcs) {
 | |
|       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
 | |
|       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
 | |
|         $raw_diff = $bundle->toGitPatch();
 | |
|         break;
 | |
|       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
 | |
|       default:
 | |
|         $raw_diff = $bundle->toUnifiedDiff();
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     $request_uri = $this->getRequest()->getRequestURI();
 | |
| 
 | |
|     // this ends up being something like
 | |
|     //   D123.diff
 | |
|     // or the verbose
 | |
|     //   D123.vs123.id123.whitespaceignore-all.diff
 | |
|     // lame but nice to include these options
 | |
|     $file_name = ltrim($request_uri->getPath(), '/').'.';
 | |
|     foreach ($request_uri->getQueryParams() as $key => $value) {
 | |
|       if ($key == 'download') {
 | |
|         continue;
 | |
|       }
 | |
|       $file_name .= $key.$value.'.';
 | |
|     }
 | |
|     $file_name .= 'diff';
 | |
| 
 | |
|     $file = PhabricatorFile::buildFromFileDataOrHash(
 | |
|       $raw_diff,
 | |
|       array(
 | |
|         'name' => $file_name,
 | |
|         'ttl' => (60 * 60 * 24),
 | |
|         'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
 | |
|       ));
 | |
| 
 | |
|     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
 | |
|       $file->attachToObject($revision->getPHID());
 | |
|     unset($unguarded);
 | |
| 
 | |
|     return $file->getRedirectResponse();
 | |
|   }
 | |
| 
 | |
|   private function buildTransactions(
 | |
|     DifferentialRevision $revision,
 | |
|     DifferentialDiff $left_diff,
 | |
|     DifferentialDiff $right_diff,
 | |
|     array $changesets) {
 | |
| 
 | |
|     $timeline = $this->buildTransactionTimeline(
 | |
|       $revision,
 | |
|       new DifferentialTransactionQuery(),
 | |
|       $engine = null,
 | |
|       array(
 | |
|         'left' => $left_diff->getID(),
 | |
|         'right' => $right_diff->getID(),
 | |
|       ));
 | |
| 
 | |
|     return $timeline;
 | |
|   }
 | |
| 
 | |
|   private function buildRevisionWarnings(
 | |
|     DifferentialRevision $revision,
 | |
|     PhabricatorCustomFieldList $field_list,
 | |
|     array $warning_handle_map,
 | |
|     array $handles) {
 | |
| 
 | |
|     $warnings = array();
 | |
|     foreach ($field_list->getFields() as $key => $field) {
 | |
|       $phids = idx($warning_handle_map, $key, array());
 | |
|       $field_handles = array_select_keys($handles, $phids);
 | |
|       $field_warnings = $field->getWarningsForRevisionHeader($field_handles);
 | |
|       foreach ($field_warnings as $warning) {
 | |
|         $warnings[] = $warning;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return $warnings;
 | |
|   }
 | |
| 
 | |
| }
 |