Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/differential/controller/DifferentialController.php b/src/applications/differential/controller/DifferentialController.php
index 2258c4fdcb..fb8a6249a7 100644
--- a/src/applications/differential/controller/DifferentialController.php
+++ b/src/applications/differential/controller/DifferentialController.php
@@ -1,226 +1,276 @@
<?php
abstract class DifferentialController extends PhabricatorController {
+ private $packageChangesetMap;
+ private $pathPackageMap;
+ private $authorityPackages;
+
public function buildSideNavView($for_app = false) {
$viewer = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new DifferentialRevisionSearchEngine())
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNavView(true)->getMenu();
}
- protected function buildTableOfContents(
- array $changesets,
- array $visible_changesets,
- array $coverage) {
- $viewer = $this->getViewer();
+ protected function buildPackageMaps(array $changesets) {
+ assert_instances_of($changesets, 'DifferentialChangeset');
- $toc_view = id(new PHUIDiffTableOfContentsListView())
- ->setViewer($viewer)
- ->setBare(true);
+ $this->packageChangesetMap = array();
+ $this->pathPackageMap = array();
+ $this->authorityPackages = array();
+
+ if (!$changesets) {
+ return;
+ }
+
+ $viewer = $this->getViewer();
$have_owners = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorOwnersApplication',
$viewer);
- if ($have_owners) {
- $repository_phid = null;
- if ($changesets) {
- $changeset = head($changesets);
- $diff = $changeset->getDiff();
- $repository_phid = $diff->getRepositoryPHID();
- }
+ if (!$have_owners) {
+ return;
+ }
- if (!$repository_phid) {
- $have_owners = false;
- } else {
- if ($viewer->getPHID()) {
- $packages = id(new PhabricatorOwnersPackageQuery())
- ->setViewer($viewer)
- ->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
- ->withAuthorityPHIDs(array($viewer->getPHID()))
- ->execute();
- $toc_view->setAuthorityPackages($packages);
- }
-
- $paths = mpull($changesets, 'getOwnersFilename');
-
- $control_query = id(new PhabricatorOwnersPackageQuery())
- ->setViewer($viewer)
- ->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
- ->withControl($repository_phid, $paths);
- $control_query->execute();
+ $changeset = head($changesets);
+ $diff = $changeset->getDiff();
+ $repository_phid = $diff->getRepositoryPHID();
+ if (!$repository_phid) {
+ return;
+ }
+
+ if ($viewer->getPHID()) {
+ $packages = id(new PhabricatorOwnersPackageQuery())
+ ->setViewer($viewer)
+ ->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
+ ->withAuthorityPHIDs(array($viewer->getPHID()))
+ ->execute();
+ $this->authorityPackages = $packages;
+ }
+
+ $paths = mpull($changesets, 'getOwnersFilename');
+
+ $control_query = id(new PhabricatorOwnersPackageQuery())
+ ->setViewer($viewer)
+ ->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
+ ->withControl($repository_phid, $paths);
+ $control_query->execute();
+
+ foreach ($changesets as $changeset) {
+ $changeset_path = $changeset->getOwnersFilename();
+
+ $packages = $control_query->getControllingPackagesForPath(
+ $repository_phid,
+ $changeset_path);
+
+ $this->pathPackageMap[$changeset_path] = $packages;
+ foreach ($packages as $package) {
+ $this->packageChangesetMap[$package->getPHID()][] = $changeset;
}
}
+ }
+
+ protected function getAuthorityPackages() {
+ if ($this->authorityPackages === null) {
+ throw new PhutilInvalidStateException('buildPackageMaps');
+ }
+ return $this->authorityPackages;
+ }
+
+ protected function getChangesetPackages(DifferentialChangeset $changeset) {
+ if ($this->pathPackageMap === null) {
+ throw new PhutilInvalidStateException('buildPackageMaps');
+ }
+
+ $path = $changeset->getOwnersFilename();
+ return idx($this->pathPackageMap, $path, array());
+ }
+
+ protected function getPackageChangesets($package_phid) {
+ if ($this->packageChangesetMap === null) {
+ throw new PhutilInvalidStateException('buildPackageMaps');
+ }
+
+ return idx($this->packageChangesetMap, $package_phid, array());
+ }
+
+ protected function buildTableOfContents(
+ array $changesets,
+ array $visible_changesets,
+ array $coverage) {
+ $viewer = $this->getViewer();
+
+ $toc_view = id(new PHUIDiffTableOfContentsListView())
+ ->setViewer($viewer)
+ ->setBare(true)
+ ->setAuthorityPackages($this->getAuthorityPackages());
foreach ($changesets as $changeset_id => $changeset) {
$is_visible = isset($visible_changesets[$changeset_id]);
$anchor = $changeset->getAnchorName();
$filename = $changeset->getFilename();
$coverage_id = 'differential-mcoverage-'.md5($filename);
$item = id(new PHUIDiffTableOfContentsItemView())
->setChangeset($changeset)
->setIsVisible($is_visible)
->setAnchor($anchor)
->setCoverage(idx($coverage, $filename))
->setCoverageID($coverage_id);
- if ($have_owners) {
- $packages = $control_query->getControllingPackagesForPath(
- $repository_phid,
- $changeset->getOwnersFilename());
- $item->setPackages($packages);
- }
+ $packages = $this->getChangesetPackages($changeset);
+ $item->setPackages($packages);
$toc_view->addItem($item);
}
return $toc_view;
}
protected function loadDiffProperties(array $diffs) {
$diffs = mpull($diffs, null, 'getID');
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID IN (%Ld)',
array_keys($diffs));
$properties = mgroup($properties, 'getDiffID');
foreach ($diffs as $id => $diff) {
$values = idx($properties, $id, array());
$values = mpull($values, 'getData', 'getName');
$diff->attachDiffProperties($values);
}
}
protected function loadHarbormasterData(array $diffs) {
$viewer = $this->getViewer();
$diffs = mpull($diffs, null, 'getPHID');
$buildables = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withBuildablePHIDs(array_keys($diffs))
->withManualBuildables(false)
->needBuilds(true)
->needTargets(true)
->execute();
$buildables = mpull($buildables, null, 'getBuildablePHID');
foreach ($diffs as $phid => $diff) {
$diff->attachBuildable(idx($buildables, $phid));
}
$target_map = array();
foreach ($diffs as $phid => $diff) {
$target_map[$phid] = $diff->getBuildTargetPHIDs();
}
$all_target_phids = array_mergev($target_map);
if ($all_target_phids) {
$unit_messages = id(new HarbormasterBuildUnitMessage())->loadAllWhere(
'buildTargetPHID IN (%Ls)',
$all_target_phids);
$unit_messages = mgroup($unit_messages, 'getBuildTargetPHID');
} else {
$unit_messages = array();
}
foreach ($diffs as $phid => $diff) {
$target_phids = idx($target_map, $phid, array());
$messages = array_select_keys($unit_messages, $target_phids);
$messages = array_mergev($messages);
$diff->attachUnitMessages($messages);
}
// For diffs with no messages, look for legacy unit messages stored on the
// diff itself.
foreach ($diffs as $phid => $diff) {
if ($diff->getUnitMessages()) {
continue;
}
if (!$diff->hasDiffProperty('arc:unit')) {
continue;
}
$legacy_messages = $diff->getProperty('arc:unit');
if (!$legacy_messages) {
continue;
}
// Show the top 100 legacy lint messages. Previously, we showed some
// by default and let the user toggle the rest. With modern messages,
// we can send the user to the Harbormaster detail page. Just show
// "a lot" of messages in legacy cases to try to strike a balance
// between implementation simplicitly and compatibility.
$legacy_messages = array_slice($legacy_messages, 0, 100);
$messages = array();
foreach ($legacy_messages as $message) {
$messages[] = HarbormasterBuildUnitMessage::newFromDictionary(
new HarbormasterBuildTarget(),
$this->getModernUnitMessageDictionary($message));
}
$diff->attachUnitMessages($messages);
}
}
private function getModernUnitMessageDictionary(array $map) {
// Strip out `null` values to satisfy stricter typechecks.
foreach ($map as $key => $value) {
if ($value === null) {
unset($map[$key]);
}
}
// Cast duration to a float since it used to be a string in some
// cases.
if (isset($map['duration'])) {
$map['duration'] = (double)$map['duration'];
}
return $map;
}
protected function getDiffTabLabels(array $diffs) {
// Make sure we're only going to render unique diffs.
$diffs = mpull($diffs, null, 'getID');
$labels = array(pht('Left'), pht('Right'));
$results = array();
foreach ($diffs as $diff) {
if (count($diffs) == 2) {
$label = array_shift($labels);
$label = pht('%s (Diff %d)', $label, $diff->getID());
} else {
$label = pht('Diff %d', $diff->getID());
}
$results[] = array(
$label,
$diff,
);
}
return $results;
}
}
diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php
index 7340687bc4..e56eb27d99 100644
--- a/src/applications/differential/controller/DifferentialDiffViewController.php
+++ b/src/applications/differential/controller/DifferentialDiffViewController.php
@@ -1,214 +1,216 @@
<?php
final class DifferentialDiffViewController extends DifferentialController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$diff = id(new DifferentialDiffQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$diff) {
return new Aphront404Response();
}
if ($diff->getRevisionID()) {
return id(new AphrontRedirectResponse())
->setURI('/D'.$diff->getRevisionID().'?id='.$diff->getID());
}
if ($request->isFormPost()) {
$diff_id = $diff->getID();
$revision_id = $request->getInt('revisionID');
if ($revision_id) {
$attach_uri = "/revision/attach/{$diff_id}/to/{$revision_id}/";
} else {
$attach_uri = "/revision/attach/{$diff_id}/to/";
}
$attach_uri = $this->getApplicationURI($attach_uri);
return id(new AphrontRedirectResponse())
->setURI($attach_uri);
}
$diff_phid = $diff->getPHID();
$buildables = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withBuildablePHIDs(array($diff_phid))
->withManualBuildables(false)
->needBuilds(true)
->needTargets(true)
->execute();
$buildables = mpull($buildables, null, 'getBuildablePHID');
$diff->attachBuildable(idx($buildables, $diff_phid));
// TODO: implement optgroup support in AphrontFormSelectControl?
$select = array();
$select[] = hsprintf('<optgroup label="%s">', pht('Create New Revision'));
$select[] = phutil_tag(
'option',
array('value' => ''),
pht('Create a new Revision...'));
$select[] = hsprintf('</optgroup>');
$selected_id = $request->getInt('revisionID');
$revisions = $this->loadSelectableRevisions($viewer, $selected_id);
if ($revisions) {
$select[] = hsprintf(
'<optgroup label="%s">',
pht('Update Existing Revision'));
foreach ($revisions as $revision) {
if ($selected_id == $revision->getID()) {
$selected = 'selected';
} else {
$selected = null;
}
$select[] = phutil_tag(
'option',
array(
'value' => $revision->getID(),
'selected' => $selected,
),
id(new PhutilUTF8StringTruncator())
->setMaximumGlyphs(128)
->truncateString(
'D'.$revision->getID().' '.$revision->getTitle()));
}
$select[] = hsprintf('</optgroup>');
}
$select = phutil_tag(
'select',
array('name' => 'revisionID'),
$select);
$form = id(new AphrontFormView())
->setViewer($viewer)
->appendRemarkupInstructions(
pht(
'Review the diff for correctness. When you are satisfied, either '.
'**create a new revision** or **update an existing revision**.'))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Attach To'))
->setValue($select))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Continue')));
$props = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$diff->getID());
$props = mpull($props, 'getData', 'getName');
$property_head = id(new PHUIHeaderView())
->setHeader(pht('Properties'));
$property_view = new PHUIPropertyListView();
$changesets = $diff->loadChangesets();
$changesets = msort($changesets, 'getSortKey');
+ $this->buildPackageMaps($changesets);
+
$table_of_contents = $this->buildTableOfContents(
$changesets,
$changesets,
$diff->loadCoverageMap($viewer));
$refs = array();
foreach ($changesets as $changeset) {
$refs[$changeset->getID()] = $changeset->getID();
}
$details = id(new DifferentialChangesetListView())
->setChangesets($changesets)
->setVisibleChangesets($changesets)
->setRenderingReferences($refs)
->setStandaloneURI('/differential/changeset/')
->setDiff($diff)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTitle(pht('Diff %d', $diff->getID()))
->setUser($request->getUser());
$title = pht('Diff %d', $diff->getID());
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($title);
$prop_box = id(new PHUIObjectBoxView())
->setHeader($property_head)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($property_view)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setMainColumn(array(
))
->setFooter(array(
$prop_box,
$table_of_contents,
$details,
));
$page = $this->newPage()
->setTitle(pht('Diff View'))
->setCrumbs($crumbs)
->appendChild($view);
return $page;
}
private function loadSelectableRevisions(
PhabricatorUser $viewer,
$selected_id) {
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withAuthors(array($viewer->getPHID()))
->withIsOpen(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$revisions = mpull($revisions, null, 'getID');
// If a specific revision is selected (for example, because the user is
// following the "Update Diff" workflow), but not present in the dropdown,
// try to add it to the dropdown even if it is closed. This allows the
// workflow to be used to update abandoned revisions.
if ($selected_id) {
if (empty($revisions[$selected_id])) {
$selected = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withAuthors(array($viewer->getPHID()))
->withIDs(array($selected_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$revisions = mpull($selected, null, 'getID') + $revisions;
}
}
return $revisions;
}
}
diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php
index 06d8cf0a3f..e1da5ed32e 100644
--- a/src/applications/differential/controller/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/DifferentialRevisionViewController.php
@@ -1,1139 +1,1149 @@
<?php
final class DifferentialRevisionViewController extends DifferentialController {
private $revisionID;
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->revisionID = $request->getURIData('id');
$viewer_is_anonymous = !$viewer->isLoggedIn();
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($this->revisionID))
->setViewer($viewer)
->needReviewers(true)
->needReviewerAuthority(true)
->executeOne();
if (!$revision) {
return new Aphront404Response();
}
$diffs = id(new DifferentialDiffQuery())
->setViewer($viewer)
->withRevisionIDs(array($this->revisionID))
->execute();
$diffs = array_reverse($diffs, $preserve_keys = true);
if (!$diffs) {
throw new Exception(
pht('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($viewer)
->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);
}
$map = $vs_map;
if (!$map) {
$map = array_fill_keys(array_keys($changesets), 0);
}
$old_ids = array();
$new_ids = array();
foreach ($map as $id => $vs) {
if ($vs <= 0) {
$old_ids[] = $id;
$new_ids[] = $id;
} else {
$new_ids[] = $id;
$new_ids[] = $vs;
}
}
$this->loadDiffProperties($diffs);
$props = $target_manual->getDiffProperties();
$subscriber_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$revision->getPHID());
$object_phids = array_merge(
$revision->getReviewerPHIDs(),
$subscriber_phids,
$revision->loadCommitPHIDs(),
array(
$revision->getAuthorPHID(),
$viewer->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($viewer);
$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 PHUIInfoView();
$warning->setTitle(pht('Very Large Diff'));
$warning->setSeverity(PHUIInfoView::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 button-grey',
'href' => $request_uri
->alter('large', 'true')
->setFragment('toc'),
),
pht('Show All Files Inline'))));
$warning = $warning->render();
$old = array_select_keys($changesets, $old_ids);
$new = array_select_keys($changesets, $new_ids);
$query = id(new DifferentialInlineCommentQuery())
->setViewer($viewer)
->needHidden(true)
->withRevisionPHIDs(array($revision->getPHID()));
$inlines = $query->execute();
$inlines = $query->adjustInlinesForChangesets(
$inlines,
$old,
$new,
$revision);
$visible_changesets = array();
foreach ($inlines as $inline) {
$changeset_id = $inline->getChangesetID();
if (isset($changesets[$changeset_id])) {
$visible_changesets[$changeset_id] = $changesets[$changeset_id];
}
}
} else {
$warning = null;
$visible_changesets = $changesets;
}
$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($viewer)
->withIdentifiers($commit_hashes)
->execute();
$commits_for_links = mpull(
$commits_for_links,
null,
'getCommitIdentifier');
} else {
$commits_for_links = array();
}
$header = $this->buildHeader($revision);
$subheader = $this->buildSubheaderView($revision);
$details = $this->buildDetails($revision, $field_list);
$curtain = $this->buildCurtain($revision);
$whitespace = $request->getStr(
'whitespace',
DifferentialChangesetParser::WHITESPACE_IGNORE_MOST);
$repository = $revision->getRepository();
if ($repository) {
$symbol_indexes = $this->buildSymbolIndexes(
$repository,
$visible_changesets);
} else {
$symbol_indexes = array();
}
$revision_warnings = $this->buildRevisionWarnings(
$revision,
$field_list,
$warning_handle_map,
$handles);
$info_view = null;
if ($revision_warnings) {
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors($revision_warnings);
}
$detail_diffs = array_select_keys(
$diffs,
array($diff_vs, $target->getID()));
$detail_diffs = mpull($detail_diffs, null, 'getPHID');
$this->loadHarbormasterData($detail_diffs);
$diff_detail_box = $this->buildDiffDetailView(
$detail_diffs,
$revision,
$field_list);
$unit_box = $this->buildUnitMessagesView(
$target,
$revision);
$timeline = $this->buildTransactions(
$revision,
$diff_vs ? $diffs[$diff_vs] : $target,
$target,
$old_ids,
$new_ids);
$timeline->setQuoteRef($revision->getMonogram());
$changeset_view = id(new DifferentialChangesetListView())
->setChangesets($changesets)
->setVisibleChangesets($visible_changesets)
->setStandaloneURI('/differential/changeset/')
->setRawFileURIs(
'/differential/changeset/?view=old',
'/differential/changeset/?view=new')
->setUser($viewer)
->setDiff($target)
->setRenderingReferences($rendering_references)
->setVsMap($vs_map)
->setWhitespace($whitespace)
->setSymbolIndexes($symbol_indexes)
->setTitle(pht('Diff %s', $target->getID()))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
$revision_id = $revision->getID();
$inline_list_uri = "/revision/inlines/{$revision_id}/";
$inline_list_uri = $this->getApplicationURI($inline_list_uri);
$changeset_view->setInlineListURI($inline_list_uri);
if ($repository) {
$changeset_view->setRepository($repository);
}
if (!$viewer_is_anonymous) {
$changeset_view->setInlineCommentControllerURI(
'/differential/comment/inline/edit/'.$revision->getID().'/');
}
$broken_diffs = $this->loadHistoryDiffStatus($diffs);
$history = id(new DifferentialRevisionUpdateHistoryView())
->setUser($viewer)
->setDiffs($diffs)
->setDiffUnitStatuses($broken_diffs)
->setSelectedVersusDiffID($diff_vs)
->setSelectedDiffID($target->getID())
->setSelectedWhitespace($whitespace)
->setCommitsForLinks($commits_for_links);
$local_table = id(new DifferentialLocalCommitsView())
->setUser($viewer)
->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);
}
+ $this->buildPackageMaps($changesets);
+
$toc_view = $this->buildTableOfContents(
$changesets,
$visible_changesets,
$target->loadCoverageMap($viewer));
+ // Attach changesets to each reviewer so we can show which Owners package
+ // reviewers own no files.
+ foreach ($revision->getReviewers() as $reviewer) {
+ $reviewer_phid = $reviewer->getReviewerPHID();
+ $reviewer_changesets = $this->getPackageChangesets($reviewer_phid);
+ $reviewer->attachChangesets($reviewer_changesets);
+ }
+
$tab_group = id(new PHUITabGroupView())
->addTab(
id(new PHUITabView())
->setName(pht('Files'))
->setKey('files')
->appendChild($toc_view))
->addTab(
id(new PHUITabView())
->setName(pht('History'))
->setKey('history')
->appendChild($history))
->addTab(
id(new PHUITabView())
->setName(pht('Commits'))
->setKey('commits')
->appendChild($local_table));
$stack_graph = id(new DifferentialRevisionGraph())
->setViewer($viewer)
->setSeedPHID($revision->getPHID())
->setLoadEntireGraph(true)
->loadGraph();
if (!$stack_graph->isEmpty()) {
$stack_table = $stack_graph->newGraphTable();
$parent_type = DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST;
$reachable = $stack_graph->getReachableObjects($parent_type);
foreach ($reachable as $key => $reachable_revision) {
if ($reachable_revision->isClosed()) {
unset($reachable[$key]);
}
}
if ($reachable) {
$stack_name = pht('Stack (%s Open)', phutil_count($reachable));
$stack_color = PHUIListItemView::STATUS_FAIL;
} else {
$stack_name = pht('Stack');
$stack_color = null;
}
$tab_group->addTab(
id(new PHUITabView())
->setName($stack_name)
->setKey('stack')
->setColor($stack_color)
->appendChild($stack_table));
}
if ($other_view) {
$tab_group->addTab(
id(new PHUITabView())
->setName(pht('Similar'))
->setKey('similar')
->appendChild($other_view));
}
$tab_view = id(new PHUIObjectBoxView())
->setHeaderText(pht('Revision Contents'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addTabGroup($tab_group);
$signatures = DifferentialRequiredSignaturesField::loadForRevision(
$revision);
$missing_signatures = false;
foreach ($signatures as $phid => $signed) {
if (!$signed) {
$missing_signatures = true;
}
}
$footer = array();
$signature_message = null;
if ($missing_signatures) {
$signature_message = id(new PHUIInfoView())
->setTitle(pht('Content Hidden'))
->appendChild(
pht(
'The content of this revision is hidden until the author has '.
'signed all of the required legal agreements.'));
} else {
$anchor = id(new PhabricatorAnchorView())
->setAnchorName('toc')
->setNavigationMarker(true);
$footer[] = array(
$anchor,
$warning,
$tab_view,
$changeset_view,
);
}
$comment_view = id(new DifferentialRevisionEditEngine())
->setViewer($viewer)
->buildEditEngineCommentView($revision);
$comment_view->setTransactionTimeline($timeline);
$review_warnings = array();
foreach ($field_list->getFields() as $field) {
$review_warnings[] = $field->getWarningsForDetailView();
}
$review_warnings = array_mergev($review_warnings);
if ($review_warnings) {
$warnings_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors($review_warnings);
$comment_view->setInfoView($warnings_view);
}
$footer[] = $comment_view;
$monogram = $revision->getMonogram();
$operations_box = $this->buildOperationsBox($revision);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($monogram);
$crumbs->setBorder(true);
$filetree_on = $viewer->compareUserSetting(
PhabricatorShowFiletreeSetting::SETTINGKEY,
PhabricatorShowFiletreeSetting::VALUE_ENABLE_FILETREE);
$nav = null;
if ($filetree_on) {
$collapsed_key = PhabricatorFiletreeVisibleSetting::SETTINGKEY;
$collapsed_value = $viewer->getUserSetting($collapsed_key);
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setTitle($monogram)
->setBaseURI(new PhutilURI($revision->getURI()))
->setCollapsed((bool)$collapsed_value)
->build($changesets);
}
Javelin::initBehavior('differential-user-select');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setSubheader($subheader)
->setCurtain($curtain)
->setMainColumn(
array(
$operations_box,
$info_view,
$details,
$diff_detail_box,
$unit_box,
$timeline,
$signature_message,
))
->setFooter($footer);
$page = $this->newPage()
->setTitle($monogram.' '.$revision->getTitle())
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($revision->getPHID()))
->appendChild($view);
if ($nav) {
$page->setNavigation($nav);
}
return $page;
}
private function buildHeader(DifferentialRevision $revision) {
$view = id(new PHUIHeaderView())
->setHeader($revision->getTitle($revision))
->setUser($this->getViewer())
->setPolicyObject($revision)
->setHeaderIcon('fa-cog');
$status_tag = id(new PHUITagView())
->setName($revision->getStatusDisplayName())
->setIcon($revision->getStatusIcon())
->setColor($revision->getStatusIconColor())
->setType(PHUITagView::TYPE_SHADE);
$view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_tag);
return $view;
}
private function buildSubheaderView(DifferentialRevision $revision) {
$viewer = $this->getViewer();
$author_phid = $revision->getAuthorPHID();
$author = $viewer->renderHandle($author_phid)->render();
$date = phabricator_datetime($revision->getDateCreated(), $viewer);
$author = phutil_tag('strong', array(), $author);
$handles = $viewer->loadHandles(array($author_phid));
$image_uri = $handles[$author_phid]->getImageURI();
$image_href = $handles[$author_phid]->getURI();
$content = pht('Authored by %s on %s.', $author, $date);
return id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
}
private function buildDetails(
DifferentialRevision $revision,
$custom_fields) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
if ($custom_fields) {
$custom_fields->appendFieldsToPropertyList(
$revision,
$viewer,
$properties);
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Details'));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
}
private function buildCurtain(DifferentialRevision $revision) {
$viewer = $this->getViewer();
$revision_id = $revision->getID();
$revision_phid = $revision->getPHID();
$curtain = $this->newCurtainView($revision);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$revision,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setHref("/differential/revision/edit/{$revision_id}/")
->setName(pht('Edit Revision'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-upload')
->setHref("/differential/revision/update/{$revision_id}/")
->setName(pht('Update Diff'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$request_uri = $this->getRequest()->getRequestURI();
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-download')
->setName(pht('Download Raw Diff'))
->setHref($request_uri->alter('download', 'true')));
$relationship_list = PhabricatorObjectRelationshipList::newForObject(
$viewer,
$revision);
$revision_actions = array(
DifferentialRevisionHasParentRelationship::RELATIONSHIPKEY,
DifferentialRevisionHasChildRelationship::RELATIONSHIPKEY,
);
$revision_submenu = $relationship_list->newActionSubmenu($revision_actions)
->setName(pht('Edit Related Revisions...'))
->setIcon('fa-cog');
$curtain->addAction($revision_submenu);
$relationship_submenu = $relationship_list->newActionMenu();
if ($relationship_submenu) {
$curtain->addAction($relationship_submenu);
}
$repository = $revision->getRepository();
if ($repository && $repository->canPerformAutomation()) {
$revision_id = $revision->getID();
$op = new DrydockLandRepositoryOperation();
$barrier = $op->getBarrierToLanding($viewer, $revision);
if ($barrier) {
$can_land = false;
} else {
$can_land = true;
}
$action = id(new PhabricatorActionView())
->setName(pht('Land Revision'))
->setIcon('fa-fighter-jet')
->setHref("/differential/revision/operation/{$revision_id}/")
->setWorkflow(true)
->setDisabled(!$can_land);
$curtain->addAction($action);
}
return $curtain;
}
private function loadHistoryDiffStatus(array $diffs) {
assert_instances_of($diffs, 'DifferentialDiff');
$diff_phids = mpull($diffs, 'getPHID');
$bad_unit_status = array(
ArcanistUnitTestResult::RESULT_FAIL,
ArcanistUnitTestResult::RESULT_BROKEN,
);
$message = new HarbormasterBuildUnitMessage();
$target = new HarbormasterBuildTarget();
$build = new HarbormasterBuild();
$buildable = new HarbormasterBuildable();
$broken_diffs = queryfx_all(
$message->establishConnection('r'),
'SELECT distinct a.buildablePHID
FROM %T m
JOIN %T t ON m.buildTargetPHID = t.phid
JOIN %T b ON t.buildPHID = b.phid
JOIN %T a ON b.buildablePHID = a.phid
WHERE a.buildablePHID IN (%Ls)
AND m.result in (%Ls)',
$message->getTableName(),
$target->getTableName(),
$build->getTableName(),
$buildable->getTableName(),
$diff_phids,
$bad_unit_status);
$unit_status = array();
foreach ($broken_diffs as $broken) {
$phid = $broken['buildablePHID'];
$unit_status[$phid] = DifferentialUnitStatus::UNIT_FAIL;
}
return $unit_status;
}
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(
PhabricatorRepository $repository,
array $visible_changesets) {
assert_instances_of($visible_changesets, 'DifferentialChangeset');
$engine = PhabricatorSyntaxHighlighter::newEngine();
$langs = $repository->getSymbolLanguages();
$langs = nonempty($langs, array());
$sources = $repository->getSymbolSources();
$sources = nonempty($sources, array());
$symbol_indexes = array();
if ($langs && $sources) {
$have_symbols = id(new DiffusionSymbolQuery())
->existsSymbolsInRepository($repository->getPHID());
if (!$have_symbols) {
return $symbol_indexes;
}
}
$repository_phids = array_merge(
array($repository->getPHID()),
$sources);
$indexed_langs = array_fill_keys($langs, true);
foreach ($visible_changesets as $key => $changeset) {
$lang = $engine->getLanguageFromFilename($changeset->getFilename());
if (empty($indexed_langs) || isset($indexed_langs[$lang])) {
$symbol_indexes[$key] = array(
'lang' => $lang,
'repositories' => $repository_phids,
);
}
}
return $symbol_indexes;
}
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();
}
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
$query = id(new DifferentialRevisionQuery())
->setViewer($this->getRequest()->getUser())
->withIsOpen(true)
->withUpdatedEpochBetween($recent, null)
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
->setLimit(10)
->needFlags(true)
->needDrafts(true)
->needReviewers(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');
$viewer = $this->getViewer();
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Similar Revisions'));
$view = id(new DifferentialRevisionListView())
->setRevisions($revisions)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setNoBox(true)
->setUser($viewer);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
return $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->getViewer();
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';
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file = PhabricatorFile::newFromFileData(
$raw_diff,
array(
'name' => $file_name,
'ttl.relative' => phutil_units('24 hours in seconds'),
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
));
$file->attachToObject($revision->getPHID());
unset($unguarded);
return $file->getRedirectResponse();
}
private function buildTransactions(
DifferentialRevision $revision,
DifferentialDiff $left_diff,
DifferentialDiff $right_diff,
array $old_ids,
array $new_ids) {
$timeline = $this->buildTransactionTimeline(
$revision,
new DifferentialTransactionQuery(),
$engine = null,
array(
'left' => $left_diff->getID(),
'right' => $right_diff->getID(),
'old' => implode(',', $old_ids),
'new' => implode(',', $new_ids),
));
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;
}
private function buildDiffDetailView(
array $diffs,
DifferentialRevision $revision,
PhabricatorCustomFieldList $field_list) {
$viewer = $this->getViewer();
$fields = array();
foreach ($field_list->getFields() as $field) {
if ($field->shouldAppearInDiffPropertyView()) {
$fields[] = $field;
}
}
if (!$fields) {
return null;
}
$property_lists = array();
foreach ($this->getDiffTabLabels($diffs) as $tab) {
list($label, $diff) = $tab;
$property_lists[] = array(
$label,
$this->buildDiffPropertyList($diff, $revision, $fields),
);
}
$tab_group = id(new PHUITabGroupView())
->setHideSingleTab(true);
foreach ($property_lists as $key => $property_list) {
list($tab_name, $list_view) = $property_list;
$tab = id(new PHUITabView())
->setKey($key)
->setName($tab_name)
->appendChild($list_view);
$tab_group->addTab($tab);
$tab_group->selectTab($key);
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Diff Detail'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setUser($viewer)
->addTabGroup($tab_group);
}
private function buildDiffPropertyList(
DifferentialDiff $diff,
DifferentialRevision $revision,
array $fields) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($diff);
foreach ($fields as $field) {
$label = $field->renderDiffPropertyViewLabel($diff);
$value = $field->renderDiffPropertyViewValue($diff);
if ($value !== null) {
$view->addProperty($label, $value);
}
}
return $view;
}
private function buildOperationsBox(DifferentialRevision $revision) {
$viewer = $this->getViewer();
// Save a query if we can't possibly have pending operations.
$repository = $revision->getRepository();
if (!$repository || !$repository->canPerformAutomation()) {
return null;
}
$operations = id(new DrydockRepositoryOperationQuery())
->setViewer($viewer)
->withObjectPHIDs(array($revision->getPHID()))
->withIsDismissed(false)
->withOperationTypes(
array(
DrydockLandRepositoryOperation::OPCONST,
))
->execute();
if (!$operations) {
return null;
}
$state_fail = DrydockRepositoryOperation::STATE_FAIL;
// We're going to show the oldest operation which hasn't failed, or the
// most recent failure if they're all failures.
$operations = msort($operations, 'getID');
foreach ($operations as $operation) {
if ($operation->getOperationState() != $state_fail) {
break;
}
}
// If we found a completed operation, don't render anything. We don't want
// to show an older error after the thing worked properly.
if ($operation->isDone()) {
return null;
}
$box_view = id(new PHUIObjectBoxView())
->setHeaderText(pht('Active Operations'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
return id(new DrydockRepositoryOperationStatusView())
->setUser($viewer)
->setBoxView($box_view)
->setOperation($operation);
}
private function buildUnitMessagesView(
$diff,
DifferentialRevision $revision) {
$viewer = $this->getViewer();
if (!$diff->getBuildable()) {
return null;
}
if (!$diff->getUnitMessages()) {
return null;
}
$interesting_messages = array();
foreach ($diff->getUnitMessages() as $message) {
switch ($message->getResult()) {
case ArcanistUnitTestResult::RESULT_PASS:
case ArcanistUnitTestResult::RESULT_SKIP:
break;
default:
$interesting_messages[] = $message;
break;
}
}
if (!$interesting_messages) {
return null;
}
$excuse = null;
if ($diff->hasDiffProperty('arc:unit-excuse')) {
$excuse = $diff->getProperty('arc:unit-excuse');
}
return id(new HarbormasterUnitSummaryView())
->setUser($viewer)
->setExcuse($excuse)
->setBuildable($diff->getBuildable())
->setUnitMessages($diff->getUnitMessages())
->setLimit(5)
->setShowViewAll(true);
}
}
diff --git a/src/applications/differential/storage/DifferentialReviewer.php b/src/applications/differential/storage/DifferentialReviewer.php
index 3aa9bf6362..9df149e788 100644
--- a/src/applications/differential/storage/DifferentialReviewer.php
+++ b/src/applications/differential/storage/DifferentialReviewer.php
@@ -1,127 +1,137 @@
<?php
final class DifferentialReviewer
extends DifferentialDAO {
protected $revisionPHID;
protected $reviewerPHID;
protected $reviewerStatus;
protected $lastActionDiffPHID;
protected $lastCommentDiffPHID;
protected $lastActorPHID;
protected $voidedPHID;
private $authority = array();
+ private $changesets = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_COLUMN_SCHEMA => array(
'reviewerStatus' => 'text64',
'lastActionDiffPHID' => 'phid?',
'lastCommentDiffPHID' => 'phid?',
'lastActorPHID' => 'phid?',
'voidedPHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_revision' => array(
'columns' => array('revisionPHID', 'reviewerPHID'),
'unique' => true,
),
'key_reviewer' => array(
'columns' => array('reviewerPHID', 'revisionPHID'),
),
),
) + parent::getConfiguration();
}
public function isUser() {
$user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
return (phid_get_type($this->getReviewerPHID()) == $user_type);
}
public function isPackage() {
$package_type = PhabricatorOwnersPackagePHIDType::TYPECONST;
return (phid_get_type($this->getReviewerPHID()) == $package_type);
}
public function attachAuthority(PhabricatorUser $user, $has_authority) {
$this->authority[$user->getCacheFragment()] = $has_authority;
return $this;
}
public function hasAuthority(PhabricatorUser $viewer) {
$cache_fragment = $viewer->getCacheFragment();
return $this->assertAttachedKey($this->authority, $cache_fragment);
}
+ public function attachChangesets(array $changesets) {
+ $this->changesets = $changesets;
+ return $this;
+ }
+
+ public function getChangesets() {
+ return $this->assertAttached($this->changesets);
+ }
+
public function isResigned() {
$status_resigned = DifferentialReviewerStatus::STATUS_RESIGNED;
return ($this->getReviewerStatus() == $status_resigned);
}
public function isRejected($diff_phid) {
$status_rejected = DifferentialReviewerStatus::STATUS_REJECTED;
if ($this->getReviewerStatus() != $status_rejected) {
return false;
}
if ($this->getVoidedPHID()) {
return false;
}
if ($this->isCurrentAction($diff_phid)) {
return true;
}
return false;
}
public function isAccepted($diff_phid) {
$status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED;
if ($this->getReviewerStatus() != $status_accepted) {
return false;
}
// If this accept has been voided (for example, but a reviewer using
// "Request Review"), don't count it as a real "Accept" even if it is
// against the current diff PHID.
if ($this->getVoidedPHID()) {
return false;
}
if ($this->isCurrentAction($diff_phid)) {
return true;
}
$sticky_key = 'differential.sticky-accept';
$is_sticky = PhabricatorEnv::getEnvConfig($sticky_key);
if ($is_sticky) {
return true;
}
return false;
}
private function isCurrentAction($diff_phid) {
if (!$diff_phid) {
return true;
}
$action_phid = $this->getLastActionDiffPHID();
if (!$action_phid) {
return true;
}
if ($action_phid == $diff_phid) {
return true;
}
return false;
}
}
diff --git a/src/applications/differential/view/DifferentialReviewersView.php b/src/applications/differential/view/DifferentialReviewersView.php
index 3fcb1c0ccf..33aad25289 100644
--- a/src/applications/differential/view/DifferentialReviewersView.php
+++ b/src/applications/differential/view/DifferentialReviewersView.php
@@ -1,180 +1,186 @@
<?php
final class DifferentialReviewersView extends AphrontView {
private $reviewers;
private $handles;
private $diff;
public function setReviewers(array $reviewers) {
assert_instances_of($reviewers, 'DifferentialReviewer');
$this->reviewers = $reviewers;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function setActiveDiff(DifferentialDiff $diff) {
$this->diff = $diff;
return $this;
}
public function render() {
$viewer = $this->getUser();
$reviewers = $this->reviewers;
$view = new PHUIStatusListView();
// Move resigned reviewers to the bottom.
$head = array();
$tail = array();
foreach ($reviewers as $key => $reviewer) {
if ($reviewer->isResigned()) {
$tail[$key] = $reviewer;
} else {
$head[$key] = $reviewer;
}
}
$reviewers = $head + $tail;
foreach ($reviewers as $reviewer) {
$phid = $reviewer->getReviewerPHID();
$handle = $this->handles[$phid];
$action_phid = $reviewer->getLastActionDiffPHID();
$is_current_action = $this->isCurrent($action_phid);
$comment_phid = $reviewer->getLastCommentDiffPHID();
$is_current_comment = $this->isCurrent($comment_phid);
$item = new PHUIStatusItemView();
$item->setHighlighted($reviewer->hasAuthority($viewer));
// If someone other than the reviewer acted on the reviewer's behalf,
// show who is responsible for the current state. This is usually a
// user accepting for a package or project.
$authority_phid = $reviewer->getLastActorPHID();
if ($authority_phid && ($authority_phid !== $phid)) {
$authority_name = $viewer->renderHandle($authority_phid)
->setAsText(true);
} else {
$authority_name = null;
}
switch ($reviewer->getReviewerStatus()) {
case DifferentialReviewerStatus::STATUS_ADDED:
if ($comment_phid) {
if ($is_current_comment) {
$icon = 'fa-comment';
$color = 'blue';
$label = pht('Commented');
} else {
$icon = 'fa-comment-o';
$color = 'bluegrey';
$label = pht('Commented Previously');
}
} else {
$icon = PHUIStatusItemView::ICON_OPEN;
$color = 'bluegrey';
$label = pht('Review Requested');
}
break;
case DifferentialReviewerStatus::STATUS_ACCEPTED:
if ($is_current_action) {
$icon = PHUIStatusItemView::ICON_ACCEPT;
$color = 'green';
if ($authority_name !== null) {
$label = pht('Accepted (by %s)', $authority_name);
} else {
$label = pht('Accepted');
}
} else {
$icon = 'fa-check-circle-o';
$color = 'bluegrey';
if ($authority_name !== null) {
$label = pht('Accepted Prior Diff (by %s)', $authority_name);
} else {
$label = pht('Accepted Prior Diff');
}
}
break;
case DifferentialReviewerStatus::STATUS_REJECTED:
if ($is_current_action) {
$icon = PHUIStatusItemView::ICON_REJECT;
$color = 'red';
if ($authority_name !== null) {
$label = pht('Requested Changes (by %s)', $authority_name);
} else {
$label = pht('Requested Changes');
}
} else {
$icon = 'fa-times-circle-o';
$color = 'red';
if ($authority_name !== null) {
$label = pht(
'Requested Changes to Prior Diff (by %s)',
$authority_name);
} else {
$label = pht('Requested Changes to Prior Diff');
}
}
break;
case DifferentialReviewerStatus::STATUS_BLOCKING:
$icon = PHUIStatusItemView::ICON_MINUS;
$color = 'red';
$label = pht('Blocking Review');
break;
case DifferentialReviewerStatus::STATUS_RESIGNED:
$icon = 'fa-times';
$color = 'grey';
$label = pht('Resigned');
break;
default:
$icon = PHUIStatusItemView::ICON_QUESTION;
$color = 'bluegrey';
$label = pht('Unknown ("%s")', $reviewer->getReviewerStatus());
break;
}
$item->setIcon($icon, $color, $label);
$item->setTarget($handle->renderHovercardLink());
+ if ($reviewer->isPackage()) {
+ if (!$reviewer->getChangesets()) {
+ $item->setNote(pht('(Owns No Changed Paths)'));
+ }
+ }
+
$view->addItem($item);
}
return $view;
}
private function isCurrent($action_phid) {
if (!$this->diff) {
return true;
}
if (!$action_phid) {
return true;
}
$diff_phid = $this->diff->getPHID();
if (!$diff_phid) {
return true;
}
if ($diff_phid == $action_phid) {
return true;
}
return false;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, May 1, 9:00 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
108930
Default Alt Text
(60 KB)

Event Timeline