Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php
index e06da5f4e3..8657981836 100644
--- a/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php
+++ b/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php
@@ -1,111 +1,155 @@
<?php
/**
* Expands aggregate mail recipients into their component mailables. For
* example, a project currently expands into all of its members.
*/
final class PhabricatorMetaMTAMemberQuery extends PhabricatorQuery {
private $phids = array();
private $viewer;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function execute() {
+ $viewer = $this->getViewer();
+
$phids = array_fuse($this->phids);
$actors = array();
$type_map = array();
foreach ($phids as $phid) {
$type_map[phid_get_type($phid)][] = $phid;
}
// TODO: Generalize this somewhere else.
+
+ // If we have packages, break them down into their constituent user and
+ // project owners first. Then we'll resolve those and build the packages
+ // back up from the pieces.
+ $package_type = PhabricatorOwnersPackagePHIDType::TYPECONST;
+ $package_phids = idx($type_map, $package_type, array());
+ unset($type_map[$package_type]);
+
+ $package_map = array();
+ if ($package_phids) {
+ $packages = id(new PhabricatorOwnersPackageQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($package_phids)
+ ->execute();
+
+ foreach ($packages as $package) {
+ $package_owners = array();
+ foreach ($package->getOwners() as $owner) {
+ $owner_phid = $owner->getUserPHID();
+ $owner_type = phid_get_type($owner_phid);
+ $type_map[$owner_type][] = $owner_phid;
+ $package_owners[] = $owner_phid;
+ }
+ $package_map[$package->getPHID()] = $package_owners;
+ }
+ }
+
$results = array();
foreach ($type_map as $type => $phids) {
switch ($type) {
case PhabricatorProjectProjectPHIDType::TYPECONST:
// NOTE: We're loading the projects here in order to respect policies.
$projects = id(new PhabricatorProjectQuery())
- ->setViewer($this->getViewer())
+ ->setViewer($viewer)
->withPHIDs($phids)
->needMembers(true)
->needWatchers(true)
->execute();
$edge_type = PhabricatorProjectSilencedEdgeType::EDGECONST;
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($phids)
->withEdgeTypes(
array(
$edge_type,
));
$edge_query->execute();
$projects = mpull($projects, null, 'getPHID');
foreach ($phids as $phid) {
$project = idx($projects, $phid);
if (!$project) {
$results[$phid] = array();
continue;
}
// Recipients are members who haven't silenced the project, plus
// watchers.
$members = $project->getMemberPHIDs();
$members = array_fuse($members);
$watchers = $project->getWatcherPHIDs();
$watchers = array_fuse($watchers);
$silenced = $edge_query->getDestinationPHIDs(
array($phid),
array($edge_type));
$silenced = array_fuse($silenced);
$result_map = array_diff_key($members, $silenced);
$result_map = $result_map + $watchers;
$results[$phid] = array_values($result_map);
}
break;
default:
// For other types, just map the PHID to itself without modification.
// This allows callers to do less work.
foreach ($phids as $phid) {
$results[$phid] = array($phid);
}
break;
}
}
+ // For any packages, stitch them back together from the resolved users
+ // and projects.
+ if ($package_map) {
+ foreach ($package_map as $package_phid => $owner_phids) {
+ $resolved = array();
+ foreach ($owner_phids as $owner_phid) {
+ $resolved_phids = idx($results, $owner_phid, array());
+ foreach ($resolved_phids as $resolved_phid) {
+ $resolved[] = $resolved_phid;
+ }
+ }
+ $results[$package_phid] = $resolved;
+ }
+ }
+
return $results;
}
/**
* Execute the query, merging results into a single list of unique member
* PHIDs.
*/
public function executeExpansion() {
return array_unique(array_mergev($this->execute()));
}
}
diff --git a/src/applications/metamta/replyhandler/PhabricatorMailTarget.php b/src/applications/metamta/replyhandler/PhabricatorMailTarget.php
index e7e79c5ef9..c607087b22 100644
--- a/src/applications/metamta/replyhandler/PhabricatorMailTarget.php
+++ b/src/applications/metamta/replyhandler/PhabricatorMailTarget.php
@@ -1,146 +1,150 @@
<?php
final class PhabricatorMailTarget extends Phobject {
private $viewer;
private $replyTo;
private $toMap = array();
private $ccMap = array();
private $rawToPHIDs;
private $rawCCPHIDs;
public function setRawToPHIDs(array $to_phids) {
$this->rawToPHIDs = $to_phids;
return $this;
}
public function setRawCCPHIDs(array $cc_phids) {
$this->rawCCPHIDs = $cc_phids;
return $this;
}
public function setCCMap(array $cc_map) {
$this->ccMap = $cc_map;
return $this;
}
public function getCCMap() {
return $this->ccMap;
}
public function setToMap(array $to_map) {
$this->toMap = $to_map;
return $this;
}
public function getToMap() {
return $this->toMap;
}
public function setReplyTo($reply_to) {
$this->replyTo = $reply_to;
return $this;
}
public function getReplyTo() {
return $this->replyTo;
}
public function setViewer($viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function willSendMail(PhabricatorMetaMTAMail $mail) {
$viewer = $this->getViewer();
$mail->addPHIDHeaders('X-Phabricator-To', $this->rawToPHIDs);
$mail->addPHIDHeaders('X-Phabricator-Cc', $this->rawCCPHIDs);
$to_handles = $viewer->loadHandles($this->rawToPHIDs);
$cc_handles = $viewer->loadHandles($this->rawCCPHIDs);
$body = $mail->getBody();
$body .= "\n";
$body .= $this->getRecipientsSummary($to_handles, $cc_handles);
$mail->setBody($body);
$html_body = $mail->getHTMLBody();
if (strlen($html_body)) {
$html_body .= hsprintf(
'%s',
$this->getRecipientsSummaryHTML($to_handles, $cc_handles));
}
$mail->setHTMLBody($html_body);
$reply_to = $this->getReplyTo();
if ($reply_to) {
$mail->setReplyTo($reply_to);
}
$to = array_keys($this->getToMap());
if ($to) {
$mail->addTos($to);
}
$cc = array_keys($this->getCCMap());
if ($cc) {
$mail->addCCs($cc);
}
return $mail;
}
private function getRecipientsSummary(
PhabricatorHandleList $to_handles,
PhabricatorHandleList $cc_handles) {
if (!PhabricatorEnv::getEnvConfig('metamta.recipients.show-hints')) {
return '';
}
$to_handles = iterator_to_array($to_handles);
$cc_handles = iterator_to_array($cc_handles);
$body = '';
+
if ($to_handles) {
- $body .= "To: ".implode(', ', mpull($to_handles, 'getName'))."\n";
+ $to_names = mpull($to_handles, 'getCommandLineObjectName');
+ $body .= "To: ".implode(', ', $to_names)."\n";
}
+
if ($cc_handles) {
- $body .= "Cc: ".implode(', ', mpull($cc_handles, 'getName'))."\n";
+ $cc_names = mpull($cc_handles, 'getCommandLineObjectName');
+ $body .= "Cc: ".implode(', ', $cc_names)."\n";
}
return $body;
}
private function getRecipientsSummaryHTML(
PhabricatorHandleList $to_handles,
PhabricatorHandleList $cc_handles) {
if (!PhabricatorEnv::getEnvConfig('metamta.recipients.show-hints')) {
return '';
}
$to_handles = iterator_to_array($to_handles);
$cc_handles = iterator_to_array($cc_handles);
$body = array();
if ($to_handles) {
$body[] = phutil_tag('strong', array(), 'To: ');
$body[] = phutil_implode_html(', ', mpull($to_handles, 'getName'));
$body[] = phutil_tag('br');
}
if ($cc_handles) {
$body[] = phutil_tag('strong', array(), 'Cc: ');
$body[] = phutil_implode_html(', ', mpull($cc_handles, 'getName'));
$body[] = phutil_tag('br');
}
return phutil_tag('div', array(), $body);
}
}
diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php
index 5fa7492fef..2e0e03bb3c 100644
--- a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php
+++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php
@@ -1,25 +1,26 @@
<?php
final class PhabricatorMetaMTAMailableDatasource
extends PhabricatorTypeaheadCompositeDatasource {
public function getBrowseTitle() {
return pht('Browse Subscribers');
}
public function getPlaceholderText() {
- return pht('Type a user, project, or mailing list name...');
+ return pht('Type a user, project, package, or mailing list name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorMetaMTAApplication';
}
public function getComponentDatasources() {
return array(
new PhabricatorPeopleDatasource(),
new PhabricatorProjectDatasource(),
+ new PhabricatorOwnersPackageDatasource(),
);
}
}
diff --git a/src/applications/owners/controller/PhabricatorOwnersPathsController.php b/src/applications/owners/controller/PhabricatorOwnersPathsController.php
index 55aeb11b60..d1d6de760d 100644
--- a/src/applications/owners/controller/PhabricatorOwnersPathsController.php
+++ b/src/applications/owners/controller/PhabricatorOwnersPathsController.php
@@ -1,171 +1,171 @@
<?php
final class PhabricatorOwnersPathsController
extends PhabricatorOwnersController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$package = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->needPaths(true)
->executeOne();
if (!$package) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
$paths = $request->getArr('path');
$repos = $request->getArr('repo');
$excludes = $request->getArr('exclude');
$path_refs = array();
foreach ($paths as $key => $path) {
if (!isset($repos[$key])) {
throw new Exception(
pht(
'No repository PHID for path "%s"!',
$key));
}
if (!isset($excludes[$key])) {
throw new Exception(
pht(
'No exclusion value for path "%s"!',
$key));
}
$path_refs[] = array(
'repositoryPHID' => $repos[$key],
'path' => $path,
'excluded' => (int)$excludes[$key],
);
}
$type_paths = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
$xactions = array();
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
->setTransactionType($type_paths)
->setNewValue($path_refs);
$editor = id(new PhabricatorOwnersPackageTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($package, $xactions);
return id(new AphrontRedirectResponse())
- ->setURI('/owners/package/'.$package->getID().'/');
+ ->setURI($package->getURI());
} else {
$paths = $package->getPaths();
$path_refs = mpull($paths, 'getRef');
}
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->execute();
$default_paths = array();
foreach ($repos as $repo) {
$default_path = $repo->getDetail('default-owners-path');
if ($default_path) {
$default_paths[$repo->getPHID()] = $default_path;
}
}
$repos = mpull($repos, 'getDisplayName', 'getPHID');
asort($repos);
$template = new AphrontTypeaheadTemplateView();
$template = $template->render();
Javelin::initBehavior(
'owners-path-editor',
array(
'root' => 'path-editor',
'table' => 'paths',
'add_button' => 'addpath',
'repositories' => $repos,
'input_template' => $template,
'pathRefs' => $path_refs,
'completeURI' => '/diffusion/services/path/complete/',
'validateURI' => '/diffusion/services/path/validate/',
'repositoryDefaultPaths' => $default_paths,
));
require_celerity_resource('owners-path-editor-css');
- $cancel_uri = '/owners/package/'.$package->getID().'/';
+ $cancel_uri = $package->getURI();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new PHUIFormInsetView())
->setTitle(pht('Paths'))
->addDivAttributes(array('id' => 'path-editor'))
->setRightButton(javelin_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'addpath',
'mustcapture' => true,
),
pht('Add New Path')))
->setDescription(
pht(
'Specify the files and directories which comprise '.
'this package.'))
->setContent(javelin_tag(
'table',
array(
'class' => 'owners-path-editor-table',
'sigil' => 'paths',
),
'')))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue(pht('Save Paths')));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Paths'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
$package->getName(),
$this->getApplicationURI('package/'.$package->getID().'/'));
$crumbs->addTextCrumb(pht('Edit Paths'));
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Edit Paths: %s', $package->getName()))
->setHeaderIcon('fa-pencil');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($box);
$title = array($package->getName(), pht('Edit Paths'));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}
diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php
index fc97be6fce..7f705ec13d 100644
--- a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php
+++ b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php
@@ -1,147 +1,146 @@
<?php
final class PhabricatorOwnersPackageEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'owners.package';
public function getEngineName() {
return pht('Owners Packages');
}
public function getSummaryHeader() {
return pht('Configure Owners Package Forms');
}
public function getSummaryText() {
return pht('Configure forms for creating and editing packages in Owners.');
}
public function getEngineApplicationClass() {
return 'PhabricatorOwnersApplication';
}
protected function newEditableObject() {
return PhabricatorOwnersPackage::initializeNewPackage($this->getViewer());
}
protected function newObjectQuery() {
return id(new PhabricatorOwnersPackageQuery())
->needPaths(true);
}
protected function getObjectCreateTitleText($object) {
return pht('Create New Package');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Package: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Package %d', $object->getID());
}
protected function getObjectCreateShortText() {
return pht('Create Package');
}
protected function getObjectName() {
return pht('Package');
}
protected function getObjectViewURI($object) {
- $id = $object->getID();
- return "/owners/package/{$id}/";
+ return $object->getURI();
}
protected function buildCustomEditFields($object) {
$paths_help = pht(<<<EOTEXT
When updating the paths for a package, pass a list of dictionaries like
this as the `value` for the transaction:
```lang=json, name="Example Paths Value"
[
{
"repositoryPHID": "PHID-REPO-1234",
"path": "/path/to/directory/",
"excluded": false
},
{
"repositoryPHID": "PHID-REPO-1234",
"path": "/another/example/path/",
"excluded": false
}
]
```
This transaction will set the paths to the list you provide, overwriting any
previous paths.
Generally, you will call `owners.search` first to get a list of current paths
(which are provided in the same format), make changes, then update them by
applying a transaction of this type.
EOTEXT
);
return array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the package.'))
->setTransactionType(PhabricatorOwnersPackageTransaction::TYPE_NAME)
->setIsRequired(true)
->setValue($object->getName()),
id(new PhabricatorDatasourceEditField())
->setKey('owners')
->setLabel(pht('Owners'))
->setDescription(pht('Users and projects which own the package.'))
->setTransactionType(PhabricatorOwnersPackageTransaction::TYPE_OWNERS)
->setDatasource(new PhabricatorProjectOrUserDatasource())
->setIsCopyable(true)
->setValue($object->getOwnerPHIDs()),
id(new PhabricatorSelectEditField())
->setKey('auditing')
->setLabel(pht('Auditing'))
->setDescription(
pht(
'Automatically trigger audits for commits affecting files in '.
'this package.'))
->setTransactionType(PhabricatorOwnersPackageTransaction::TYPE_AUDITING)
->setIsCopyable(true)
->setValue($object->getAuditingEnabled())
->setOptions(
array(
'' => pht('Disabled'),
'1' => pht('Enabled'),
)),
id(new PhabricatorRemarkupEditField())
->setKey('description')
->setLabel(pht('Description'))
->setDescription(pht('Human-readable description of the package.'))
->setTransactionType(
PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION)
->setValue($object->getDescription()),
id(new PhabricatorSelectEditField())
->setKey('status')
->setLabel(pht('Status'))
->setDescription(pht('Archive or enable the package.'))
->setTransactionType(PhabricatorOwnersPackageTransaction::TYPE_STATUS)
->setIsConduitOnly(true)
->setValue($object->getStatus())
->setOptions($object->getStatusNameMap()),
id(new PhabricatorConduitEditField())
->setKey('paths.set')
->setLabel(pht('Paths'))
->setIsConduitOnly(true)
->setTransactionType(PhabricatorOwnersPackageTransaction::TYPE_PATHS)
->setConduitDescription(
pht('Overwrite existing package paths with new paths.'))
->setConduitTypeDescription(
pht('List of dictionaries, each describing a path.'))
->setConduitDocumentation($paths_help),
);
}
}
diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php
index 52f30e0994..f9267a0ba9 100644
--- a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php
+++ b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php
@@ -1,363 +1,362 @@
<?php
final class PhabricatorOwnersPackageTransactionEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorOwnersApplication';
}
public function getEditorObjectsDescription() {
return pht('Owners Packages');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorOwnersPackageTransaction::TYPE_NAME;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_OWNERS;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_AUDITING;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
return $object->getName();
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
$phids = mpull($object->getOwners(), 'getUserPHID');
$phids = array_values($phids);
return $phids;
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
return (int)$object->getAuditingEnabled();
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
return $object->getDescription();
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
$paths = $object->getPaths();
return mpull($paths, 'getRef');
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
return $object->getStatus();
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
return $xaction->getNewValue();
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
$new = $xaction->getNewValue();
foreach ($new as $key => $info) {
$new[$key]['excluded'] = (int)idx($info, 'excluded');
}
return $new;
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
return (int)$xaction->getNewValue();
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
$phids = $xaction->getNewValue();
$phids = array_unique($phids);
$phids = array_values($phids);
return $phids;
}
}
protected function transactionHasEffect(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
list($rem, $add) = $diffs;
return ($rem || $add);
}
return parent::transactionHasEffect($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
$object->setDescription($xaction->getNewValue());
return;
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
$object->setAuditingEnabled($xaction->getNewValue());
return;
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
return;
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
$object->setStatus($xaction->getNewValue());
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
return;
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$owners = $object->getOwners();
$owners = mpull($owners, null, 'getUserPHID');
$rem = array_diff($old, $new);
foreach ($rem as $phid) {
if (isset($owners[$phid])) {
$owners[$phid]->delete();
unset($owners[$phid]);
}
}
$add = array_diff($new, $old);
foreach ($add as $phid) {
$owners[$phid] = id(new PhabricatorOwnersOwner())
->setPackageID($object->getID())
->setUserPHID($phid)
->save();
}
// TODO: Attach owners here
return;
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$paths = $object->getPaths();
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
list($rem, $add) = $diffs;
$set = PhabricatorOwnersPath::getSetFromTransactionValue($rem);
foreach ($paths as $path) {
$ref = $path->getRef();
if (PhabricatorOwnersPath::isRefInSet($ref, $set)) {
$path->delete();
}
}
foreach ($add as $ref) {
$path = PhabricatorOwnersPath::newFromRef($ref)
->setPackageID($object->getID())
->save();
}
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Package name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
foreach ($xactions as $xaction) {
$new = $xaction->getNewValue();
if (preg_match('([,!])', $new)) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'Package names may not contain commas (",") or exclamation '.
'marks ("!"). These characters are ambiguous when package '.
'names are parsed from the command line.'),
$xaction);
}
}
break;
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
if (!$xactions) {
continue;
}
$old = mpull($object->getPaths(), 'getRef');
foreach ($xactions as $xaction) {
$new = $xaction->getNewValue();
// Check that we have a list of paths.
if (!is_array($new)) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht('Path specification must be a list of paths.'),
$xaction);
continue;
}
// Check that each item in the list is formatted properly.
$type_exception = null;
foreach ($new as $key => $value) {
try {
PhutilTypeSpec::checkMap(
$value,
array(
'repositoryPHID' => 'string',
'path' => 'string',
'excluded' => 'optional wild',
));
} catch (PhutilTypeCheckException $ex) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'Path specification list contains invalid value '.
'in key "%s": %s.',
$key,
$ex->getMessage()),
$xaction);
$type_exception = $ex;
}
}
if ($type_exception) {
continue;
}
// Check that any new paths reference legitimate repositories which
// the viewer has permission to see.
list($rem, $add) = PhabricatorOwnersPath::getTransactionValueChanges(
$old,
$new);
if ($add) {
$repository_phids = ipull($add, 'repositoryPHID');
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getActor())
->withPHIDs($repository_phids)
->execute();
$repositories = mpull($repositories, null, 'getPHID');
foreach ($add as $ref) {
$repository_phid = $ref['repositoryPHID'];
if (isset($repositories[$repository_phid])) {
continue;
}
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'Path specification list references repository PHID "%s", '.
'but that is not a valid, visible repository.',
$repository_phid));
}
}
}
break;
}
return $errors;
}
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function getMailSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.package.subject-prefix');
}
protected function getMailTo(PhabricatorLiskDAO $object) {
return array(
$this->requireActor()->getPHID(),
);
}
protected function getMailCC(PhabricatorLiskDAO $object) {
return mpull($object->getOwners(), 'getUserPHID');
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return id(new OwnersPackageReplyHandler())
->setMailReceiver($object);
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$name = $object->getName();
return id(new PhabricatorMetaMTAMail())
->setSubject($name)
->addHeader('Thread-Topic', $object->getPHID());
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
- $detail_uri = PhabricatorEnv::getProductionURI(
- '/owners/package/'.$object->getID().'/');
+ $detail_uri = PhabricatorEnv::getProductionURI($object->getURI());
$body->addLinkSection(
pht('PACKAGE DETAIL'),
$detail_uri);
return $body;
}
protected function supportsSearch() {
return true;
}
}
diff --git a/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php b/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php
index 772bb84fc0..cb23ca4041 100644
--- a/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php
+++ b/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php
@@ -1,84 +1,85 @@
<?php
final class PhabricatorOwnersPackagePHIDType extends PhabricatorPHIDType {
const TYPECONST = 'OPKG';
public function getTypeName() {
return pht('Owners Package');
}
public function getTypeIcon() {
return 'fa-list-alt';
}
public function newObject() {
return new PhabricatorOwnersPackage();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorOwnersApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorOwnersPackageQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$package = $objects[$phid];
$monogram = $package->getMonogram();
$name = $package->getName();
$id = $package->getID();
+ $uri = $package->getURI();
$handle
->setName($monogram)
->setFullName("{$monogram}: {$name}")
->setCommandLineObjectName("{$monogram} {$name}")
- ->setURI("/owners/package/{$id}/");
+ ->setURI($uri);
if ($package->isArchived()) {
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
}
}
}
public function canLoadNamedObject($name) {
return preg_match('/^O\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PhabricatorOwnersPackageQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php b/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php
index e74d3c6a63..728c3f42a8 100644
--- a/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php
+++ b/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php
@@ -1,178 +1,178 @@
<?php
final class PhabricatorOwnersPackageSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Owners Packages');
}
public function getApplicationClassName() {
return 'PhabricatorOwnersApplication';
}
public function newQuery() {
return new PhabricatorOwnersPackageQuery();
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Authority'))
->setKey('authorityPHIDs')
->setAliases(array('authority', 'authorities'))
->setConduitKey('owners')
->setDescription(
pht('Search for packages with specific owners.'))
->setDatasource(new PhabricatorProjectOrUserDatasource()),
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('name')
->setDescription(pht('Search for packages by name substrings.')),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Repositories'))
->setKey('repositoryPHIDs')
->setConduitKey('repositories')
->setAliases(array('repository', 'repositories'))
->setDescription(
pht('Search for packages by included repositories.'))
->setDatasource(new DiffusionRepositoryDatasource()),
id(new PhabricatorSearchStringListField())
->setLabel(pht('Paths'))
->setKey('paths')
->setAliases(array('path'))
->setDescription(
pht('Search for packages affecting specific paths.')),
id(new PhabricatorSearchCheckboxesField())
->setKey('statuses')
->setLabel(pht('Status'))
->setDescription(
pht('Search for active or archived packages.'))
->setOptions(
id(new PhabricatorOwnersPackage())
->getStatusNameMap()),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['authorityPHIDs']) {
$query->withAuthorityPHIDs($map['authorityPHIDs']);
}
if ($map['repositoryPHIDs']) {
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
}
if ($map['paths']) {
$query->withPaths($map['paths']);
}
if ($map['statuses']) {
$query->withStatuses($map['statuses']);
}
if (strlen($map['name'])) {
$query->withNameNgrams($map['name']);
}
return $query;
}
protected function getURI($path) {
return '/owners/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['authority'] = pht('Owned');
}
$names += array(
'active' => pht('Active Packages'),
'all' => pht('All Packages'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'active':
return $query->setParameter(
'statuses',
array(
PhabricatorOwnersPackage::STATUS_ACTIVE,
));
case 'authority':
return $query->setParameter(
'authorityPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $packages,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($packages, 'PhabricatorOwnersPackage');
$viewer = $this->requireViewer();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($packages as $package) {
$id = $package->getID();
$item = id(new PHUIObjectItemView())
->setObject($package)
->setObjectName($package->getMonogram())
->setHeader($package->getName())
- ->setHref('/owners/package/'.$id.'/');
+ ->setHref($package->getURI());
if ($package->isArchived()) {
$item->setDisabled(true);
}
$list->addItem($item);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($list);
$result->setNoDataString(pht('No packages found.'));
return $result;
}
protected function getNewUserBody() {
$create_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Create a Package'))
->setHref('/owners/edit/')
->setColor(PHUIButtonView::GREEN);
$icon = $this->getApplication()->getIcon();
$app_name = $this->getApplication()->getName();
$view = id(new PHUIBigInfoView())
->setIcon($icon)
->setTitle(pht('Welcome to %s', $app_name))
->setDescription(
pht('Group sections of a codebase into packages for re-use in other '.
'areas of Phabricator, like Herald rules.'))
->addAction($create_button);
return $view;
}
}
diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php
index 83c007e170..14eb618c30 100644
--- a/src/applications/owners/storage/PhabricatorOwnersPackage.php
+++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php
@@ -1,468 +1,472 @@
<?php
final class PhabricatorOwnersPackage
extends PhabricatorOwnersDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorCustomFieldInterface,
PhabricatorDestructibleInterface,
PhabricatorConduitResultInterface,
PhabricatorFulltextInterface,
PhabricatorNgramsInterface {
protected $name;
protected $originalName;
protected $auditingEnabled;
protected $description;
protected $primaryOwnerPHID;
protected $mailKey;
protected $status;
protected $viewPolicy;
protected $editPolicy;
private $paths = self::ATTACHABLE;
private $owners = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
const STATUS_ACTIVE = 'active';
const STATUS_ARCHIVED = 'archived';
public static function initializeNewPackage(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorOwnersApplication'))
->executeOne();
$view_policy = $app->getPolicy(
PhabricatorOwnersDefaultViewCapability::CAPABILITY);
$edit_policy = $app->getPolicy(
PhabricatorOwnersDefaultEditCapability::CAPABILITY);
return id(new PhabricatorOwnersPackage())
->setAuditingEnabled(0)
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->attachPaths(array())
->setStatus(self::STATUS_ACTIVE)
->attachOwners(array())
->setDescription('');
}
public static function getStatusNameMap() {
return array(
self::STATUS_ACTIVE => pht('Active'),
self::STATUS_ARCHIVED => pht('Archived'),
);
}
protected function getConfiguration() {
return array(
// This information is better available from the history table.
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'sort128',
'originalName' => 'text255',
'description' => 'text',
'primaryOwnerPHID' => 'phid?',
'auditingEnabled' => 'bool',
'mailKey' => 'bytes20',
'status' => 'text32',
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorOwnersPackagePHIDType::TYPECONST);
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public function isArchived() {
return ($this->getStatus() == self::STATUS_ARCHIVED);
}
public function setName($name) {
$this->name = $name;
if (!$this->getID()) {
$this->originalName = $name;
}
return $this;
}
public function loadOwners() {
if (!$this->getID()) {
return array();
}
return id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID = %d',
$this->getID());
}
public function loadPaths() {
if (!$this->getID()) {
return array();
}
return id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID = %d',
$this->getID());
}
public static function loadAffectedPackages(
PhabricatorRepository $repository,
array $paths) {
if (!$paths) {
return array();
}
return self::loadPackagesForPaths($repository, $paths);
}
public static function loadOwningPackages($repository, $path) {
if (empty($path)) {
return array();
}
return self::loadPackagesForPaths($repository, array($path), 1);
}
private static function loadPackagesForPaths(
PhabricatorRepository $repository,
array $paths,
$limit = 0) {
$fragments = array();
foreach ($paths as $path) {
foreach (self::splitPath($path) as $fragment) {
$fragments[$fragment][$path] = true;
}
}
$package = new PhabricatorOwnersPackage();
$path = new PhabricatorOwnersPath();
$conn = $package->establishConnection('r');
$repository_clause = qsprintf(
$conn,
'AND p.repositoryPHID = %s',
$repository->getPHID());
// NOTE: The list of $paths may be very large if we're coming from
// the OwnersWorker and processing, e.g., an SVN commit which created a new
// branch. Break it apart so that it will fit within 'max_allowed_packet',
// and then merge results in PHP.
$rows = array();
foreach (array_chunk(array_keys($fragments), 128) as $chunk) {
$rows[] = queryfx_all(
$conn,
'SELECT pkg.id, p.excluded, p.path
FROM %T pkg JOIN %T p ON p.packageID = pkg.id
WHERE p.path IN (%Ls) %Q',
$package->getTableName(),
$path->getTableName(),
$chunk,
$repository_clause);
}
$rows = array_mergev($rows);
$ids = self::findLongestPathsPerPackage($rows, $fragments);
if (!$ids) {
return array();
}
arsort($ids);
if ($limit) {
$ids = array_slice($ids, 0, $limit, $preserve_keys = true);
}
$ids = array_keys($ids);
$packages = $package->loadAllWhere('id in (%Ld)', $ids);
$packages = array_select_keys($packages, $ids);
return $packages;
}
public static function loadPackagesForRepository($repository) {
$package = new PhabricatorOwnersPackage();
$ids = ipull(
queryfx_all(
$package->establishConnection('r'),
'SELECT DISTINCT packageID FROM %T WHERE repositoryPHID = %s',
id(new PhabricatorOwnersPath())->getTableName(),
$repository->getPHID()),
'packageID');
return $package->loadAllWhere('id in (%Ld)', $ids);
}
public static function findLongestPathsPerPackage(array $rows, array $paths) {
$ids = array();
foreach (igroup($rows, 'id') as $id => $package_paths) {
$relevant_paths = array_select_keys(
$paths,
ipull($package_paths, 'path'));
// For every package, remove all excluded paths.
$remove = array();
foreach ($package_paths as $package_path) {
if ($package_path['excluded']) {
$remove += idx($relevant_paths, $package_path['path'], array());
unset($relevant_paths[$package_path['path']]);
}
}
if ($remove) {
foreach ($relevant_paths as $fragment => $fragment_paths) {
$relevant_paths[$fragment] = array_diff_key($fragment_paths, $remove);
}
}
$relevant_paths = array_filter($relevant_paths);
if ($relevant_paths) {
$ids[$id] = max(array_map('strlen', array_keys($relevant_paths)));
}
}
return $ids;
}
public static function splitPath($path) {
$trailing_slash = preg_match('@/$@', $path) ? '/' : '';
$path = trim($path, '/');
$parts = explode('/', $path);
$result = array();
while (count($parts)) {
$result[] = '/'.implode('/', $parts).$trailing_slash;
$trailing_slash = '/';
array_pop($parts);
}
$result[] = '/';
return array_reverse($result);
}
public function attachPaths(array $paths) {
assert_instances_of($paths, 'PhabricatorOwnersPath');
$this->paths = $paths;
return $this;
}
public function getPaths() {
return $this->assertAttached($this->paths);
}
public function attachOwners(array $owners) {
assert_instances_of($owners, 'PhabricatorOwnersOwner');
$this->owners = $owners;
return $this;
}
public function getOwners() {
return $this->assertAttached($this->owners);
}
public function getOwnerPHIDs() {
return mpull($this->getOwners(), 'getUserPHID');
}
public function isOwnerPHID($phid) {
if (!$phid) {
return false;
}
$owner_phids = $this->getOwnerPHIDs();
$owner_phids = array_fuse($owner_phids);
return isset($owner_phids[$phid]);
}
public function getMonogram() {
return 'O'.$this->getID();
}
+ public function getURI() {
+ // TODO: Move these to "/O123" for consistency.
+ return '/owners/package/'.$this->getID().'/';
+ }
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
if ($this->isOwnerPHID($viewer->getPHID())) {
return true;
}
break;
}
return false;
}
public function describeAutomaticCapability($capability) {
return pht('Owners of a package may always view it.');
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorOwnersPackageTransactionEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorOwnersPackageTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('owners.fields');
}
public function getCustomFieldBaseClass() {
return 'PhabricatorOwnersCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$conn_w = $this->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE packageID = %d',
id(new PhabricatorOwnersPath())->getTableName(),
$this->getID());
queryfx(
$conn_w,
'DELETE FROM %T WHERE packageID = %d',
id(new PhabricatorOwnersOwner())->getTableName(),
$this->getID());
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('name')
->setType('string')
->setDescription(pht('The name of the package.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('description')
->setType('string')
->setDescription(pht('The package description.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('status')
->setType('string')
->setDescription(pht('Active or archived status of the package.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('owners')
->setType('list<map<string, wild>>')
->setDescription(pht('List of package owners.')),
);
}
public function getFieldValuesForConduit() {
$owner_list = array();
foreach ($this->getOwners() as $owner) {
$owner_list[] = array(
'ownerPHID' => $owner->getUserPHID(),
);
}
return array(
'name' => $this->getName(),
'description' => $this->getDescription(),
'status' => $this->getStatus(),
'owners' => $owner_list,
);
}
public function getConduitSearchAttachments() {
return array(
id(new PhabricatorOwnersPathsSearchEngineAttachment())
->setAttachmentKey('paths'),
);
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new PhabricatorOwnersPackageFulltextEngine();
}
/* -( PhabricatorNgramsInterface )----------------------------------------- */
public function newNgrams() {
return array(
id(new PhabricatorOwnersPackageNameNgrams())
->setValue($this->getName()),
);
}
}
diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php
index 9230ce270e..41d5d6823a 100644
--- a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php
+++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php
@@ -1,39 +1,42 @@
<?php
final class PhabricatorOwnersPackageDatasource
extends PhabricatorTypeaheadDatasource {
public function getBrowseTitle() {
return pht('Browse Packages');
}
public function getPlaceholderText() {
return pht('Type a package name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorOwnersApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$query = id(new PhabricatorOwnersPackageQuery())
->withNameNgrams($raw_query)
->setOrder('name');
$packages = $this->executeQuery($query);
foreach ($packages as $package) {
+ $name = $package->getName();
+ $monogram = $package->getMonogram();
+
$results[] = id(new PhabricatorTypeaheadResult())
- ->setName($package->getName())
- ->setURI('/owners/package/'.$package->getID().'/')
+ ->setName("{$monogram}: {$name}")
+ ->setURI($package->getURI())
->setPHID($package->getPHID());
}
return $this->filterResultsAgainstTokens($results);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 10, 4:02 PM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
140483
Default Alt Text
(54 KB)

Event Timeline