1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-24 20:49:06 +01:00

(stable) Promote 2019 Week 44

This commit is contained in:
epriestley 2019-11-03 18:06:52 -08:00
commit 7585b1212b
22 changed files with 696 additions and 185 deletions

View file

@ -9,10 +9,10 @@ return array(
'names' => array(
'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => '686ae87c',
'core.pkg.css' => '77de226f',
'core.pkg.js' => '6e5c894f',
'differential.pkg.css' => '607c84be',
'differential.pkg.js' => 'a0212a0b',
'differential.pkg.js' => '1b97518d',
'diffusion.pkg.css' => '42c75c37',
'diffusion.pkg.js' => 'a98c0bf7',
'maniphest.pkg.css' => '35995d6d',
@ -30,7 +30,7 @@ return array(
'rsrc/css/aphront/notification.css' => '30240bd2',
'rsrc/css/aphront/panel-view.css' => '46923d46',
'rsrc/css/aphront/phabricator-nav-view.css' => 'f8a0c1bf',
'rsrc/css/aphront/table-view.css' => '5f13a9e4',
'rsrc/css/aphront/table-view.css' => '0bb61df1',
'rsrc/css/aphront/tokenizer.css' => 'b52d0668',
'rsrc/css/aphront/tooltip.css' => 'e3f2412f',
'rsrc/css/aphront/typeahead-browse.css' => 'b7ed02d2',
@ -428,7 +428,7 @@ return array(
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'aa3a100c',
'rsrc/js/application/repository/repository-crossreference.js' => 'c15122b4',
'rsrc/js/application/repository/repository-crossreference.js' => '1c95ea63',
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f',
'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2',
@ -535,7 +535,7 @@ return array(
'aphront-list-filter-view-css' => 'feb64255',
'aphront-multi-column-view-css' => 'fbc00ba3',
'aphront-panel-view-css' => '46923d46',
'aphront-table-view-css' => '5f13a9e4',
'aphront-table-view-css' => '0bb61df1',
'aphront-tokenizer-control-css' => 'b52d0668',
'aphront-tooltip-css' => 'e3f2412f',
'aphront-typeahead-control-css' => '8779483d',
@ -682,7 +682,7 @@ return array(
'javelin-behavior-reorder-applications' => 'aa371860',
'javelin-behavior-reorder-columns' => '8ac32fd9',
'javelin-behavior-reorder-profile-menu-items' => 'e5bdb730',
'javelin-behavior-repository-crossreference' => 'c15122b4',
'javelin-behavior-repository-crossreference' => '1c95ea63',
'javelin-behavior-scrollbar' => '92388bae',
'javelin-behavior-search-reorder-queries' => 'b86f297f',
'javelin-behavior-select-content' => 'e8240b50',
@ -1034,6 +1034,12 @@ return array(
'javelin-install',
'javelin-util',
),
'1c95ea63' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'1cab0e9a' => array(
'javelin-behavior',
'javelin-dom',
@ -1977,12 +1983,6 @@ return array(
'c03f2fb4' => array(
'javelin-install',
),
'c15122b4' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'c2c500a7' => array(
'javelin-install',
'javelin-dom',

View file

@ -0,0 +1,4 @@
<?php
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
'PhabricatorRepositoryQuery');

View file

@ -1338,6 +1338,8 @@ phutil_register_library_map(array(
'HarbormasterArcLintBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php',
'HarbormasterArcUnitBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcUnitBuildStepImplementation.php',
'HarbormasterArtifact' => 'applications/harbormaster/artifact/HarbormasterArtifact.php',
'HarbormasterArtifactSearchConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterArtifactSearchConduitAPIMethod.php',
'HarbormasterArtifactSearchEngine' => 'applications/harbormaster/query/HarbormasterArtifactSearchEngine.php',
'HarbormasterAutotargetsTestCase' => 'applications/harbormaster/__tests__/HarbormasterAutotargetsTestCase.php',
'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php',
'HarbormasterBuildAbortedException' => 'applications/harbormaster/exception/HarbormasterBuildAbortedException.php',
@ -4650,6 +4652,7 @@ phutil_register_library_map(array(
'PhabricatorSearchHovercardController' => 'applications/search/controller/PhabricatorSearchHovercardController.php',
'PhabricatorSearchIndexVersion' => 'applications/search/storage/PhabricatorSearchIndexVersion.php',
'PhabricatorSearchIndexVersionDestructionEngineExtension' => 'applications/search/engineextension/PhabricatorSearchIndexVersionDestructionEngineExtension.php',
'PhabricatorSearchIntField' => 'applications/search/field/PhabricatorSearchIntField.php',
'PhabricatorSearchManagementIndexWorkflow' => 'applications/search/management/PhabricatorSearchManagementIndexWorkflow.php',
'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php',
'PhabricatorSearchManagementNgramsWorkflow' => 'applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php',
@ -7369,6 +7372,8 @@ phutil_register_library_map(array(
'HarbormasterArcLintBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterArcUnitBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterArtifact' => 'Phobject',
'HarbormasterArtifactSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'HarbormasterArtifactSearchEngine' => 'PhabricatorApplicationSearchEngine',
'HarbormasterAutotargetsTestCase' => 'PhabricatorTestCase',
'HarbormasterBuild' => array(
'HarbormasterDAO',
@ -7384,6 +7389,7 @@ phutil_register_library_map(array(
'HarbormasterDAO',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorConduitResultInterface',
),
'HarbormasterBuildArtifactPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
@ -11268,6 +11274,7 @@ phutil_register_library_map(array(
'PhabricatorSearchHovercardController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchIndexVersion' => 'PhabricatorSearchDAO',
'PhabricatorSearchIndexVersionDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
'PhabricatorSearchIntField' => 'PhabricatorSearchField',
'PhabricatorSearchManagementIndexWorkflow' => 'PhabricatorSearchManagementWorkflow',
'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow',
'PhabricatorSearchManagementNgramsWorkflow' => 'PhabricatorSearchManagementWorkflow',

View file

@ -44,15 +44,6 @@ final class DifferentialRevisionPHIDType extends PhabricatorPHIDType {
if ($revision->isClosed()) {
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
}
$icon = $revision->getStatusIcon();
$color = $revision->getStatusIconColor();
$name = $revision->getStatusDisplayName();
$handle
->setStateIcon($icon)
->setStateColor($color)
->setStateName($name);
}
}

View file

@ -26,20 +26,45 @@ final class DiffusionHovercardEngineExtension
$viewer = $this->getViewer();
$author_phid = $commit->getAuthorPHID();
if ($author_phid) {
$author = $viewer->renderHandle($author_phid);
} else {
$commit_data = $commit->loadCommitData();
$author = phutil_tag('em', array(), $commit_data->getAuthorName());
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->needIdentities(true)
->needCommitData(true)
->withPHIDs(array($commit->getPHID()))
->executeOne();
if (!$commit) {
return;
}
$author_phid = $commit->getAuthorDisplayPHID();
$committer_phid = $commit->getCommitterDisplayPHID();
$repository_phid = $commit->getRepository()->getPHID();
$phids = array();
$phids[] = $author_phid;
$phids[] = $committer_phid;
$phids[] = $repository_phid;
$handles = $viewer->loadHandles($phids);
$hovercard->setTitle($handle->getName());
$hovercard->setDetail($commit->getSummary());
$hovercard->addField(pht('Author'), $author);
$hovercard->addField(pht('Date'),
phabricator_date($commit->getEpoch(), $viewer));
$repository = $handles[$repository_phid]->renderLink();
$hovercard->addField(pht('Repository'), $repository);
$author = $handles[$author_phid]->renderLink();
if ($author_phid) {
$hovercard->addField(pht('Author'), $author);
}
if ($committer_phid && ($committer_phid !== $author_phid)) {
$committer = $handles[$committer_phid]->renderLink();
$hovercard->addField(pht('Committer'), $committer);
}
$date = phabricator_date($commit->getEpoch(), $viewer);
$hovercard->addField(pht('Date'), $date);
if (!$commit->isAuditStatusNoAudit()) {
$status = $commit->getAuditStatusObject();

View file

@ -29,6 +29,7 @@ final class PhabricatorFactChartFunction
$key_id = id(new PhabricatorFactKeyDimension())
->newDimensionID($fact->getKey());
if (!$key_id) {
$this->map = array();
return;
}

View file

@ -0,0 +1,18 @@
<?php
final class HarbormasterArtifactSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'harbormaster.artifact.search';
}
public function newSearchEngine() {
return new HarbormasterArtifactSearchEngine();
}
public function getMethodSummary() {
return pht('Query information about build artifacts.');
}
}

View file

@ -0,0 +1,93 @@
<?php
final class HarbormasterArtifactSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Harbormaster Artifacts');
}
public function getApplicationClassName() {
return 'PhabricatorHarbormasterApplication';
}
public function newQuery() {
return new HarbormasterBuildArtifactQuery();
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorPHIDsSearchField())
->setLabel(pht('Targets'))
->setKey('buildTargetPHIDs')
->setAliases(
array(
'buildTargetPHID',
'buildTargets',
'buildTarget',
'targetPHIDs',
'targetPHID',
'targets',
'target',
))
->setDescription(
pht('Search for artifacts attached to particular build targets.')),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['buildTargetPHIDs']) {
$query->withBuildTargetPHIDs($map['buildTargetPHIDs']);
}
return $query;
}
protected function getURI($path) {
return '/harbormaster/artifact/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'all' => pht('All Artifacts'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $artifacts,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($artifacts, 'HarbormasterBuildArtifact');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
foreach ($artifacts as $artifact) {
$id = $artifact->getID();
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Artifact %d', $id));
$list->addItem($item);
}
return id(new PhabricatorApplicationSearchResultView())
->setObjectList($list)
->setNoDataString(pht('No artifacts found.'));
}
}

View file

@ -4,7 +4,8 @@ final class HarbormasterBuildArtifact
extends HarbormasterDAO
implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorConduitResultInterface {
protected $buildTargetPHID;
protected $artifactType;
@ -18,6 +19,7 @@ final class HarbormasterBuildArtifact
public static function initializeNewBuildArtifact(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildArtifact())
->attachBuildTarget($build_target)
->setBuildTargetPHID($build_target->getPHID());
@ -53,9 +55,8 @@ final class HarbormasterBuildArtifact
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildArtifactPHIDType::TYPECONST);
public function getPHIDType() {
return HarbormasterBuildArtifactPHIDType::TYPECONST;
}
public function attachBuildTarget(HarbormasterBuildTarget $build_target) {
@ -147,7 +148,8 @@ final class HarbormasterBuildArtifact
}
public function describeAutomaticCapability($capability) {
return pht('Users must be able to see a buildable to see its artifacts.');
return pht(
'Users must be able to see a build target to see its artifacts.');
}
@ -165,4 +167,40 @@ final class HarbormasterBuildArtifact
$this->saveTransaction();
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('buildTargetPHID')
->setType('phid')
->setDescription(pht('The build target this artifact is attached to.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('artifactType')
->setType('string')
->setDescription(pht('The artifact type.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('artifactKey')
->setType('string')
->setDescription(pht('The artifact key.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('isReleased')
->setType('bool')
->setDescription(pht('True if this artifact has been released.')),
);
}
public function getFieldValuesForConduit() {
return array(
'buildTargetPHID' => $this->getBuildTargetPHID(),
'artifactType' => $this->getArtifactType(),
'artifactKey' => $this->getArtifactKey(),
'isReleased' => (bool)$this->getIsReleased(),
);
}
public function getConduitSearchAttachments() {
return array();
}
}

View file

@ -179,11 +179,14 @@ final class ManiphestTaskDetailController extends ManiphestController {
->addTabGroup($tab_group);
}
$changes_view = $this->newChangesView($task, $edges);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(
array(
$changes_view,
$tab_view,
$timeline,
$comment_view,
@ -395,58 +398,6 @@ final class ManiphestTaskDetailController extends ManiphestController {
$source));
}
$edge_types = array(
ManiphestTaskHasRevisionEdgeType::EDGECONST
=> pht('Differential Revisions'),
);
$revisions_commits = array();
$commit_phids = array_keys(
$edges[ManiphestTaskHasCommitEdgeType::EDGECONST]);
if ($commit_phids) {
$commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST;
$drev_edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($commit_phids)
->withEdgeTypes(array($commit_drev))
->execute();
foreach ($commit_phids as $phid) {
$revisions_commits[$phid] = $handles->renderHandle($phid)
->setShowHovercard(true)
->setShowStateIcon(true);
$revision_phid = key($drev_edges[$phid][$commit_drev]);
$revision_handle = $handles->getHandleIfExists($revision_phid);
if ($revision_handle) {
$task_drev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
unset($edges[$task_drev][$revision_phid]);
$revisions_commits[$phid] = hsprintf(
'%s / %s',
$revision_handle->renderHovercardLink($revision_handle->getName()),
$revisions_commits[$phid]);
}
}
}
foreach ($edge_types as $edge_type => $edge_name) {
if (!$edges[$edge_type]) {
continue;
}
$edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type]));
$edge_list = $edge_handles->renderList()
->setShowStateIcons(true);
$view->addProperty($edge_name, $edge_list);
}
if ($revisions_commits) {
$view->addProperty(
pht('Commits'),
phutil_implode_html(phutil_tag('br'), $revisions_commits));
}
$field_list->appendFieldsToPropertyList(
$task,
$viewer,
@ -596,5 +547,291 @@ final class ManiphestTaskDetailController extends ManiphestController {
return $handles->newSublist($phids);
}
private function newChangesView(ManiphestTask $task, array $edges) {
$viewer = $this->getViewer();
$revision_type = ManiphestTaskHasRevisionEdgeType::EDGECONST;
$commit_type = ManiphestTaskHasCommitEdgeType::EDGECONST;
$revision_phids = idx($edges, $revision_type, array());
$revision_phids = array_keys($revision_phids);
$revision_phids = array_fuse($revision_phids);
$commit_phids = idx($edges, $commit_type, array());
$commit_phids = array_keys($commit_phids);
$commit_phids = array_fuse($commit_phids);
if (!$revision_phids && !$commit_phids) {
return null;
}
if ($commit_phids) {
$link_type = DiffusionCommitHasRevisionEdgeType::EDGECONST;
$link_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($commit_phids)
->withEdgeTypes(array($link_type));
$link_query->execute();
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withPHIDs($commit_phids)
->execute();
$commits = mpull($commits, null, 'getPHID');
} else {
$commits = array();
}
if ($revision_phids) {
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withPHIDs($revision_phids)
->execute();
$revisions = mpull($revisions, null, 'getPHID');
} else {
$revisions = array();
}
$handle_phids = array();
$any_linked = false;
$any_status = false;
$idx = 0;
$objects = array();
foreach ($commit_phids as $commit_phid) {
$handle_phids[] = $commit_phid;
$link_phids = $link_query->getDestinationPHIDs(array($commit_phid));
foreach ($link_phids as $link_phid) {
$handle_phids[] = $link_phid;
unset($revision_phids[$link_phid]);
$any_linked = true;
}
$commit = idx($commits, $commit_phid);
if ($commit) {
$repository_phid = $commit->getRepository()->getPHID();
$handle_phids[] = $repository_phid;
} else {
$repository_phid = null;
}
$status_view = null;
if ($commit) {
$status = $commit->getAuditStatusObject();
if (!$status->isNoAudit()) {
$status_view = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setIcon($status->getIcon())
->setColor($status->getColor())
->setName($status->getName());
}
}
$object_link = null;
if ($commit) {
$commit_monogram = $commit->getDisplayName();
$commit_monogram = phutil_tag(
'span',
array(
'class' => 'object-name',
),
$commit_monogram);
$commit_link = javelin_tag(
'a',
array(
'href' => $commit->getURI(),
'sigil' => 'hovercard',
'meta' => array(
'hoverPHID' => $commit->getPHID(),
),
),
$commit->getSummary());
$object_link = array(
$commit_monogram,
' ',
$commit_link,
);
}
$objects[] = array(
'objectPHID' => $commit_phid,
'objectLink' => $object_link,
'repositoryPHID' => $repository_phid,
'revisionPHIDs' => $link_phids,
'status' => $status_view,
'order' => id(new PhutilSortVector())
->addInt($repository_phid ? 1 : 0)
->addString((string)$repository_phid)
->addInt(1)
->addInt($idx++),
);
}
foreach ($revision_phids as $revision_phid) {
$handle_phids[] = $revision_phid;
$revision = idx($revisions, $revision_phid);
if ($revision) {
$repository_phid = $revision->getRepositoryPHID();
$handle_phids[] = $repository_phid;
} else {
$repository_phid = null;
}
if ($revision) {
$icon = $revision->getStatusIcon();
$color = $revision->getStatusIconColor();
$name = $revision->getStatusDisplayName();
$status_view = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setIcon($icon)
->setColor($color)
->setName($name);
} else {
$status_view = null;
}
$object_link = null;
if ($revision) {
$revision_monogram = $revision->getMonogram();
$revision_monogram = phutil_tag(
'span',
array(
'class' => 'object-name',
),
$revision_monogram);
$revision_link = javelin_tag(
'a',
array(
'href' => $revision->getURI(),
'sigil' => 'hovercard',
'meta' => array(
'hoverPHID' => $revision->getPHID(),
),
),
$revision->getTitle());
$object_link = array(
$revision_monogram,
' ',
$revision_link,
);
}
$objects[] = array(
'objectPHID' => $revision_phid,
'objectLink' => $object_link,
'repositoryPHID' => $repository_phid,
'revisionPHIDs' => array(),
'status' => $status_view,
'order' => id(new PhutilSortVector())
->addInt($repository_phid ? 1 : 0)
->addString((string)$repository_phid)
->addInt(0)
->addInt($idx++),
);
}
$handles = $viewer->loadHandles($handle_phids);
$order = ipull($objects, 'order');
$order = msortv($order, 'getSelf');
$objects = array_select_keys($objects, array_keys($order));
$last_repository = false;
$rows = array();
$rowd = array();
foreach ($objects as $object) {
$repository_phid = $object['repositoryPHID'];
if ($repository_phid !== $last_repository) {
$repository_link = null;
if ($repository_phid) {
$repository_handle = $handles[$repository_phid];
$rows[] = array(
$repository_handle->renderLink(),
);
$rowd[] = true;
}
$last_repository = $repository_phid;
}
$object_phid = $object['objectPHID'];
$handle = $handles[$object_phid];
$object_link = $object['objectLink'];
if ($object_link === null) {
$object_link = $handle->renderLink();
}
$object_icon = id(new PHUIIconView())
->setIcon($handle->getIcon());
$status_view = $object['status'];
if ($status_view) {
$any_status = true;
}
$revision_tags = array();
foreach ($object['revisionPHIDs'] as $link_phid) {
$revision_handle = $handles[$link_phid];
$revision_name = $revision_handle->getName();
$revision_tags[] = $revision_handle
->renderHovercardLink($revision_name);
}
$revision_tags = phutil_implode_html(
phutil_tag('br'),
$revision_tags);
$rowd[] = false;
$rows[] = array(
$object_icon,
$status_view,
$revision_tags,
$object_link,
);
}
$changes_table = id(new AphrontTableView($rows))
->setNoDataString(pht('This task has no related commits or revisions.'))
->setRowDividers($rowd)
->setColumnClasses(
array(
'indent center',
null,
null,
'wide pri object-link',
))
->setColumnVisibility(
array(
true,
$any_status,
$any_linked,
true,
))
->setDeviceVisibility(
array(
false,
$any_status,
false,
true,
));
$changes_header = id(new PHUIHeaderView())
->setHeader(pht('Revisions and Commits'));
$changes_view = id(new PHUIObjectBoxView())
->setHeader($changes_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($changes_table);
return $changes_view;
}
}

View file

@ -33,10 +33,6 @@ final class PhabricatorObjectHandle
private $commandLineObjectName;
private $mailStampName;
private $stateIcon;
private $stateColor;
private $stateName;
public function setIcon($icon) {
$this->icon = $icon;
return $this;
@ -299,55 +295,6 @@ final class PhabricatorObjectHandle
return $this->complete;
}
public function setStateIcon($state_icon) {
$this->stateIcon = $state_icon;
return $this;
}
public function getStateIcon() {
return $this->stateIcon;
}
public function setStateColor($state_color) {
$this->stateColor = $state_color;
return $this;
}
public function getStateColor() {
return $this->stateColor;
}
public function setStateName($state_name) {
$this->stateName = $state_name;
return $this;
}
public function getStateName() {
return $this->stateName;
}
public function renderStateIcon() {
$icon = $this->getStateIcon();
if ($icon === null) {
$icon = 'fa-question-circle-o';
}
$color = $this->getStateColor();
$name = $this->getStateName();
if ($name === null) {
$name = pht('Unknown');
}
return id(new PHUIIconView())
->setIcon($icon, $color)
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => $name,
));
}
public function renderLink($name = null) {
return $this->renderLinkWithAttributes($name, array());
}

View file

@ -13,7 +13,6 @@ final class PHUIHandleListView
private $handleList;
private $asInline;
private $asText;
private $showStateIcons;
private $glyphLimit;
public function setHandleList(PhabricatorHandleList $list) {
@ -39,15 +38,6 @@ final class PHUIHandleListView
return $this->asText;
}
public function setShowStateIcons($show_state_icons) {
$this->showStateIcons = $show_state_icons;
return $this;
}
public function getShowStateIcons() {
return $this->showStateIcons;
}
public function setGlyphLimit($glyph_limit) {
$this->glyphLimit = $glyph_limit;
return $this;
@ -70,7 +60,6 @@ final class PHUIHandleListView
protected function getTagContent() {
$list = $this->handleList;
$show_state_icons = $this->getShowStateIcons();
$glyph_limit = $this->getGlyphLimit();
$items = array();
@ -79,10 +68,6 @@ final class PHUIHandleListView
->setShowHovercard(true)
->setAsText($this->getAsText());
if ($show_state_icons) {
$view->setShowStateIcon(true);
}
if ($glyph_limit) {
$view->setGlyphLimit($glyph_limit);
}

View file

@ -17,7 +17,6 @@ final class PHUIHandleView
private $asText;
private $useShortName;
private $showHovercard;
private $showStateIcon;
private $glyphLimit;
public function setHandleList(PhabricatorHandleList $list) {
@ -50,15 +49,6 @@ final class PHUIHandleView
return $this;
}
public function setShowStateIcon($show_state_icon) {
$this->showStateIcon = $show_state_icon;
return $this;
}
public function getShowStateIcon() {
return $this->showStateIcon;
}
public function setGlyphLimit($glyph_limit) {
$this->glyphLimit = $glyph_limit;
return $this;
@ -104,11 +94,6 @@ final class PHUIHandleView
$link = $handle->renderLink($name);
}
if ($this->showStateIcon) {
$icon = $handle->renderStateIcon();
$link = array($icon, ' ', $link);
}
return $link;
}

View file

@ -53,7 +53,11 @@ final class PhabricatorProjectBurndownChartEngine
$open_function = $this->newFunction(
array(
'accumulate',
array('fact', 'tasks.open-count.create'),
array(
'sum',
array('fact', 'tasks.open-count.create'),
array('fact', 'tasks.open-count.status'),
),
));
$closed_function = $this->newFunction(

View file

@ -65,6 +65,35 @@ final class PhabricatorProjectSearchEngine
pht(
'Pass true to find only milestones, or false to omit '.
'milestones.')),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Root Projects'))
->setKey('isRoot')
->setOptions(
pht('(Show All)'),
pht('Show Only Root Projects'),
pht('Hide Root Projects'))
->setDescription(
pht(
'Pass true to find only root projects, or false to omit '.
'root projects.')),
id(new PhabricatorSearchIntField())
->setLabel(pht('Minimum Depth'))
->setKey('minDepth')
->setIsHidden(true)
->setDescription(
pht(
'Find projects with a given minimum depth. Root projects '.
'have depth 0, their immediate children have depth 1, and '.
'so on.')),
id(new PhabricatorSearchIntField())
->setLabel(pht('Maximum Depth'))
->setKey('maxDepth')
->setIsHidden(true)
->setDescription(
pht(
'Find projects with a given maximum depth. Root projects '.
'have depth 0, their immediate children have depth 1, and '.
'so on.')),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Subtypes'))
->setKey('subtypes')
@ -137,6 +166,42 @@ final class PhabricatorProjectSearchEngine
$query->withIsMilestone($map['isMilestone']);
}
$min_depth = $map['minDepth'];
$max_depth = $map['maxDepth'];
if ($min_depth !== null || $max_depth !== null) {
if ($min_depth !== null && $max_depth !== null) {
if ($min_depth > $max_depth) {
throw new Exception(
pht(
'Search constraint "minDepth" must be no larger than '.
'search constraint "maxDepth".'));
}
}
}
if ($map['isRoot'] !== null) {
if ($map['isRoot']) {
if ($max_depth === null) {
$max_depth = 0;
} else {
$max_depth = min(0, $max_depth);
}
$query->withDepthBetween(null, 0);
} else {
if ($min_depth === null) {
$min_depth = 1;
} else {
$min_depth = max($min_depth, 1);
}
}
}
if ($min_depth !== null || $max_depth !== null) {
$query->withDepthBetween($min_depth, $max_depth);
}
if ($map['parentPHIDs']) {
$query->withParentProjectPHIDs($map['parentPHIDs']);
}

View file

@ -130,10 +130,35 @@ final class PhabricatorRepositoryURINormalizer extends Phobject {
$domain = $uri->getDomain();
if (!strlen($domain)) {
$domain = '<void>';
return '<void>';
}
return phutil_utf8_strtolower($domain);
$domain = phutil_utf8_strtolower($domain);
// See T13435. If the domain for a repository URI is same as the install
// base URI, store it as a "<base-uri>" token instead of the actual domain
// so that the index does not fall out of date if the install moves.
$base_uri = PhabricatorEnv::getURI('/');
$base_uri = new PhutilURI($base_uri);
$base_domain = $base_uri->getDomain();
$base_domain = phutil_utf8_strtolower($base_domain);
if ($domain === $base_domain) {
return '<base-uri>';
}
// Likewise, store a token for the "SSH Host" domain so it can be changed
// without requiring an index rebuild.
$ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host');
if (strlen($ssh_host)) {
$ssh_host = phutil_utf8_strtolower($ssh_host);
if ($domain === $ssh_host) {
return '<ssh-host>';
}
}
return $domain;
}

View file

@ -31,6 +31,36 @@ final class PhabricatorRepositoryURINormalizerTestCase
}
}
public function testDomainURINormalizer() {
$base_domain = 'base.phabricator.example.com';
$ssh_domain = 'ssh.phabricator.example.com';
$env = PhabricatorEnv::beginScopedEnv();
$env->overrideEnvConfig('phabricator.base-uri', 'http://'.$base_domain);
$env->overrideEnvConfig('diffusion.ssh-host', $ssh_domain);
$cases = array(
'/' => '<void>',
'/path/to/local/repo.git' => '<void>',
'ssh://user@domain.com/path.git' => 'domain.com',
'ssh://user@DOMAIN.COM/path.git' => 'domain.com',
'http://'.$base_domain.'/diffusion/X/' => '<base-uri>',
'ssh://'.$ssh_domain.'/diffusion/X/' => '<ssh-host>',
'git@'.$ssh_domain.':bananas.git' => '<ssh-host>',
);
$type_git = PhabricatorRepositoryURINormalizer::TYPE_GIT;
foreach ($cases as $input => $expect) {
$normal = new PhabricatorRepositoryURINormalizer($type_git, $input);
$this->assertEqual(
$expect,
$normal->getNormalizedDomain(),
pht('Normalized domain for "%s".', $input));
}
}
public function testSVNURINormalizer() {
$cases = array(
'file:///path/to/repo' => 'path/to/repo',

View file

@ -81,16 +81,6 @@ final class PhabricatorRepositoryCommitPHIDType extends PhabricatorPHIDType {
$handle->setFullName($full_name);
$handle->setURI($commit->getURI());
$handle->setTimestamp($commit->getEpoch());
$status = $commit->getAuditStatusObject();
$icon = $status->getIcon();
$color = $status->getColor();
$name = $status->getName();
$handle
->setStateIcon($icon)
->setStateColor($color)
->setStateName($name);
}
}

View file

@ -0,0 +1,22 @@
<?php
final class PhabricatorSearchIntField
extends PhabricatorSearchField {
protected function getDefaultValue() {
return null;
}
protected function getValueFromRequest(AphrontRequest $request, $key) {
return $request->getInt($key);
}
protected function newControl() {
return new AphrontFormTextControl();
}
protected function newConduitParameterType() {
return new ConduitIntParameterType();
}
}

View file

@ -24,6 +24,8 @@ final class AphrontTableView extends AphrontView {
protected $sortValues = array();
private $deviceReadyTable;
private $rowDividers = array();
public function __construct(array $data) {
$this->data = $data;
}
@ -53,6 +55,11 @@ final class AphrontTableView extends AphrontView {
return $this;
}
public function setRowDividers(array $dividers) {
$this->rowDividers = $dividers;
return $this;
}
public function setNoDataString($no_data_string) {
$this->noDataString = $no_data_string;
return $this;
@ -258,10 +265,15 @@ final class AphrontTableView extends AphrontView {
}
}
$dividers = $this->rowDividers;
$data = $this->data;
if ($data) {
$row_num = 0;
$row_idx = 0;
foreach ($data as $row) {
$is_divider = !empty($dividers[$row_num]);
$row_size = count($row);
while (count($row) > count($col_classes)) {
$col_classes[] = null;
@ -289,6 +301,18 @@ final class AphrontTableView extends AphrontView {
$class = trim($class.' '.$this->cellClasses[$row_num][$col_num]);
}
if ($is_divider) {
$tr[] = phutil_tag(
'td',
array(
'class' => 'row-divider',
'colspan' => count($visibility),
),
$value);
$row_idx = -1;
break;
}
$tr[] = phutil_tag(
'td',
array(
@ -299,7 +323,7 @@ final class AphrontTableView extends AphrontView {
}
$class = idx($this->rowClasses, $row_num);
if ($this->zebraStripes && ($row_num % 2)) {
if ($this->zebraStripes && ($row_idx % 2)) {
if ($class !== null) {
$class = 'alt alt-'.$class;
} else {
@ -309,6 +333,7 @@ final class AphrontTableView extends AphrontView {
$table[] = phutil_tag('tr', array('class' => $class), $tr);
++$row_num;
++$row_idx;
}
} else {
$colspan = max(count(array_filter($visibility)), 1);

View file

@ -55,6 +55,16 @@
background-color: {$lightbluebackground};
}
.aphront-table-view td.row-divider {
background-color: {$bluebackground};
font-weight: bold;
padding: 8px 12px;
}
.aphront-table-view td.indent {
padding-left: 24px;
}
.aphront-table-view th {
border-bottom: 1px solid {$thinblueborder};
}

View file

@ -152,7 +152,16 @@ JX.behavior('repository-crossreference', function(config, statics) {
query.char = char;
}
var uri = JX.$U('/diffusion/symbol/' + symbol + '/');
var uri_symbol = symbol;
// In some cases, lexers may include whitespace in symbol tags. Trim it,
// since symbols with semantic whitespace aren't supported.
uri_symbol = uri_symbol.trim();
// See T13437. Symbols like "#define" need to be encoded.
uri_symbol = encodeURIComponent(uri_symbol);
var uri = JX.$U('/diffusion/symbol/' + uri_symbol + '/');
uri.addQueryParams(query);
window.open(uri.toString());