PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/applications/owners/storage/PhabricatorOwnersPackage.php

http://github.com/facebook/phabricator
PHP | 803 lines | 605 code | 150 blank | 48 comment | 40 complexity | 018fd8bf5b7850360c2ba1a055975b43 MD5 | raw file
Possible License(s): JSON, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, LGPL-2.0, MIT, LGPL-2.1, LGPL-3.0
  1. <?php
  2. final class PhabricatorOwnersPackage
  3. extends PhabricatorOwnersDAO
  4. implements
  5. PhabricatorPolicyInterface,
  6. PhabricatorApplicationTransactionInterface,
  7. PhabricatorCustomFieldInterface,
  8. PhabricatorDestructibleInterface,
  9. PhabricatorConduitResultInterface,
  10. PhabricatorFulltextInterface,
  11. PhabricatorFerretInterface,
  12. PhabricatorNgramsInterface {
  13. protected $name;
  14. protected $autoReview;
  15. protected $description;
  16. protected $status;
  17. protected $viewPolicy;
  18. protected $editPolicy;
  19. protected $dominion;
  20. protected $properties = array();
  21. protected $auditingState;
  22. private $paths = self::ATTACHABLE;
  23. private $owners = self::ATTACHABLE;
  24. private $customFields = self::ATTACHABLE;
  25. private $pathRepositoryMap = array();
  26. const STATUS_ACTIVE = 'active';
  27. const STATUS_ARCHIVED = 'archived';
  28. const AUTOREVIEW_NONE = 'none';
  29. const AUTOREVIEW_SUBSCRIBE = 'subscribe';
  30. const AUTOREVIEW_SUBSCRIBE_ALWAYS = 'subscribe-always';
  31. const AUTOREVIEW_REVIEW = 'review';
  32. const AUTOREVIEW_REVIEW_ALWAYS = 'review-always';
  33. const AUTOREVIEW_BLOCK = 'block';
  34. const AUTOREVIEW_BLOCK_ALWAYS = 'block-always';
  35. const DOMINION_STRONG = 'strong';
  36. const DOMINION_WEAK = 'weak';
  37. const PROPERTY_IGNORED = 'ignored';
  38. public static function initializeNewPackage(PhabricatorUser $actor) {
  39. $app = id(new PhabricatorApplicationQuery())
  40. ->setViewer($actor)
  41. ->withClasses(array('PhabricatorOwnersApplication'))
  42. ->executeOne();
  43. $view_policy = $app->getPolicy(
  44. PhabricatorOwnersDefaultViewCapability::CAPABILITY);
  45. $edit_policy = $app->getPolicy(
  46. PhabricatorOwnersDefaultEditCapability::CAPABILITY);
  47. return id(new PhabricatorOwnersPackage())
  48. ->setAuditingState(PhabricatorOwnersAuditRule::AUDITING_NONE)
  49. ->setAutoReview(self::AUTOREVIEW_NONE)
  50. ->setDominion(self::DOMINION_STRONG)
  51. ->setViewPolicy($view_policy)
  52. ->setEditPolicy($edit_policy)
  53. ->attachPaths(array())
  54. ->setStatus(self::STATUS_ACTIVE)
  55. ->attachOwners(array())
  56. ->setDescription('');
  57. }
  58. public static function getStatusNameMap() {
  59. return array(
  60. self::STATUS_ACTIVE => pht('Active'),
  61. self::STATUS_ARCHIVED => pht('Archived'),
  62. );
  63. }
  64. public static function getAutoreviewOptionsMap() {
  65. return array(
  66. self::AUTOREVIEW_NONE => array(
  67. 'name' => pht('No Autoreview'),
  68. ),
  69. self::AUTOREVIEW_REVIEW => array(
  70. 'name' => pht('Review Changes With Non-Owner Author'),
  71. 'authority' => true,
  72. ),
  73. self::AUTOREVIEW_BLOCK => array(
  74. 'name' => pht('Review Changes With Non-Owner Author (Blocking)'),
  75. 'authority' => true,
  76. ),
  77. self::AUTOREVIEW_SUBSCRIBE => array(
  78. 'name' => pht('Subscribe to Changes With Non-Owner Author'),
  79. 'authority' => true,
  80. ),
  81. self::AUTOREVIEW_REVIEW_ALWAYS => array(
  82. 'name' => pht('Review All Changes'),
  83. ),
  84. self::AUTOREVIEW_BLOCK_ALWAYS => array(
  85. 'name' => pht('Review All Changes (Blocking)'),
  86. ),
  87. self::AUTOREVIEW_SUBSCRIBE_ALWAYS => array(
  88. 'name' => pht('Subscribe to All Changes'),
  89. ),
  90. );
  91. }
  92. public static function getDominionOptionsMap() {
  93. return array(
  94. self::DOMINION_STRONG => array(
  95. 'name' => pht('Strong (Control All Paths)'),
  96. 'short' => pht('Strong'),
  97. ),
  98. self::DOMINION_WEAK => array(
  99. 'name' => pht('Weak (Control Unowned Paths)'),
  100. 'short' => pht('Weak'),
  101. ),
  102. );
  103. }
  104. protected function getConfiguration() {
  105. return array(
  106. // This information is better available from the history table.
  107. self::CONFIG_TIMESTAMPS => false,
  108. self::CONFIG_AUX_PHID => true,
  109. self::CONFIG_SERIALIZATION => array(
  110. 'properties' => self::SERIALIZATION_JSON,
  111. ),
  112. self::CONFIG_COLUMN_SCHEMA => array(
  113. 'name' => 'sort',
  114. 'description' => 'text',
  115. 'auditingState' => 'text32',
  116. 'status' => 'text32',
  117. 'autoReview' => 'text32',
  118. 'dominion' => 'text32',
  119. ),
  120. ) + parent::getConfiguration();
  121. }
  122. public function getPHIDType() {
  123. return PhabricatorOwnersPackagePHIDType::TYPECONST;
  124. }
  125. public function isArchived() {
  126. return ($this->getStatus() == self::STATUS_ARCHIVED);
  127. }
  128. public function getMustMatchUngeneratedPaths() {
  129. $ignore_attributes = $this->getIgnoredPathAttributes();
  130. return !empty($ignore_attributes['generated']);
  131. }
  132. public function getPackageProperty($key, $default = null) {
  133. return idx($this->properties, $key, $default);
  134. }
  135. public function setPackageProperty($key, $value) {
  136. $this->properties[$key] = $value;
  137. return $this;
  138. }
  139. public function getIgnoredPathAttributes() {
  140. return $this->getPackageProperty(self::PROPERTY_IGNORED, array());
  141. }
  142. public function setIgnoredPathAttributes(array $attributes) {
  143. return $this->setPackageProperty(self::PROPERTY_IGNORED, $attributes);
  144. }
  145. public function loadOwners() {
  146. if (!$this->getID()) {
  147. return array();
  148. }
  149. return id(new PhabricatorOwnersOwner())->loadAllWhere(
  150. 'packageID = %d',
  151. $this->getID());
  152. }
  153. public function loadPaths() {
  154. if (!$this->getID()) {
  155. return array();
  156. }
  157. return id(new PhabricatorOwnersPath())->loadAllWhere(
  158. 'packageID = %d',
  159. $this->getID());
  160. }
  161. public static function loadAffectedPackages(
  162. PhabricatorRepository $repository,
  163. array $paths) {
  164. if (!$paths) {
  165. return array();
  166. }
  167. return self::loadPackagesForPaths($repository, $paths);
  168. }
  169. public static function loadAffectedPackagesForChangesets(
  170. PhabricatorRepository $repository,
  171. DifferentialDiff $diff,
  172. array $changesets) {
  173. assert_instances_of($changesets, 'DifferentialChangeset');
  174. $paths_all = array();
  175. $paths_ungenerated = array();
  176. foreach ($changesets as $changeset) {
  177. $path = $changeset->getAbsoluteRepositoryPath($repository, $diff);
  178. $paths_all[] = $path;
  179. if (!$changeset->isGeneratedChangeset()) {
  180. $paths_ungenerated[] = $path;
  181. }
  182. }
  183. if (!$paths_all) {
  184. return array();
  185. }
  186. $packages_all = self::loadAffectedPackages(
  187. $repository,
  188. $paths_all);
  189. // If there are no generated changesets, we can't possibly need to throw
  190. // away any packages for matching only generated paths. Just return the
  191. // full set of packages.
  192. if ($paths_ungenerated === $paths_all) {
  193. return $packages_all;
  194. }
  195. $must_match_ungenerated = array();
  196. foreach ($packages_all as $package) {
  197. if ($package->getMustMatchUngeneratedPaths()) {
  198. $must_match_ungenerated[] = $package;
  199. }
  200. }
  201. // If no affected packages have the "Ignore Generated Paths" flag set, we
  202. // can't possibly need to throw any away.
  203. if (!$must_match_ungenerated) {
  204. return $packages_all;
  205. }
  206. if ($paths_ungenerated) {
  207. $packages_ungenerated = self::loadAffectedPackages(
  208. $repository,
  209. $paths_ungenerated);
  210. } else {
  211. $packages_ungenerated = array();
  212. }
  213. // We have some generated paths, and some packages that ignore generated
  214. // paths. Take all the packages which:
  215. //
  216. // - ignore generated paths; and
  217. // - didn't match any ungenerated paths
  218. //
  219. // ...and remove them from the list.
  220. $must_match_ungenerated = mpull($must_match_ungenerated, null, 'getID');
  221. $packages_ungenerated = mpull($packages_ungenerated, null, 'getID');
  222. $packages_all = mpull($packages_all, null, 'getID');
  223. foreach ($must_match_ungenerated as $package_id => $package) {
  224. if (!isset($packages_ungenerated[$package_id])) {
  225. unset($packages_all[$package_id]);
  226. }
  227. }
  228. return $packages_all;
  229. }
  230. public static function loadOwningPackages($repository, $path) {
  231. if (empty($path)) {
  232. return array();
  233. }
  234. return self::loadPackagesForPaths($repository, array($path), 1);
  235. }
  236. private static function loadPackagesForPaths(
  237. PhabricatorRepository $repository,
  238. array $paths,
  239. $limit = 0) {
  240. $fragments = array();
  241. foreach ($paths as $path) {
  242. foreach (self::splitPath($path) as $fragment) {
  243. $fragments[$fragment][$path] = true;
  244. }
  245. }
  246. $package = new PhabricatorOwnersPackage();
  247. $path = new PhabricatorOwnersPath();
  248. $conn = $package->establishConnection('r');
  249. $repository_clause = qsprintf(
  250. $conn,
  251. 'AND p.repositoryPHID = %s',
  252. $repository->getPHID());
  253. // NOTE: The list of $paths may be very large if we're coming from
  254. // the OwnersWorker and processing, e.g., an SVN commit which created a new
  255. // branch. Break it apart so that it will fit within 'max_allowed_packet',
  256. // and then merge results in PHP.
  257. $rows = array();
  258. foreach (array_chunk(array_keys($fragments), 1024) as $chunk) {
  259. $indexes = array();
  260. foreach ($chunk as $fragment) {
  261. $indexes[] = PhabricatorHash::digestForIndex($fragment);
  262. }
  263. $rows[] = queryfx_all(
  264. $conn,
  265. 'SELECT pkg.id, pkg.dominion, p.excluded, p.path
  266. FROM %T pkg JOIN %T p ON p.packageID = pkg.id
  267. WHERE p.pathIndex IN (%Ls) AND pkg.status IN (%Ls) %Q',
  268. $package->getTableName(),
  269. $path->getTableName(),
  270. $indexes,
  271. array(
  272. self::STATUS_ACTIVE,
  273. ),
  274. $repository_clause);
  275. }
  276. $rows = array_mergev($rows);
  277. $ids = self::findLongestPathsPerPackage($rows, $fragments);
  278. if (!$ids) {
  279. return array();
  280. }
  281. arsort($ids);
  282. if ($limit) {
  283. $ids = array_slice($ids, 0, $limit, $preserve_keys = true);
  284. }
  285. $ids = array_keys($ids);
  286. $packages = $package->loadAllWhere('id in (%Ld)', $ids);
  287. $packages = array_select_keys($packages, $ids);
  288. return $packages;
  289. }
  290. public static function loadPackagesForRepository($repository) {
  291. $package = new PhabricatorOwnersPackage();
  292. $ids = ipull(
  293. queryfx_all(
  294. $package->establishConnection('r'),
  295. 'SELECT DISTINCT packageID FROM %T WHERE repositoryPHID = %s',
  296. id(new PhabricatorOwnersPath())->getTableName(),
  297. $repository->getPHID()),
  298. 'packageID');
  299. return $package->loadAllWhere('id in (%Ld)', $ids);
  300. }
  301. public static function findLongestPathsPerPackage(array $rows, array $paths) {
  302. // Build a map from each path to all the package paths which match it.
  303. $path_hits = array();
  304. $weak = array();
  305. foreach ($rows as $row) {
  306. $id = $row['id'];
  307. $path = $row['path'];
  308. $length = strlen($path);
  309. $excluded = $row['excluded'];
  310. if ($row['dominion'] === self::DOMINION_WEAK) {
  311. $weak[$id] = true;
  312. }
  313. $matches = $paths[$path];
  314. foreach ($matches as $match => $ignored) {
  315. $path_hits[$match][] = array(
  316. 'id' => $id,
  317. 'excluded' => $excluded,
  318. 'length' => $length,
  319. );
  320. }
  321. }
  322. // For each path, process the matching package paths to figure out which
  323. // packages actually own it.
  324. $path_packages = array();
  325. foreach ($path_hits as $match => $hits) {
  326. $hits = isort($hits, 'length');
  327. $packages = array();
  328. foreach ($hits as $hit) {
  329. $package_id = $hit['id'];
  330. if ($hit['excluded']) {
  331. unset($packages[$package_id]);
  332. } else {
  333. $packages[$package_id] = $hit;
  334. }
  335. }
  336. $path_packages[$match] = $packages;
  337. }
  338. // Remove packages with weak dominion rules that should cede control to
  339. // a more specific package.
  340. if ($weak) {
  341. foreach ($path_packages as $match => $packages) {
  342. // Group packages by length.
  343. $length_map = array();
  344. foreach ($packages as $package_id => $package) {
  345. $length_map[$package['length']][$package_id] = $package;
  346. }
  347. // For each path length, remove all weak packages if there are any
  348. // strong packages of the same length. This makes sure that if there
  349. // are one or more strong claims on a particular path, only those
  350. // claims stand.
  351. foreach ($length_map as $package_list) {
  352. $any_strong = false;
  353. foreach ($package_list as $package_id => $package) {
  354. if (!isset($weak[$package_id])) {
  355. $any_strong = true;
  356. break;
  357. }
  358. }
  359. if ($any_strong) {
  360. foreach ($package_list as $package_id => $package) {
  361. if (isset($weak[$package_id])) {
  362. unset($packages[$package_id]);
  363. }
  364. }
  365. }
  366. }
  367. $packages = isort($packages, 'length');
  368. $packages = array_reverse($packages, true);
  369. $best_length = null;
  370. foreach ($packages as $package_id => $package) {
  371. // If this is the first package we've encountered, note its length.
  372. // We're iterating over the packages from longest to shortest match,
  373. // so packages of this length always have the best claim on the path.
  374. if ($best_length === null) {
  375. $best_length = $package['length'];
  376. }
  377. // If this package has the same length as the best length, its claim
  378. // stands.
  379. if ($package['length'] === $best_length) {
  380. continue;
  381. }
  382. // If this is a weak package and does not have the best length,
  383. // cede its claim to the stronger package.
  384. if (isset($weak[$package_id])) {
  385. unset($packages[$package_id]);
  386. }
  387. }
  388. $path_packages[$match] = $packages;
  389. }
  390. }
  391. // For each package that owns at least one path, identify the longest
  392. // path it owns.
  393. $package_lengths = array();
  394. foreach ($path_packages as $match => $hits) {
  395. foreach ($hits as $hit) {
  396. $length = $hit['length'];
  397. $id = $hit['id'];
  398. if (empty($package_lengths[$id])) {
  399. $package_lengths[$id] = $length;
  400. } else {
  401. $package_lengths[$id] = max($package_lengths[$id], $length);
  402. }
  403. }
  404. }
  405. return $package_lengths;
  406. }
  407. public static function splitPath($path) {
  408. $result = array(
  409. '/',
  410. );
  411. $parts = explode('/', $path);
  412. $buffer = '/';
  413. foreach ($parts as $part) {
  414. if (!strlen($part)) {
  415. continue;
  416. }
  417. $buffer = $buffer.$part.'/';
  418. $result[] = $buffer;
  419. }
  420. return $result;
  421. }
  422. public function attachPaths(array $paths) {
  423. assert_instances_of($paths, 'PhabricatorOwnersPath');
  424. $this->paths = $paths;
  425. // Drop this cache if we're attaching new paths.
  426. $this->pathRepositoryMap = array();
  427. return $this;
  428. }
  429. public function getPaths() {
  430. return $this->assertAttached($this->paths);
  431. }
  432. public function getPathsForRepository($repository_phid) {
  433. if (isset($this->pathRepositoryMap[$repository_phid])) {
  434. return $this->pathRepositoryMap[$repository_phid];
  435. }
  436. $map = array();
  437. foreach ($this->getPaths() as $path) {
  438. if ($path->getRepositoryPHID() == $repository_phid) {
  439. $map[] = $path;
  440. }
  441. }
  442. $this->pathRepositoryMap[$repository_phid] = $map;
  443. return $this->pathRepositoryMap[$repository_phid];
  444. }
  445. public function attachOwners(array $owners) {
  446. assert_instances_of($owners, 'PhabricatorOwnersOwner');
  447. $this->owners = $owners;
  448. return $this;
  449. }
  450. public function getOwners() {
  451. return $this->assertAttached($this->owners);
  452. }
  453. public function getOwnerPHIDs() {
  454. return mpull($this->getOwners(), 'getUserPHID');
  455. }
  456. public function isOwnerPHID($phid) {
  457. if (!$phid) {
  458. return false;
  459. }
  460. $owner_phids = $this->getOwnerPHIDs();
  461. $owner_phids = array_fuse($owner_phids);
  462. return isset($owner_phids[$phid]);
  463. }
  464. public function getMonogram() {
  465. return 'O'.$this->getID();
  466. }
  467. public function getURI() {
  468. // TODO: Move these to "/O123" for consistency.
  469. return '/owners/package/'.$this->getID().'/';
  470. }
  471. public function newAuditingRule() {
  472. return PhabricatorOwnersAuditRule::newFromState($this->getAuditingState());
  473. }
  474. /* -( PhabricatorPolicyInterface )----------------------------------------- */
  475. public function getCapabilities() {
  476. return array(
  477. PhabricatorPolicyCapability::CAN_VIEW,
  478. PhabricatorPolicyCapability::CAN_EDIT,
  479. );
  480. }
  481. public function getPolicy($capability) {
  482. switch ($capability) {
  483. case PhabricatorPolicyCapability::CAN_VIEW:
  484. return $this->getViewPolicy();
  485. case PhabricatorPolicyCapability::CAN_EDIT:
  486. return $this->getEditPolicy();
  487. }
  488. }
  489. public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
  490. switch ($capability) {
  491. case PhabricatorPolicyCapability::CAN_VIEW:
  492. if ($this->isOwnerPHID($viewer->getPHID())) {
  493. return true;
  494. }
  495. break;
  496. }
  497. return false;
  498. }
  499. public function describeAutomaticCapability($capability) {
  500. return pht('Owners of a package may always view it.');
  501. }
  502. /* -( PhabricatorApplicationTransactionInterface )------------------------- */
  503. public function getApplicationTransactionEditor() {
  504. return new PhabricatorOwnersPackageTransactionEditor();
  505. }
  506. public function getApplicationTransactionTemplate() {
  507. return new PhabricatorOwnersPackageTransaction();
  508. }
  509. /* -( PhabricatorCustomFieldInterface )------------------------------------ */
  510. public function getCustomFieldSpecificationForRole($role) {
  511. return PhabricatorEnv::getEnvConfig('owners.fields');
  512. }
  513. public function getCustomFieldBaseClass() {
  514. return 'PhabricatorOwnersCustomField';
  515. }
  516. public function getCustomFields() {
  517. return $this->assertAttached($this->customFields);
  518. }
  519. public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
  520. $this->customFields = $fields;
  521. return $this;
  522. }
  523. /* -( PhabricatorDestructibleInterface )----------------------------------- */
  524. public function destroyObjectPermanently(
  525. PhabricatorDestructionEngine $engine) {
  526. $this->openTransaction();
  527. $conn_w = $this->establishConnection('w');
  528. queryfx(
  529. $conn_w,
  530. 'DELETE FROM %T WHERE packageID = %d',
  531. id(new PhabricatorOwnersPath())->getTableName(),
  532. $this->getID());
  533. queryfx(
  534. $conn_w,
  535. 'DELETE FROM %T WHERE packageID = %d',
  536. id(new PhabricatorOwnersOwner())->getTableName(),
  537. $this->getID());
  538. $this->delete();
  539. $this->saveTransaction();
  540. }
  541. /* -( PhabricatorConduitResultInterface )---------------------------------- */
  542. public function getFieldSpecificationsForConduit() {
  543. return array(
  544. id(new PhabricatorConduitSearchFieldSpecification())
  545. ->setKey('name')
  546. ->setType('string')
  547. ->setDescription(pht('The name of the package.')),
  548. id(new PhabricatorConduitSearchFieldSpecification())
  549. ->setKey('description')
  550. ->setType('string')
  551. ->setDescription(pht('The package description.')),
  552. id(new PhabricatorConduitSearchFieldSpecification())
  553. ->setKey('status')
  554. ->setType('string')
  555. ->setDescription(pht('Active or archived status of the package.')),
  556. id(new PhabricatorConduitSearchFieldSpecification())
  557. ->setKey('owners')
  558. ->setType('list<map<string, wild>>')
  559. ->setDescription(pht('List of package owners.')),
  560. id(new PhabricatorConduitSearchFieldSpecification())
  561. ->setKey('review')
  562. ->setType('map<string, wild>')
  563. ->setDescription(pht('Auto review information.')),
  564. id(new PhabricatorConduitSearchFieldSpecification())
  565. ->setKey('audit')
  566. ->setType('map<string, wild>')
  567. ->setDescription(pht('Auto audit information.')),
  568. id(new PhabricatorConduitSearchFieldSpecification())
  569. ->setKey('dominion')
  570. ->setType('map<string, wild>')
  571. ->setDescription(pht('Dominion setting information.')),
  572. id(new PhabricatorConduitSearchFieldSpecification())
  573. ->setKey('ignored')
  574. ->setType('map<string, wild>')
  575. ->setDescription(pht('Ignored attribute information.')),
  576. );
  577. }
  578. public function getFieldValuesForConduit() {
  579. $owner_list = array();
  580. foreach ($this->getOwners() as $owner) {
  581. $owner_list[] = array(
  582. 'ownerPHID' => $owner->getUserPHID(),
  583. );
  584. }
  585. $review_map = self::getAutoreviewOptionsMap();
  586. $review_value = $this->getAutoReview();
  587. if (isset($review_map[$review_value])) {
  588. $review_label = $review_map[$review_value]['name'];
  589. } else {
  590. $review_label = pht('Unknown ("%s")', $review_value);
  591. }
  592. $review = array(
  593. 'value' => $review_value,
  594. 'label' => $review_label,
  595. );
  596. $audit_rule = $this->newAuditingRule();
  597. $audit = array(
  598. 'value' => $audit_rule->getKey(),
  599. 'label' => $audit_rule->getDisplayName(),
  600. );
  601. $dominion_value = $this->getDominion();
  602. $dominion_map = self::getDominionOptionsMap();
  603. if (isset($dominion_map[$dominion_value])) {
  604. $dominion_label = $dominion_map[$dominion_value]['name'];
  605. $dominion_short = $dominion_map[$dominion_value]['short'];
  606. } else {
  607. $dominion_label = pht('Unknown ("%s")', $dominion_value);
  608. $dominion_short = pht('Unknown ("%s")', $dominion_value);
  609. }
  610. $dominion = array(
  611. 'value' => $dominion_value,
  612. 'label' => $dominion_label,
  613. 'short' => $dominion_short,
  614. );
  615. // Force this to always emit as a JSON object even if empty, never as
  616. // a JSON list.
  617. $ignored = $this->getIgnoredPathAttributes();
  618. if (!$ignored) {
  619. $ignored = (object)array();
  620. }
  621. return array(
  622. 'name' => $this->getName(),
  623. 'description' => $this->getDescription(),
  624. 'status' => $this->getStatus(),
  625. 'owners' => $owner_list,
  626. 'review' => $review,
  627. 'audit' => $audit,
  628. 'dominion' => $dominion,
  629. 'ignored' => $ignored,
  630. );
  631. }
  632. public function getConduitSearchAttachments() {
  633. return array(
  634. id(new PhabricatorOwnersPathsSearchEngineAttachment())
  635. ->setAttachmentKey('paths'),
  636. );
  637. }
  638. /* -( PhabricatorFulltextInterface )--------------------------------------- */
  639. public function newFulltextEngine() {
  640. return new PhabricatorOwnersPackageFulltextEngine();
  641. }
  642. /* -( PhabricatorFerretInterface )----------------------------------------- */
  643. public function newFerretEngine() {
  644. return new PhabricatorOwnersPackageFerretEngine();
  645. }
  646. /* -( PhabricatorNgramsInterface )----------------------------------------- */
  647. public function newNgrams() {
  648. return array(
  649. id(new PhabricatorOwnersPackageNameNgrams())
  650. ->setValue($this->getName()),
  651. );
  652. }
  653. }