@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Convert Owners paths to application transactions

Summary:
Ref T8320. Fixes T8317. Fixes T2831. Fixes T8073. Fixes T7127.

There was a bug with this line:

for ($ii = 0; $ii < count($paths); $ii++) {

...because the array may be sparse if there have been deletes, so `count($paths)` might be 3, but the real keys could be `1`, `5` and `6`. I think this was the primary issue behind T7127.

The old Editor did a lot of work to try to validate paths. When a path failed to validate, it silently discarded it. This was silly and pointless: it's incredibly bad UX; and it's totally fine if users saves "invalid" paths. This was likely the cause of T8317, and probably the cause of T8073.

T2831 I'm less sure about, but I can't reproduce it and I rewrote all the logic so I suspect it's gone.

This also records and shows edits, so if stuff does keep happening it should be more clear what's going on.

I removed some adjacent stuff:

- I removed the ability to delete packages. I'll add "disable" in a future diff, plus `bin/remove destroy`, like other objects. Getting rid of this now let me get rid of all the mail stuff.
- I removed "path validation" where packages would try to automatically update in response to commits. This doesn't necessarily make sense in Git/Mercurial, is sketchy, could easily have been the source of T2831, and seems generally complicated and not very valuable. We could maybe restore it some day, but I'd like to get Owners stable before trying to do crazy stuff like that.

Test Plan: {F437687}

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T8317, T8073, T7127, T2831, T8320

Differential Revision: https://secure.phabricator.com/D13032

+204 -851
+3 -3
resources/celerity/map.php
··· 7 7 */ 8 8 return array( 9 9 'names' => array( 10 - 'core.pkg.css' => '4e7df908', 10 + 'core.pkg.css' => '97a49e3e', 11 11 'core.pkg.js' => '328799d0', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => '30602b8c', ··· 26 26 'rsrc/css/aphront/pager-view.css' => '2e3539af', 27 27 'rsrc/css/aphront/panel-view.css' => '8427b78d', 28 28 'rsrc/css/aphront/phabricator-nav-view.css' => '7aeaf435', 29 - 'rsrc/css/aphront/table-view.css' => '59e2c0f8', 29 + 'rsrc/css/aphront/table-view.css' => '0b4cd283', 30 30 'rsrc/css/aphront/tokenizer.css' => '86a13f7f', 31 31 'rsrc/css/aphront/tooltip.css' => '7672b60f', 32 32 'rsrc/css/aphront/two-column.css' => '16ab3ad2', ··· 489 489 'aphront-multi-column-view-css' => 'fd18389d', 490 490 'aphront-pager-view-css' => '2e3539af', 491 491 'aphront-panel-view-css' => '8427b78d', 492 - 'aphront-table-view-css' => '59e2c0f8', 492 + 'aphront-table-view-css' => '0b4cd283', 493 493 'aphront-tokenizer-control-css' => '86a13f7f', 494 494 'aphront-tooltip-css' => '7672b60f', 495 495 'aphront-two-column-view-css' => '16ab3ad2',
-13
src/__phutil_library_map__.php
··· 1230 1230 'PHUITypeaheadExample' => 'applications/uiexample/examples/PHUITypeaheadExample.php', 1231 1231 'PHUIWorkboardView' => 'view/phui/PHUIWorkboardView.php', 1232 1232 'PHUIWorkpanelView' => 'view/phui/PHUIWorkpanelView.php', 1233 - 'PackageCreateMail' => 'applications/owners/mail/PackageCreateMail.php', 1234 - 'PackageDeleteMail' => 'applications/owners/mail/PackageDeleteMail.php', 1235 - 'PackageMail' => 'applications/owners/mail/PackageMail.php', 1236 - 'PackageModifyMail' => 'applications/owners/mail/PackageModifyMail.php', 1237 1233 'PassphraseAbstractKey' => 'applications/passphrase/keys/PassphraseAbstractKey.php', 1238 1234 'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php', 1239 1235 'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php', ··· 2167 2163 'PhabricatorOwnersConfigOptions' => 'applications/owners/config/PhabricatorOwnersConfigOptions.php', 2168 2164 'PhabricatorOwnersController' => 'applications/owners/controller/PhabricatorOwnersController.php', 2169 2165 'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php', 2170 - 'PhabricatorOwnersDeleteController' => 'applications/owners/controller/PhabricatorOwnersDeleteController.php', 2171 2166 'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php', 2172 2167 'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php', 2173 2168 'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php', 2174 2169 'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php', 2175 2170 'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php', 2176 2171 'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php', 2177 - 'PhabricatorOwnersPackageEditor' => 'applications/owners/editor/PhabricatorOwnersPackageEditor.php', 2178 2172 'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php', 2179 - 'PhabricatorOwnersPackagePathValidator' => 'applications/repository/worker/commitchangeparser/PhabricatorOwnersPackagePathValidator.php', 2180 2173 'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php', 2181 2174 'PhabricatorOwnersPackageSearchEngine' => 'applications/owners/query/PhabricatorOwnersPackageSearchEngine.php', 2182 2175 'PhabricatorOwnersPackageTestCase' => 'applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php', ··· 4565 4558 'PHUITypeaheadExample' => 'PhabricatorUIExample', 4566 4559 'PHUIWorkboardView' => 'AphrontTagView', 4567 4560 'PHUIWorkpanelView' => 'AphrontTagView', 4568 - 'PackageCreateMail' => 'PackageMail', 4569 - 'PackageDeleteMail' => 'PackageMail', 4570 - 'PackageMail' => 'PhabricatorMail', 4571 - 'PackageModifyMail' => 'PackageMail', 4572 4561 'PassphraseAbstractKey' => 'Phobject', 4573 4562 'PassphraseConduitAPIMethod' => 'ConduitAPIMethod', 4574 4563 'PassphraseController' => 'PhabricatorController', ··· 5576 5565 'PhabricatorOwnersConfigOptions' => 'PhabricatorApplicationConfigOptions', 5577 5566 'PhabricatorOwnersController' => 'PhabricatorController', 5578 5567 'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO', 5579 - 'PhabricatorOwnersDeleteController' => 'PhabricatorOwnersController', 5580 5568 'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController', 5581 5569 'PhabricatorOwnersEditController' => 'PhabricatorOwnersController', 5582 5570 'PhabricatorOwnersListController' => 'PhabricatorOwnersController', ··· 5587 5575 'PhabricatorApplicationTransactionInterface', 5588 5576 ), 5589 5577 'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource', 5590 - 'PhabricatorOwnersPackageEditor' => 'PhabricatorEditor', 5591 5578 'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType', 5592 5579 'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 5593 5580 'PhabricatorOwnersPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
-1
src/applications/owners/application/PhabricatorOwnersApplication.php
··· 46 46 'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersEditController', 47 47 'new/' => 'PhabricatorOwnersEditController', 48 48 'package/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersDetailController', 49 - 'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersDeleteController', 50 49 'paths/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersPathsController', 51 50 ), 52 51 );
-44
src/applications/owners/controller/PhabricatorOwnersDeleteController.php
··· 1 - <?php 2 - 3 - final class PhabricatorOwnersDeleteController 4 - extends PhabricatorOwnersController { 5 - 6 - private $id; 7 - 8 - public function willProcessRequest(array $data) { 9 - $this->id = $data['id']; 10 - } 11 - 12 - public function processRequest() { 13 - $request = $this->getRequest(); 14 - $user = $request->getUser(); 15 - 16 - $package = id(new PhabricatorOwnersPackage())->load($this->id); 17 - if (!$package) { 18 - return new Aphront404Response(); 19 - } 20 - 21 - if ($request->isDialogFormPost()) { 22 - id(new PhabricatorOwnersPackageEditor()) 23 - ->setActor($user) 24 - ->setPackage($package) 25 - ->delete(); 26 - return id(new AphrontRedirectResponse())->setURI('/owners/'); 27 - } 28 - 29 - $text = pht( 30 - 'Are you sure you want to delete the "%s" package? This '. 31 - 'operation can not be undone.', 32 - $package->getName()); 33 - $dialog = id(new AphrontDialogView()) 34 - ->setUser($user) 35 - ->setTitle(pht('Really delete this package?')) 36 - ->appendChild(phutil_tag('p', array(), $text)) 37 - ->addSubmitButton(pht('Delete')) 38 - ->addCancelButton('/owners/package/'.$package->getID().'/') 39 - ->setSubmitURI($request->getRequestURI()); 40 - 41 - return id(new AphrontDialogResponse())->setDialog($dialog); 42 - } 43 - 44 - }
+1 -9
src/applications/owners/controller/PhabricatorOwnersDetailController.php
··· 195 195 $id = $package->getID(); 196 196 $edit_uri = $this->getApplicationURI("/edit/{$id}/"); 197 197 $paths_uri = $this->getApplicationURI("/paths/{$id}/"); 198 - $delete_uri = $this->getApplicationURI("/delete/{$id}/"); 199 198 200 199 $view = id(new PhabricatorActionListView()) 201 200 ->setUser($viewer) ··· 213 212 ->setIcon('fa-folder-open') 214 213 ->setDisabled(!$can_edit) 215 214 ->setWorkflow(!$can_edit) 216 - ->setHref($paths_uri)) 217 - ->addAction( 218 - id(new PhabricatorActionView()) 219 - ->setName(pht('Delete Package')) 220 - ->setIcon('fa-times') 221 - ->setDisabled(!$can_edit) 222 - ->setWorkflow(true) 223 - ->setHref($delete_uri)); 215 + ->setHref($paths_uri)); 224 216 225 217 return $view; 226 218 }
+30 -21
src/applications/owners/controller/PhabricatorOwnersPathsController.php
··· 26 26 $excludes = $request->getArr('exclude'); 27 27 28 28 $path_refs = array(); 29 - for ($ii = 0; $ii < count($paths); $ii++) { 30 - if (empty($paths[$ii]) || empty($repos[$ii])) { 31 - continue; 29 + foreach ($paths as $key => $path) { 30 + if (!isset($repos[$key])) { 31 + throw new Exception( 32 + pht( 33 + 'No repository PHID for path "%s"!', 34 + $key)); 32 35 } 36 + 37 + if (!isset($excludes[$key])) { 38 + throw new Exception( 39 + pht( 40 + 'No exclusion value for path "%s"!', 41 + $key)); 42 + } 43 + 33 44 $path_refs[] = array( 34 - 'repositoryPHID' => $repos[$ii], 35 - 'path' => $paths[$ii], 36 - 'excluded' => $excludes[$ii], 45 + 'repositoryPHID' => $repos[$key], 46 + 'path' => $path, 47 + 'excluded' => (int)$excludes[$key], 37 48 ); 38 49 } 39 50 40 - $package->attachUnsavedOwners(array()); 41 - $package->attachUnsavedPaths($path_refs); 42 - $package->attachOldAuditingEnabled($package->getAuditingEnabled()); 43 - $package->attachOldPrimaryOwnerPHID($package->getPrimaryOwnerPHID()); 51 + $type_paths = PhabricatorOwnersPackageTransaction::TYPE_PATHS; 52 + 53 + $xactions = array(); 54 + $xactions[] = id(new PhabricatorOwnersPackageTransaction()) 55 + ->setTransactionType($type_paths) 56 + ->setNewValue($path_refs); 44 57 45 - id(new PhabricatorOwnersPackageEditor()) 58 + $editor = id(new PhabricatorOwnersPackageTransactionEditor()) 46 59 ->setActor($viewer) 47 - ->setPackage($package) 48 - ->save(); 60 + ->setContentSourceFromRequest($request) 61 + ->setContinueOnNoEffect(true) 62 + ->setContinueOnMissingFields(true); 63 + 64 + $editor->applyTransactions($package, $xactions); 49 65 50 66 return id(new AphrontRedirectResponse()) 51 67 ->setURI('/owners/package/'.$package->getID().'/'); 52 68 } else { 53 69 $paths = $package->loadPaths(); 54 - $path_refs = array(); 55 - foreach ($paths as $path) { 56 - $path_refs[] = array( 57 - 'repositoryPHID' => $path->getRepositoryPHID(), 58 - 'path' => $path->getPath(), 59 - 'excluded' => $path->getExcluded(), 60 - ); 61 - } 70 + $path_refs = mpull($paths, 'getRef'); 62 71 } 63 72 64 73 $repos = id(new PhabricatorRepositoryQuery())
-198
src/applications/owners/editor/PhabricatorOwnersPackageEditor.php
··· 1 - <?php 2 - 3 - final class PhabricatorOwnersPackageEditor extends PhabricatorEditor { 4 - 5 - private $package; 6 - 7 - public function setPackage(PhabricatorOwnersPackage $package) { 8 - $this->package = $package; 9 - return $this; 10 - } 11 - 12 - public function getPackage() { 13 - return $this->package; 14 - } 15 - 16 - public function save() { 17 - $actor = $this->getActor(); 18 - $package = $this->getPackage(); 19 - $package->attachActorPHID($actor->getPHID()); 20 - 21 - if ($package->getID()) { 22 - $is_new = false; 23 - } else { 24 - $is_new = true; 25 - } 26 - 27 - $package->openTransaction(); 28 - 29 - $ret = $package->save(); 30 - 31 - $add_owners = array(); 32 - $remove_owners = array(); 33 - $all_owners = array(); 34 - if ($package->getUnsavedOwners()) { 35 - $new_owners = array_fill_keys($package->getUnsavedOwners(), true); 36 - $cur_owners = array(); 37 - foreach ($package->loadOwners() as $owner) { 38 - if (empty($new_owners[$owner->getUserPHID()])) { 39 - $remove_owners[$owner->getUserPHID()] = true; 40 - $owner->delete(); 41 - continue; 42 - } 43 - $cur_owners[$owner->getUserPHID()] = true; 44 - } 45 - 46 - $add_owners = array_diff_key($new_owners, $cur_owners); 47 - $all_owners = array_merge( 48 - array($package->getPrimaryOwnerPHID() => true), 49 - $new_owners, 50 - $remove_owners); 51 - foreach ($add_owners as $phid => $ignored) { 52 - $owner = new PhabricatorOwnersOwner(); 53 - $owner->setPackageID($package->getID()); 54 - $owner->setUserPHID($phid); 55 - $owner->save(); 56 - } 57 - $package->attachUnsavedOwners(array()); 58 - } 59 - 60 - $add_paths = array(); 61 - $remove_paths = array(); 62 - $touched_repos = array(); 63 - if ($package->getUnsavedPaths()) { 64 - $new_paths = igroup( 65 - $package->getUnsavedPaths(), 66 - 'repositoryPHID', 67 - 'path'); 68 - $cur_paths = $package->loadPaths(); 69 - foreach ($cur_paths as $key => $path) { 70 - $repository_phid = $path->getRepositoryPHID(); 71 - $new_path = head(idx( 72 - idx($new_paths, $repository_phid, array()), 73 - $path->getPath(), 74 - array())); 75 - $excluded = $path->getExcluded(); 76 - if ($new_path === false || 77 - idx($new_path, 'excluded') != $excluded) { 78 - $touched_repos[$repository_phid] = true; 79 - $remove_paths[$repository_phid][$path->getPath()] = $excluded; 80 - $path->delete(); 81 - unset($cur_paths[$key]); 82 - } 83 - } 84 - 85 - $cur_paths = mgroup($cur_paths, 'getRepositoryPHID', 'getPath'); 86 - $repositories = id(new PhabricatorRepositoryQuery()) 87 - ->setViewer($actor) 88 - ->withPHIDs(array_keys($cur_paths)) 89 - ->execute(); 90 - $repositories = mpull($repositories, null, 'getPHID'); 91 - foreach ($new_paths as $repository_phid => $paths) { 92 - $repository = idx($repositories, $repository_phid); 93 - if (!$repository) { 94 - continue; 95 - } 96 - foreach ($paths as $path => $dicts) { 97 - $path = ltrim($path, '/'); 98 - // build query to validate path 99 - $drequest = DiffusionRequest::newFromDictionary( 100 - array( 101 - 'user' => $actor, 102 - 'repository' => $repository, 103 - 'path' => $path, 104 - )); 105 - $results = DiffusionBrowseResultSet::newFromConduit( 106 - DiffusionQuery::callConduitWithDiffusionRequest( 107 - $actor, 108 - $drequest, 109 - 'diffusion.browsequery', 110 - array( 111 - 'commit' => $drequest->getCommit(), 112 - 'path' => $path, 113 - 'needValidityOnly' => true, 114 - ))); 115 - $valid = $results->isValidResults(); 116 - $is_directory = true; 117 - if (!$valid) { 118 - switch ($results->getReasonForEmptyResultSet()) { 119 - case DiffusionBrowseResultSet::REASON_IS_FILE: 120 - $valid = true; 121 - $is_directory = false; 122 - break; 123 - case DiffusionBrowseResultSet::REASON_IS_EMPTY: 124 - $valid = true; 125 - break; 126 - } 127 - } 128 - if ($is_directory && substr($path, -1) != '/') { 129 - $path .= '/'; 130 - } 131 - if (substr($path, 0, 1) != '/') { 132 - $path = '/'.$path; 133 - } 134 - if (empty($cur_paths[$repository_phid][$path]) && $valid) { 135 - $touched_repos[$repository_phid] = true; 136 - $excluded = idx(reset($dicts), 'excluded', 0); 137 - $add_paths[$repository_phid][$path] = $excluded; 138 - $obj = new PhabricatorOwnersPath(); 139 - $obj->setPackageID($package->getID()); 140 - $obj->setRepositoryPHID($repository_phid); 141 - $obj->setPath($path); 142 - $obj->setExcluded($excluded); 143 - $obj->save(); 144 - } 145 - } 146 - } 147 - $package->attachUnsavedPaths(array()); 148 - } 149 - 150 - $package->saveTransaction(); 151 - 152 - if ($is_new) { 153 - $mail = new PackageCreateMail($package); 154 - } else { 155 - $mail = new PackageModifyMail( 156 - $package, 157 - array_keys($add_owners), 158 - array_keys($remove_owners), 159 - array_keys($all_owners), 160 - array_keys($touched_repos), 161 - $add_paths, 162 - $remove_paths); 163 - } 164 - $mail->setActor($actor); 165 - $mail->send(); 166 - 167 - return $ret; 168 - } 169 - 170 - public function delete() { 171 - $actor = $this->getActor(); 172 - $package = $this->getPackage(); 173 - $package->attachActorPHID($actor->getPHID()); 174 - 175 - $mails = id(new PackageDeleteMail($package)) 176 - ->setActor($actor) 177 - ->prepareMails(); 178 - 179 - $package->openTransaction(); 180 - 181 - foreach ($package->loadOwners() as $owner) { 182 - $owner->delete(); 183 - } 184 - foreach ($package->loadPaths() as $path) { 185 - $path->delete(); 186 - } 187 - $ret = $package->delete(); 188 - 189 - $package->saveTransaction(); 190 - 191 - foreach ($mails as $mail) { 192 - $mail->saveAndSend(); 193 - } 194 - 195 - return $ret; 196 - } 197 - 198 - }
+50
src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php
··· 19 19 $types[] = PhabricatorOwnersPackageTransaction::TYPE_OWNERS; 20 20 $types[] = PhabricatorOwnersPackageTransaction::TYPE_AUDITING; 21 21 $types[] = PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION; 22 + $types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS; 22 23 23 24 return $types; 24 25 } ··· 41 42 return (int)$object->getAuditingEnabled(); 42 43 case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: 43 44 return $object->getDescription(); 45 + case PhabricatorOwnersPackageTransaction::TYPE_PATHS: 46 + // TODO: needPaths() this on the query 47 + $paths = $object->loadPaths(); 48 + return mpull($paths, 'getRef'); 44 49 } 45 50 } 46 51 ··· 52 57 case PhabricatorOwnersPackageTransaction::TYPE_NAME: 53 58 case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY: 54 59 case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: 60 + case PhabricatorOwnersPackageTransaction::TYPE_PATHS: 55 61 return $xaction->getNewValue(); 56 62 case PhabricatorOwnersPackageTransaction::TYPE_AUDITING: 57 63 return (int)$xaction->getNewValue(); ··· 63 69 } 64 70 } 65 71 72 + protected function transactionHasEffect( 73 + PhabricatorLiskDAO $object, 74 + PhabricatorApplicationTransaction $xaction) { 75 + 76 + switch ($xaction->getTransactionType()) { 77 + case PhabricatorOwnersPackageTransaction::TYPE_PATHS: 78 + $old = $xaction->getOldValue(); 79 + $new = $xaction->getNewValue(); 80 + 81 + $diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new); 82 + list($rem, $add) = $diffs; 83 + 84 + return ($rem || $add); 85 + } 86 + 87 + return parent::transactionHasEffect($object, $xaction); 88 + } 89 + 66 90 protected function applyCustomInternalTransaction( 67 91 PhabricatorLiskDAO $object, 68 92 PhabricatorApplicationTransaction $xaction) { ··· 81 105 $object->setAuditingEnabled($xaction->getNewValue()); 82 106 return; 83 107 case PhabricatorOwnersPackageTransaction::TYPE_OWNERS: 108 + case PhabricatorOwnersPackageTransaction::TYPE_PATHS: 84 109 return; 85 110 } 86 111 ··· 122 147 } 123 148 124 149 // TODO: Attach owners here 150 + return; 151 + case PhabricatorOwnersPackageTransaction::TYPE_PATHS: 152 + $old = $xaction->getOldValue(); 153 + $new = $xaction->getNewValue(); 154 + 155 + // TODO: needPaths this 156 + $paths = $object->loadPaths(); 157 + 158 + $diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new); 159 + list($rem, $add) = $diffs; 160 + 161 + $set = PhabricatorOwnersPath::getSetFromTransactionValue($rem); 162 + foreach ($paths as $path) { 163 + $ref = $path->getRef(); 164 + if (PhabricatorOwnersPath::isRefInSet($ref, $set)) { 165 + $path->delete(); 166 + } 167 + } 168 + 169 + foreach ($add as $ref) { 170 + $path = PhabricatorOwnersPath::newFromRef($ref) 171 + ->setPackageID($object->getID()) 172 + ->save(); 173 + } 174 + 125 175 return; 126 176 } 127 177
-12
src/applications/owners/mail/PackageCreateMail.php
··· 1 - <?php 2 - 3 - final class PackageCreateMail extends PackageMail { 4 - 5 - protected function isNewThread() { 6 - return true; 7 - } 8 - 9 - protected function getVerb() { 10 - return pht('Created'); 11 - } 12 - }
-13
src/applications/owners/mail/PackageDeleteMail.php
··· 1 - <?php 2 - 3 - final class PackageDeleteMail extends PackageMail { 4 - 5 - protected function getVerb() { 6 - return pht('Deleted'); 7 - } 8 - 9 - protected function isNewThread() { 10 - return false; 11 - } 12 - 13 - }
-212
src/applications/owners/mail/PackageMail.php
··· 1 - <?php 2 - 3 - abstract class PackageMail extends PhabricatorMail { 4 - 5 - protected $package; 6 - protected $handles; 7 - protected $owners; 8 - protected $paths; 9 - protected $mailTo; 10 - 11 - public function __construct(PhabricatorOwnersPackage $package) { 12 - $this->package = $package; 13 - } 14 - 15 - abstract protected function getVerb(); 16 - 17 - abstract protected function isNewThread(); 18 - 19 - final protected function getPackage() { 20 - return $this->package; 21 - } 22 - 23 - final protected function getHandles() { 24 - return $this->handles; 25 - } 26 - 27 - final protected function getOwners() { 28 - return $this->owners; 29 - } 30 - 31 - final protected function getPaths() { 32 - return $this->paths; 33 - } 34 - 35 - final protected function getMailTo() { 36 - return $this->mailTo; 37 - } 38 - 39 - final protected function renderPackageTitle() { 40 - return $this->getPackage()->getName(); 41 - } 42 - 43 - final protected function renderRepoSubSection($repository_phid, $paths) { 44 - $handles = $this->getHandles(); 45 - $section = array(); 46 - $section[] = ' '. 47 - pht('In repository %s', $handles[$repository_phid]->getName()). 48 - ' - '.PhabricatorEnv::getProductionURI($handles[$repository_phid] 49 - ->getURI()); 50 - foreach ($paths as $path => $excluded) { 51 - $section[] = ' '. 52 - ($excluded ? pht('Excluded') : pht('Included')).' '.$path; 53 - } 54 - 55 - return implode("\n", $section); 56 - } 57 - 58 - protected function needSend() { 59 - return true; 60 - } 61 - 62 - protected function loadData() { 63 - $package = $this->getPackage(); 64 - $owners = $package->loadOwners(); 65 - $this->owners = $owners; 66 - 67 - $owner_phids = mpull($owners, 'getUserPHID'); 68 - $primary_owner_phid = $package->getPrimaryOwnerPHID(); 69 - $mail_to = $owner_phids; 70 - if (!in_array($primary_owner_phid, $owner_phids)) { 71 - $mail_to[] = $primary_owner_phid; 72 - } 73 - $this->mailTo = $mail_to; 74 - 75 - $this->paths = array(); 76 - $repository_paths = mgroup($package->loadPaths(), 'getRepositoryPHID'); 77 - foreach ($repository_paths as $repository_phid => $paths) { 78 - $this->paths[$repository_phid] = mpull($paths, 'getExcluded', 'getPath'); 79 - } 80 - 81 - $phids = array_merge( 82 - $this->mailTo, 83 - array($package->getActorPHID()), 84 - array_keys($this->paths)); 85 - $this->handles = id(new PhabricatorHandleQuery()) 86 - ->setViewer($this->getActor()) 87 - ->withPHIDs($phids) 88 - ->execute(); 89 - } 90 - 91 - final protected function renderSummarySection() { 92 - $package = $this->getPackage(); 93 - $handles = $this->getHandles(); 94 - $section = array(); 95 - $section[] = $handles[$package->getActorPHID()]->getName().' '. 96 - strtolower($this->getVerb()).' '.$this->renderPackageTitle().'.'; 97 - $section[] = ''; 98 - 99 - $section[] = pht('PACKAGE DETAIL'); 100 - $section[] = ' '.PhabricatorEnv::getProductionURI( 101 - '/owners/package/'.$package->getID().'/'); 102 - 103 - return implode("\n", $section); 104 - } 105 - 106 - protected function renderDescriptionSection() { 107 - return pht('PACKAGE DESCRIPTION')."\n ". 108 - $this->getPackage()->getDescription(); 109 - } 110 - 111 - protected function renderPrimaryOwnerSection() { 112 - $handles = $this->getHandles(); 113 - return pht('PRIMARY OWNER')."\n ". 114 - $handles[$this->getPackage()->getPrimaryOwnerPHID()]->getName(); 115 - } 116 - 117 - protected function renderOwnersSection() { 118 - $handles = $this->getHandles(); 119 - $owners = $this->getOwners(); 120 - if (!$owners) { 121 - return null; 122 - } 123 - 124 - $owners = mpull($owners, 'getUserPHID'); 125 - $owners = array_select_keys($handles, $owners); 126 - $owners = mpull($owners, 'getName'); 127 - return pht('OWNERS')."\n ".implode(', ', $owners); 128 - } 129 - 130 - protected function renderAuditingEnabledSection() { 131 - return pht('AUDITING ENABLED STATUS')."\n ". 132 - ($this->getPackage()->getAuditingEnabled() 133 - ? pht('Enabled') 134 - : pht('Disabled')); 135 - } 136 - 137 - protected function renderPathsSection() { 138 - $section = array(); 139 - $section[] = pht('PATHS'); 140 - foreach ($this->paths as $repository_phid => $paths) { 141 - $section[] = $this->renderRepoSubSection($repository_phid, $paths); 142 - } 143 - 144 - return implode("\n", $section); 145 - } 146 - 147 - final protected function renderBody() { 148 - $body = array(); 149 - $body[] = $this->renderSummarySection(); 150 - $body[] = $this->renderDescriptionSection(); 151 - $body[] = $this->renderPrimaryOwnerSection(); 152 - $body[] = $this->renderOwnersSection(); 153 - $body[] = $this->renderAuditingEnabledSection(); 154 - $body[] = $this->renderPathsSection(); 155 - $body = array_filter($body); 156 - return implode("\n\n", $body)."\n"; 157 - } 158 - 159 - final public function send() { 160 - $mails = $this->prepareMails(); 161 - 162 - foreach ($mails as $mail) { 163 - $mail->saveAndSend(); 164 - } 165 - } 166 - 167 - final public function prepareMails() { 168 - if (!$this->needSend()) { 169 - return array(); 170 - } 171 - 172 - $this->loadData(); 173 - 174 - $package = $this->getPackage(); 175 - $prefix = PhabricatorEnv::getEnvConfig('metamta.package.subject-prefix'); 176 - $verb = $this->getVerb(); 177 - $threading = $this->getMailThreading(); 178 - list($thread_id, $thread_topic) = $threading; 179 - 180 - $template = id(new PhabricatorMetaMTAMail()) 181 - ->setSubject($this->renderPackageTitle()) 182 - ->setSubjectPrefix($prefix) 183 - ->setVarySubjectPrefix("[{$verb}]") 184 - ->setFrom($package->getActorPHID()) 185 - ->setThreadID($thread_id, $this->isNewThread()) 186 - ->addHeader('Thread-Topic', $thread_topic) 187 - ->setRelatedPHID($package->getPHID()) 188 - ->setIsBulk(true) 189 - ->setBody($this->renderBody()); 190 - 191 - $reply_handler = $this->newReplyHandler(); 192 - $mails = $reply_handler->multiplexMail( 193 - $template, 194 - array_select_keys($this->getHandles(), $this->getMailTo()), 195 - array()); 196 - return $mails; 197 - } 198 - 199 - private function getMailThreading() { 200 - return array( 201 - 'package-'.$this->getPackage()->getPHID(), 202 - 'Package '.$this->getPackage()->getOriginalName(), 203 - ); 204 - } 205 - 206 - private function newReplyHandler() { 207 - $reply_handler = new OwnersPackageReplyHandler(); 208 - $reply_handler->setMailReceiver($this->getPackage()); 209 - return $reply_handler; 210 - } 211 - 212 - }
-160
src/applications/owners/mail/PackageModifyMail.php
··· 1 - <?php 2 - 3 - final class PackageModifyMail extends PackageMail { 4 - 5 - protected $addOwners; 6 - protected $removeOwners; 7 - protected $allOwners; 8 - protected $touchedRepos; 9 - protected $addPaths; 10 - protected $removePaths; 11 - 12 - public function __construct( 13 - PhabricatorOwnersPackage $package, 14 - $add_owners, 15 - $remove_owners, 16 - $all_owners, 17 - $touched_repos, 18 - $add_paths, 19 - $remove_paths) { 20 - 21 - $this->package = $package; 22 - 23 - $this->addOwners = $add_owners; 24 - $this->removeOwners = $remove_owners; 25 - $this->allOwners = $all_owners; 26 - $this->touchedRepos = $touched_repos; 27 - $this->addPaths = $add_paths; 28 - $this->removePaths = $remove_paths; 29 - } 30 - 31 - protected function getVerb() { 32 - return pht('Modified'); 33 - } 34 - 35 - protected function isNewThread() { 36 - return false; 37 - } 38 - 39 - protected function needSend() { 40 - $package = $this->getPackage(); 41 - if ($package->getOldPrimaryOwnerPHID() !== $package->getPrimaryOwnerPHID() 42 - || $package->getOldAuditingEnabled() != $package->getAuditingEnabled() 43 - || $this->addOwners 44 - || $this->removeOwners 45 - || $this->addPaths 46 - || $this->removePaths) { 47 - return true; 48 - } else { 49 - return false; 50 - } 51 - } 52 - 53 - protected function loadData() { 54 - $this->mailTo = $this->allOwners; 55 - 56 - $phids = array_merge( 57 - $this->allOwners, 58 - $this->touchedRepos, 59 - array( 60 - $this->getPackage()->getActorPHID(), 61 - )); 62 - $this->handles = id(new PhabricatorHandleQuery()) 63 - ->setViewer($this->getActor()) 64 - ->withPHIDs($phids) 65 - ->execute(); 66 - } 67 - 68 - protected function renderDescriptionSection() { 69 - return null; 70 - } 71 - 72 - protected function renderPrimaryOwnerSection() { 73 - $package = $this->getPackage(); 74 - $handles = $this->getHandles(); 75 - 76 - $old_primary_owner_phid = $package->getOldPrimaryOwnerPHID(); 77 - $primary_owner_phid = $package->getPrimaryOwnerPHID(); 78 - if ($old_primary_owner_phid == $primary_owner_phid) { 79 - return null; 80 - } 81 - 82 - $section = array(); 83 - $section[] = pht('PRIMARY OWNER CHANGE'); 84 - $section[] = ' '.pht('Old owner:').' '. 85 - $handles[$old_primary_owner_phid]->getName(); 86 - $section[] = ' '.pht('New owner:').' '. 87 - $handles[$primary_owner_phid]->getName(); 88 - 89 - return implode("\n", $section); 90 - } 91 - 92 - protected function renderOwnersSection() { 93 - $section = array(); 94 - $add_owners = $this->addOwners; 95 - $remove_owners = $this->removeOwners; 96 - $handles = $this->getHandles(); 97 - 98 - if ($add_owners) { 99 - $add_owners = array_select_keys($handles, $add_owners); 100 - $add_owners = mpull($add_owners, 'getName'); 101 - $section[] = pht('ADDED OWNERS'); 102 - $section[] = ' '.implode(', ', $add_owners); 103 - } 104 - 105 - if ($remove_owners) { 106 - if ($add_owners) { 107 - $section[] = ''; 108 - } 109 - $remove_owners = array_select_keys($handles, $remove_owners); 110 - $remove_owners = mpull($remove_owners, 'getName'); 111 - $section[] = pht('REMOVED OWNERS'); 112 - $section[] = ' '.implode(', ', $remove_owners); 113 - } 114 - 115 - if ($section) { 116 - return implode("\n", $section); 117 - } else { 118 - return null; 119 - } 120 - } 121 - 122 - protected function renderAuditingEnabledSection() { 123 - $package = $this->getPackage(); 124 - $old_auditing_enabled = $package->getOldAuditingEnabled(); 125 - $auditing_enabled = $package->getAuditingEnabled(); 126 - if ($old_auditing_enabled == $auditing_enabled) { 127 - return null; 128 - } 129 - 130 - $section = array(); 131 - $section[] = pht('AUDITING ENABLED STATUS CHANGE'); 132 - $section[] = ' '.pht('Old value:').' '. 133 - ($old_auditing_enabled ? pht('Enabled') : pht('Disabled')); 134 - $section[] = ' '.pht('New value:').' '. 135 - ($auditing_enabled ? pht('Enabled') : pht('Disabled')); 136 - return implode("\n", $section); 137 - } 138 - 139 - protected function renderPathsSection() { 140 - $section = array(); 141 - if ($this->addPaths) { 142 - $section[] = pht('ADDED PATHS'); 143 - foreach ($this->addPaths as $repository_phid => $paths) { 144 - $section[] = $this->renderRepoSubSection($repository_phid, $paths); 145 - } 146 - } 147 - 148 - if ($this->removePaths) { 149 - if ($this->addPaths) { 150 - $section[] = ''; 151 - } 152 - $section[] = pht('REMOVED PATHS'); 153 - foreach ($this->removePaths as $repository_phid => $paths) { 154 - $section[] = $this->renderRepoSubSection($repository_phid, $paths); 155 - } 156 - } 157 - return implode("\n", $section); 158 - } 159 - 160 - }
+3 -54
src/applications/owners/storage/PhabricatorOwnersPackage.php
··· 13 13 protected $primaryOwnerPHID; 14 14 protected $mailKey; 15 15 16 - private $unsavedOwners = self::ATTACHABLE; 17 - private $unsavedPaths = self::ATTACHABLE; 18 - private $actorPHID; 19 - private $oldPrimaryOwnerPHID; 20 - private $oldAuditingEnabled; 21 - 22 16 public static function initializeNewPackage(PhabricatorUser $actor) { 23 17 return id(new PhabricatorOwnersPackage()) 24 18 ->setAuditingEnabled(0) ··· 83 77 return parent::save(); 84 78 } 85 79 86 - public function attachUnsavedOwners(array $owners) { 87 - $this->unsavedOwners = $owners; 88 - return $this; 89 - } 90 - 91 - public function getUnsavedOwners() { 92 - return $this->assertAttached($this->unsavedOwners); 93 - } 94 - 95 - public function attachUnsavedPaths(array $paths) { 96 - $this->unsavedPaths = $paths; 97 - return $this; 98 - } 99 - 100 - public function getUnsavedPaths() { 101 - return $this->assertAttached($this->unsavedPaths); 102 - } 103 - 104 - public function attachActorPHID($actor_phid) { 105 - $this->actorPHID = $actor_phid; 106 - return $this; 107 - } 108 - 109 - public function getActorPHID() { 110 - return $this->actorPHID; 111 - } 112 - 113 - public function attachOldPrimaryOwnerPHID($old_primary) { 114 - $this->oldPrimaryOwnerPHID = $old_primary; 115 - return $this; 116 - } 117 - 118 - public function getOldPrimaryOwnerPHID() { 119 - return $this->oldPrimaryOwnerPHID; 120 - } 121 - 122 - public function attachOldAuditingEnabled($auditing_enabled) { 123 - $this->oldAuditingEnabled = $auditing_enabled; 124 - return $this; 125 - } 126 - 127 - public function getOldAuditingEnabled() { 128 - return $this->oldAuditingEnabled; 129 - } 130 - 131 80 public function setName($name) { 132 81 $this->name = $name; 133 82 if (!$this->getID()) { ··· 163 112 } 164 113 165 114 return self::loadPackagesForPaths($repository, $paths); 166 - } 115 + } 167 116 168 - public static function loadOwningPackages($repository, $path) { 117 + public static function loadOwningPackages($repository, $path) { 169 118 if (empty($path)) { 170 119 return array(); 171 120 } 172 121 173 122 return self::loadPackagesForPaths($repository, array($path), 1); 174 - } 123 + } 175 124 176 125 private static function loadPackagesForPaths( 177 126 PhabricatorRepository $repository,
+59
src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php
··· 8 8 const TYPE_OWNERS = 'owners.owners'; 9 9 const TYPE_AUDITING = 'owners.auditing'; 10 10 const TYPE_DESCRIPTION = 'owners.description'; 11 + const TYPE_PATHS = 'owners.paths'; 11 12 12 13 public function getApplicationName() { 13 14 return 'owners'; ··· 120 121 return pht( 121 122 '%s updated the description for this package.', 122 123 $this->renderHandleLink($author_phid)); 124 + case self::TYPE_PATHS: 125 + // TODO: Flesh this out. 126 + return pht( 127 + '%s updated paths for this package.', 128 + $this->renderHandleLink($author_phid)); 123 129 } 124 130 125 131 return parent::getTitle(); ··· 129 135 switch ($this->getTransactionType()) { 130 136 case self::TYPE_DESCRIPTION: 131 137 return ($this->getOldValue() !== null); 138 + case self::TYPE_PATHS: 139 + return true; 132 140 } 133 141 134 142 return parent::hasChangeDetails(); ··· 144 152 $viewer, 145 153 $old, 146 154 $new); 155 + case self::TYPE_PATHS: 156 + $old = $this->getOldValue(); 157 + $new = $this->getNewValue(); 158 + 159 + $diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new); 160 + list($rem, $add) = $diffs; 161 + 162 + $rows = array(); 163 + foreach ($rem as $ref) { 164 + $rows[] = array( 165 + 'class' => 'diff-removed', 166 + 'change' => '-', 167 + ) + $ref; 168 + } 169 + 170 + foreach ($add as $ref) { 171 + $rows[] = array( 172 + 'class' => 'diff-added', 173 + 'change' => '+', 174 + ) + $ref; 175 + } 176 + 177 + $rowc = array(); 178 + foreach ($rows as $key => $row) { 179 + $rowc[] = $row['class']; 180 + $rows[$key] = array( 181 + $row['change'], 182 + $row['excluded'] ? pht('Exclude') : pht('Include'), 183 + $viewer->renderHandle($row['repositoryPHID']), 184 + $row['path'], 185 + ); 186 + } 187 + 188 + $table = id(new AphrontTableView($rows)) 189 + ->setRowClasses($rowc) 190 + ->setHeaders( 191 + array( 192 + null, 193 + pht('Type'), 194 + pht('Repository'), 195 + pht('Path'), 196 + )) 197 + ->setColumnClasses( 198 + array( 199 + null, 200 + null, 201 + null, 202 + 'wide', 203 + )); 204 + 205 + return $table; 147 206 } 148 207 149 208 return parent::renderChangeDetails($viewer);
+48
src/applications/owners/storage/PhabricatorOwnersPath.php
··· 22 22 ) + parent::getConfiguration(); 23 23 } 24 24 25 + 26 + public static function newFromRef(array $ref) { 27 + $path = new PhabricatorOwnersPath(); 28 + $path->repositoryPHID = $ref['repositoryPHID']; 29 + $path->path = $ref['path']; 30 + $path->excluded = $ref['excluded']; 31 + return $path; 32 + } 33 + 34 + public function getRef() { 35 + return array( 36 + 'repositoryPHID' => $this->getRepositoryPHID(), 37 + 'path' => $this->getPath(), 38 + 'excluded' => (int)$this->getExcluded(), 39 + ); 40 + } 41 + 42 + public static function getTransactionValueChanges(array $old, array $new) { 43 + return array( 44 + self::getTransactionValueDiff($old, $new), 45 + self::getTransactionValueDiff($new, $old), 46 + ); 47 + } 48 + 49 + private static function getTransactionValueDiff(array $u, array $v) { 50 + $set = self::getSetFromTransactionValue($v); 51 + 52 + foreach ($u as $key => $ref) { 53 + if (self::isRefInSet($ref, $set)) { 54 + unset($u[$key]); 55 + } 56 + } 57 + 58 + return $u; 59 + } 60 + 61 + public static function getSetFromTransactionValue(array $v) { 62 + $set = array(); 63 + foreach ($v as $ref) { 64 + $set[$ref['repositoryPHID']][$ref['path']][$ref['excluded']] = true; 65 + } 66 + return $set; 67 + } 68 + 69 + public static function isRefInSet(array $ref, array $set) { 70 + return isset($set[$ref['repositoryPHID']][$ref['path']][$ref['excluded']]); 71 + } 72 + 25 73 }
-108
src/applications/repository/worker/commitchangeparser/PhabricatorOwnersPackagePathValidator.php
··· 1 - <?php 2 - 3 - final class PhabricatorOwnersPackagePathValidator { 4 - 5 - /* 6 - * If a file/directory was moved the paths in owners package become stale. 7 - * This method updates the stale paths in the owners packages to their new 8 - * paths. 9 - */ 10 - public static function updateOwnersPackagePaths( 11 - PhabricatorRepositoryCommit $commit, 12 - PhabricatorUser $actor) { 13 - 14 - $repository = id(new PhabricatorRepositoryQuery()) 15 - ->setViewer($actor) 16 - ->withIDs(array($commit->getRepositoryID())) 17 - ->executeOne(); 18 - if (!$repository) { 19 - return; 20 - } 21 - $changes = self::loadDiffusionChangesForCommit( 22 - $repository, 23 - $commit, 24 - $actor); 25 - 26 - if (!$changes) { 27 - return; 28 - } 29 - 30 - $move_map = array(); 31 - foreach ($changes as $change) { 32 - if ($change->getChangeType() == DifferentialChangeType::TYPE_MOVE_HERE) { 33 - $from_path = '/'.$change->getTargetPath(); 34 - $to_path = '/'.$change->getPath(); 35 - if ($change->getFileType() == DifferentialChangeType::FILE_DIRECTORY) { 36 - $to_path = $to_path.'/'; 37 - $from_path = $from_path.'/'; 38 - } 39 - $move_map[$from_path] = $to_path; 40 - } 41 - } 42 - 43 - if ($move_map) { 44 - self::updateAffectedPackages($repository, $move_map); 45 - } 46 - } 47 - 48 - private static function updateAffectedPackages($repository, array $move_map) { 49 - $paths = array_keys($move_map); 50 - if ($paths) { 51 - $packages = PhabricatorOwnersPackage::loadAffectedPackages($repository, 52 - $paths); 53 - foreach ($packages as $package) { 54 - self::updatePackagePaths($package, $move_map); 55 - } 56 - } 57 - } 58 - 59 - private static function updatePackagePaths($package, array $move_map) { 60 - $paths = array_keys($move_map); 61 - $pkg_paths = $package->loadPaths(); 62 - $new_paths = array(); 63 - foreach ($pkg_paths as $pkg_path) { 64 - $path_changed = false; 65 - 66 - foreach ($paths as $old_path) { 67 - if (strncmp($pkg_path->getPath(), $old_path, strlen($old_path)) === 0) { 68 - $new_paths[] = array ( 69 - 'packageID' => $package->getID(), 70 - 'repositoryPHID' => $pkg_path->getRepositoryPHID(), 71 - 'path' => str_replace($pkg_path->getPath(), $old_path, 72 - $move_map[$old_path]), 73 - ); 74 - $path_changed = true; 75 - } 76 - } 77 - 78 - if (!$path_changed) { 79 - $new_paths[] = array ( 80 - 'packageID' => $package->getID(), 81 - 'repositoryPHID' => $pkg_path->getRepositoryPHID(), 82 - 'path' => $pkg_path->getPath(), 83 - ); 84 - } 85 - } 86 - 87 - if ($new_paths) { 88 - $package->attachOldPrimaryOwnerPHID($package->getPrimaryOwnerPHID()); 89 - $package->attachUnsavedPaths($new_paths); 90 - $package->save(); // save the changes and notify the owners. 91 - } 92 - } 93 - 94 - private static function loadDiffusionChangesForCommit( 95 - PhabricatorRepository $repository, 96 - PhabricatorRepositoryCommit $commit, 97 - PhabricatorUser $actor) { 98 - $data = array( 99 - 'user' => $actor, 100 - 'repository' => $repository, 101 - 'commit' => $commit->getCommitIdentifier(), 102 - ); 103 - $drequest = DiffusionRequest::newFromDictionary($data); 104 - $change_query = 105 - DiffusionPathChangeQuery::newFromDiffusionRequest($drequest); 106 - return $change_query->loadChanges(); 107 - } 108 - }
-3
src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php
··· 96 96 id(new PhabricatorSearchIndexer()) 97 97 ->queueDocumentForIndexing($commit->getPHID()); 98 98 99 - PhabricatorOwnersPackagePathValidator::updateOwnersPackagePaths( 100 - $commit, 101 - PhabricatorUser::getOmnipotentUser()); 102 99 if ($this->shouldQueueFollowupTasks()) { 103 100 $this->queueTask( 104 101 'PhabricatorRepositoryCommitOwnersWorker',
+10
webroot/rsrc/css/aphront/table-view.css
··· 211 211 background: #fcf2bb; 212 212 } 213 213 214 + .aphront-table-view tr.diff-removed, 215 + .aphront-table-view tr.alt-diff-removed { 216 + background: {$lightred} 217 + } 218 + 219 + .aphront-table-view tr.diff-added, 220 + .aphront-table-view tr.alt-diff-added { 221 + background: {$lightgreen} 222 + } 223 + 214 224 .aphront-table-view tr.no-data td { 215 225 padding: 12px; 216 226 text-align: center;