mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-10 23:01:04 +01:00
(stable) Promote 2018 Week 18
This commit is contained in:
commit
95fed76a3f
37 changed files with 853 additions and 122 deletions
|
@ -388,7 +388,7 @@ return array(
|
|||
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc',
|
||||
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '1db13e70',
|
||||
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef',
|
||||
'rsrc/js/application/files/behavior-document-engine.js' => 'ee0deff8',
|
||||
'rsrc/js/application/files/behavior-document-engine.js' => '3935d8c4',
|
||||
'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab',
|
||||
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
|
||||
'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '549459b8',
|
||||
|
@ -600,7 +600,7 @@ return array(
|
|||
'javelin-behavior-diffusion-commit-graph' => '75b83cbb',
|
||||
'javelin-behavior-diffusion-locate-file' => '6d3e1947',
|
||||
'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc',
|
||||
'javelin-behavior-document-engine' => 'ee0deff8',
|
||||
'javelin-behavior-document-engine' => '3935d8c4',
|
||||
'javelin-behavior-doorkeeper-tag' => '1db13e70',
|
||||
'javelin-behavior-drydock-live-operation-status' => '901935ef',
|
||||
'javelin-behavior-durable-column' => '2ae077e1',
|
||||
|
@ -1080,6 +1080,11 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'3935d8c4' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'3ab51e2c' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
|
@ -2105,11 +2110,6 @@ return array(
|
|||
'javelin-behavior',
|
||||
'javelin-uri',
|
||||
),
|
||||
'ee0deff8' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'efe49472' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
|
26
resources/sql/autopatches/20180504.owners.01.mailkey.php
Normal file
26
resources/sql/autopatches/20180504.owners.01.mailkey.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
$packages_table = new PhabricatorOwnersPackage();
|
||||
$packages_conn = $packages_table->establishConnection('w');
|
||||
$packages_name = $packages_table->getTableName();
|
||||
|
||||
$properties_table = new PhabricatorMetaMTAMailProperties();
|
||||
$conn = $properties_table->establishConnection('w');
|
||||
|
||||
$iterator = new LiskRawMigrationIterator($packages_conn, $packages_name);
|
||||
foreach ($iterator as $package) {
|
||||
queryfx(
|
||||
$conn,
|
||||
'INSERT IGNORE INTO %T
|
||||
(objectPHID, mailProperties, dateCreated, dateModified)
|
||||
VALUES
|
||||
(%s, %s, %d, %d)',
|
||||
$properties_table->getTableName(),
|
||||
$package['phid'],
|
||||
phutil_json_encode(
|
||||
array(
|
||||
'mailKey' => $package['mailKey'],
|
||||
)),
|
||||
PhabricatorTime::getNow(),
|
||||
PhabricatorTime::getNow());
|
||||
}
|
2
resources/sql/autopatches/20180504.owners.02.rmkey.sql
Normal file
2
resources/sql/autopatches/20180504.owners.02.rmkey.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_owners.owners_package
|
||||
DROP mailKey;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_owners.owners_package
|
||||
ADD properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20180504.owners.04.default.sql
Normal file
2
resources/sql/autopatches/20180504.owners.04.default.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
UPDATE {$NAMESPACE}_owners.owners_package
|
||||
SET properties = '{}' WHERE properties = '';
|
|
@ -3572,6 +3572,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersPackageFerretEngine' => 'applications/owners/search/PhabricatorOwnersPackageFerretEngine.php',
|
||||
'PhabricatorOwnersPackageFulltextEngine' => 'applications/owners/search/PhabricatorOwnersPackageFulltextEngine.php',
|
||||
'PhabricatorOwnersPackageFunctionDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php',
|
||||
'PhabricatorOwnersPackageIgnoredTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageIgnoredTransaction.php',
|
||||
'PhabricatorOwnersPackageNameNgrams' => 'applications/owners/storage/PhabricatorOwnersPackageNameNgrams.php',
|
||||
'PhabricatorOwnersPackageNameTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageNameTransaction.php',
|
||||
'PhabricatorOwnersPackageOwnerDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php',
|
||||
|
@ -3867,6 +3868,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyRequestExceptionHandler' => 'aphront/handler/PhabricatorPolicyRequestExceptionHandler.php',
|
||||
'PhabricatorPolicyRule' => 'applications/policy/rule/PhabricatorPolicyRule.php',
|
||||
'PhabricatorPolicySearchEngineExtension' => 'applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php',
|
||||
'PhabricatorPolicyStrengthConstants' => 'applications/policy/constants/PhabricatorPolicyStrengthConstants.php',
|
||||
'PhabricatorPolicyTestCase' => 'applications/policy/__tests__/PhabricatorPolicyTestCase.php',
|
||||
'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php',
|
||||
'PhabricatorPolicyType' => 'applications/policy/constants/PhabricatorPolicyType.php',
|
||||
|
@ -4998,6 +5000,7 @@ phutil_register_library_map(array(
|
|||
'PhrictionDocumentMoveToTransaction' => 'applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php',
|
||||
'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php',
|
||||
'PhrictionDocumentPathHeraldField' => 'applications/phriction/herald/PhrictionDocumentPathHeraldField.php',
|
||||
'PhrictionDocumentPolicyCodex' => 'applications/phriction/codex/PhrictionDocumentPolicyCodex.php',
|
||||
'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php',
|
||||
'PhrictionDocumentSearchConduitAPIMethod' => 'applications/phriction/conduit/PhrictionDocumentSearchConduitAPIMethod.php',
|
||||
'PhrictionDocumentSearchEngine' => 'applications/phriction/query/PhrictionDocumentSearchEngine.php',
|
||||
|
@ -9319,6 +9322,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersPackageFerretEngine' => 'PhabricatorFerretEngine',
|
||||
'PhabricatorOwnersPackageFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||
'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorOwnersPackageIgnoredTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageNameNgrams' => 'PhabricatorSearchNgrams',
|
||||
'PhabricatorOwnersPackageNameTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
|
@ -9669,6 +9673,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||
'PhabricatorPolicyRule' => 'Phobject',
|
||||
'PhabricatorPolicySearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
||||
'PhabricatorPolicyStrengthConstants' => 'PhabricatorPolicyConstants',
|
||||
'PhabricatorPolicyTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorPolicyTestObject' => array(
|
||||
'Phobject',
|
||||
|
@ -11060,6 +11065,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
'PhabricatorPolicyCodexInterface',
|
||||
),
|
||||
'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField',
|
||||
'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField',
|
||||
|
@ -11076,6 +11082,7 @@ phutil_register_library_map(array(
|
|||
'PhrictionDocumentMoveToTransaction' => 'PhrictionDocumentTransactionType',
|
||||
'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhrictionDocumentPathHeraldField' => 'PhrictionDocumentHeraldField',
|
||||
'PhrictionDocumentPolicyCodex' => 'PhabricatorPolicyCodex',
|
||||
'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhrictionDocumentSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'PhrictionDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
|
|
|
@ -76,6 +76,16 @@ abstract class DifferentialController extends PhabricatorController {
|
|||
$repository_phid,
|
||||
$changeset_path);
|
||||
|
||||
// If this particular changeset is generated code and the package does
|
||||
// not match generated code, remove it from the list.
|
||||
if ($changeset->isGeneratedChangeset()) {
|
||||
foreach ($packages as $key => $package) {
|
||||
if ($package->getMustMatchUngeneratedPaths()) {
|
||||
unset($packages[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->pathPackageMap[$changeset_path] = $packages;
|
||||
foreach ($packages as $package) {
|
||||
$this->packageChangesetMap[$package->getPHID()][] = $changeset;
|
||||
|
|
|
@ -1,22 +1,43 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionViewController extends DifferentialController {
|
||||
final class DifferentialRevisionViewController
|
||||
extends DifferentialController {
|
||||
|
||||
private $revisionID;
|
||||
private $veryLargeDiff;
|
||||
private $changesetCount;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isLargeDiff() {
|
||||
return ($this->getChangesetCount() > $this->getLargeDiffLimit());
|
||||
}
|
||||
|
||||
public function isVeryLargeDiff() {
|
||||
return $this->veryLargeDiff;
|
||||
return ($this->getChangesetCount() > $this->getVeryLargeDiffLimit());
|
||||
}
|
||||
|
||||
public function getLargeDiffLimit() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
public function getVeryLargeDiffLimit() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
public function getChangesetCount() {
|
||||
if ($this->changesetCount === null) {
|
||||
throw new PhutilInvalidStateException('setChangesetCount');
|
||||
}
|
||||
return $this->changesetCount;
|
||||
}
|
||||
|
||||
public function setChangesetCount($count) {
|
||||
$this->changesetCount = $count;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$this->revisionID = $request->getURIData('id');
|
||||
|
@ -82,9 +103,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
idx($diffs, $diff_vs),
|
||||
$repository);
|
||||
|
||||
if (count($rendering_references) > $this->getVeryLargeDiffLimit()) {
|
||||
$this->veryLargeDiff = true;
|
||||
}
|
||||
$this->setChangesetCount(count($rendering_references));
|
||||
|
||||
if ($request->getExists('download')) {
|
||||
return $this->buildRawDiffResponse(
|
||||
|
@ -153,10 +172,16 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
|
||||
$request_uri = $request->getRequestURI();
|
||||
|
||||
$limit = 100;
|
||||
$large = $request->getStr('large');
|
||||
if (count($changesets) > $limit && !$large) {
|
||||
$count = count($changesets);
|
||||
|
||||
$large_warning =
|
||||
($this->isLargeDiff()) &&
|
||||
(!$this->isVeryLargeDiff()) &&
|
||||
(!$large);
|
||||
|
||||
if ($large_warning) {
|
||||
$count = $this->getChangesetCount();
|
||||
|
||||
$warning = new PHUIInfoView();
|
||||
$warning->setTitle(pht('Large Diff'));
|
||||
$warning->setSeverity(PHUIInfoView::SEVERITY_WARNING);
|
||||
|
@ -357,23 +382,33 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
$other_view = $this->renderOtherRevisions($other_revisions);
|
||||
}
|
||||
|
||||
$this->buildPackageMaps($changesets);
|
||||
|
||||
if ($this->isVeryLargeDiff()) {
|
||||
$toc_view = null;
|
||||
|
||||
// When rendering a "very large" diff, we skip computation of owners
|
||||
// that own no files because it is significantly expensive and not very
|
||||
// valuable.
|
||||
foreach ($revision->getReviewers() as $reviewer) {
|
||||
// Give each reviewer a dummy nonempty value so the UI does not render
|
||||
// the "(Owns No Changed Paths)" note. If that behavior becomes more
|
||||
// sophisticated in the future, this behavior might also need to.
|
||||
$reviewer->attachChangesets($changesets);
|
||||
}
|
||||
} else {
|
||||
$this->buildPackageMaps($changesets);
|
||||
|
||||
$toc_view = $this->buildTableOfContents(
|
||||
$changesets,
|
||||
$visible_changesets,
|
||||
$target->loadCoverageMap($viewer));
|
||||
}
|
||||
|
||||
// Attach changesets to each reviewer so we can show which Owners package
|
||||
// reviewers own no files.
|
||||
foreach ($revision->getReviewers() as $reviewer) {
|
||||
$reviewer_phid = $reviewer->getReviewerPHID();
|
||||
$reviewer_changesets = $this->getPackageChangesets($reviewer_phid);
|
||||
$reviewer->attachChangesets($reviewer_changesets);
|
||||
// Attach changesets to each reviewer so we can show which Owners package
|
||||
// reviewers own no files.
|
||||
foreach ($revision->getReviewers() as $reviewer) {
|
||||
$reviewer_phid = $reviewer->getReviewerPHID();
|
||||
$reviewer_changesets = $this->getPackageChangesets($reviewer_phid);
|
||||
$reviewer->attachChangesets($reviewer_changesets);
|
||||
}
|
||||
}
|
||||
|
||||
$tab_group = id(new PHUITabGroupView());
|
||||
|
|
|
@ -7,11 +7,13 @@ final class DifferentialTransactionEditor
|
|||
private $isCloseByCommit;
|
||||
private $repositoryPHIDOverride = false;
|
||||
private $didExpandInlineState = false;
|
||||
private $affectedPaths;
|
||||
private $firstBroadcast = false;
|
||||
private $wasBroadcasting;
|
||||
private $isDraftDemotion;
|
||||
|
||||
private $ownersDiff;
|
||||
private $ownersChangesets;
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
}
|
||||
|
@ -968,13 +970,20 @@ final class DifferentialTransactionEditor
|
|||
return array();
|
||||
}
|
||||
|
||||
if (!$this->affectedPaths) {
|
||||
$diff = $this->ownersDiff;
|
||||
$changesets = $this->ownersChangesets;
|
||||
|
||||
$this->ownersDiff = null;
|
||||
$this->ownersChangesets = null;
|
||||
|
||||
if (!$changesets) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$packages = PhabricatorOwnersPackage::loadAffectedPackages(
|
||||
$packages = PhabricatorOwnersPackage::loadAffectedPackagesForChangesets(
|
||||
$repository,
|
||||
$this->affectedPaths);
|
||||
$diff,
|
||||
$changesets);
|
||||
if (!$packages) {
|
||||
return array();
|
||||
}
|
||||
|
@ -1255,9 +1264,12 @@ final class DifferentialTransactionEditor
|
|||
$paths[] = $path_prefix.'/'.$changeset->getFilename();
|
||||
}
|
||||
|
||||
// Save the affected paths; we'll use them later to query Owners. This
|
||||
// uses the un-expanded paths.
|
||||
$this->affectedPaths = $paths;
|
||||
// If this change affected paths, save the changesets so we can apply
|
||||
// Owners rules to them later.
|
||||
if ($paths) {
|
||||
$this->ownersDiff = $diff;
|
||||
$this->ownersChangesets = $changesets;
|
||||
}
|
||||
|
||||
// Mark this as also touching all parent paths, so you can see all pending
|
||||
// changes to any file within a directory.
|
||||
|
|
|
@ -120,9 +120,10 @@ final class HeraldDifferentialRevisionAdapter
|
|||
|
||||
$repository = $this->loadRepository();
|
||||
if ($repository) {
|
||||
$packages = PhabricatorOwnersPackage::loadAffectedPackages(
|
||||
$packages = PhabricatorOwnersPackage::loadAffectedPackagesForChangesets(
|
||||
$repository,
|
||||
$this->loadAffectedPaths());
|
||||
$this->getDiff(),
|
||||
$this->loadChangesets());
|
||||
$this->affectedPackages = $packages;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -542,6 +542,12 @@ final class DifferentialChangesetParser extends Phobject {
|
|||
PhutilEventEngine::dispatchEvent($event);
|
||||
|
||||
$generated = $event->getValue('is_generated');
|
||||
|
||||
$attribute = $this->changeset->isGeneratedChangeset();
|
||||
if ($attribute) {
|
||||
$generated = true;
|
||||
}
|
||||
|
||||
$this->specialAttributes[self::ATTR_GENERATED] = $generated;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ final class DifferentialChangeset
|
|||
protected $awayPaths;
|
||||
protected $changeType;
|
||||
protected $fileType;
|
||||
protected $metadata;
|
||||
protected $metadata = array();
|
||||
protected $oldProperties;
|
||||
protected $newProperties;
|
||||
protected $addLines;
|
||||
|
@ -24,6 +24,11 @@ final class DifferentialChangeset
|
|||
|
||||
const TABLE_CACHE = 'differential_changeset_parse_cache';
|
||||
|
||||
const METADATA_TRUSTED_ATTRIBUTES = 'attributes.trusted';
|
||||
const METADATA_UNTRUSTED_ATTRIBUTES = 'attributes.untrusted';
|
||||
|
||||
const ATTRIBUTE_GENERATED = 'generated';
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
|
@ -266,6 +271,90 @@ final class DifferentialChangeset
|
|||
return null;
|
||||
}
|
||||
|
||||
public function setChangesetMetadata($key, $value) {
|
||||
if (!is_array($this->metadata)) {
|
||||
$this->metadata = array();
|
||||
}
|
||||
|
||||
$this->metadata[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getChangesetMetadata($key, $default = null) {
|
||||
if (!is_array($this->metadata)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return idx($this->metadata, $key, $default);
|
||||
}
|
||||
|
||||
private function setInternalChangesetAttribute($trusted, $key, $value) {
|
||||
if ($trusted) {
|
||||
$meta_key = self::METADATA_TRUSTED_ATTRIBUTES;
|
||||
} else {
|
||||
$meta_key = self::METADATA_UNTRUSTED_ATTRIBUTES;
|
||||
}
|
||||
|
||||
$attributes = $this->getChangesetMetadata($meta_key, array());
|
||||
$attributes[$key] = $value;
|
||||
$this->setChangesetMetadata($meta_key, $attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getInternalChangesetAttributes($trusted) {
|
||||
if ($trusted) {
|
||||
$meta_key = self::METADATA_TRUSTED_ATTRIBUTES;
|
||||
} else {
|
||||
$meta_key = self::METADATA_UNTRUSTED_ATTRIBUTES;
|
||||
}
|
||||
|
||||
return $this->getChangesetMetadata($meta_key, array());
|
||||
}
|
||||
|
||||
public function setTrustedChangesetAttribute($key, $value) {
|
||||
return $this->setInternalChangesetAttribute(true, $key, $value);
|
||||
}
|
||||
|
||||
public function getTrustedChangesetAttributes() {
|
||||
return $this->getInternalChangesetAttributes(true);
|
||||
}
|
||||
|
||||
public function getTrustedChangesetAttribute($key, $default = null) {
|
||||
$map = $this->getTrustedChangesetAttributes();
|
||||
return idx($map, $key, $default);
|
||||
}
|
||||
|
||||
public function setUntrustedChangesetAttribute($key, $value) {
|
||||
return $this->setInternalChangesetAttribute(false, $key, $value);
|
||||
}
|
||||
|
||||
public function getUntrustedChangesetAttributes() {
|
||||
return $this->getInternalChangesetAttributes(false);
|
||||
}
|
||||
|
||||
public function getUntrustedChangesetAttribute($key, $default = null) {
|
||||
$map = $this->getUntrustedChangesetAttributes();
|
||||
return idx($map, $key, $default);
|
||||
}
|
||||
|
||||
public function getChangesetAttributes() {
|
||||
// Prefer trusted values over untrusted values when both exist.
|
||||
return
|
||||
$this->getTrustedChangesetAttributes() +
|
||||
$this->getUntrustedChangesetAttributes();
|
||||
}
|
||||
|
||||
public function getChangesetAttribute($key, $default = null) {
|
||||
$map = $this->getChangesetAttributes();
|
||||
return idx($map, $key, $default);
|
||||
}
|
||||
|
||||
public function isGeneratedChangeset() {
|
||||
return $this->getChangesetAttribute(self::ATTRIBUTE_GENERATED);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -226,6 +226,8 @@ final class DifferentialDiff
|
|||
$changeset->setAddLines($add_lines);
|
||||
$changeset->setDelLines($del_lines);
|
||||
|
||||
self::detectGeneratedCode($changeset);
|
||||
|
||||
$diff->addUnsavedChangeset($changeset);
|
||||
}
|
||||
$diff->setLineCount($lines);
|
||||
|
@ -821,5 +823,50 @@ final class DifferentialDiff
|
|||
);
|
||||
}
|
||||
|
||||
private static function detectGeneratedCode(
|
||||
DifferentialChangeset $changeset) {
|
||||
|
||||
$is_generated_trusted = self::isTrustedGeneratedCode($changeset);
|
||||
if ($is_generated_trusted) {
|
||||
$changeset->setTrustedChangesetAttribute(
|
||||
DifferentialChangeset::ATTRIBUTE_GENERATED,
|
||||
$is_generated_trusted);
|
||||
}
|
||||
|
||||
$is_generated_untrusted = self::isUntrustedGeneratedCode($changeset);
|
||||
if ($is_generated_untrusted) {
|
||||
$changeset->setUntrustedChangesetAttribute(
|
||||
DifferentialChangeset::ATTRIBUTE_GENERATED,
|
||||
$is_generated_untrusted);
|
||||
}
|
||||
}
|
||||
|
||||
private static function isTrustedGeneratedCode(
|
||||
DifferentialChangeset $changeset) {
|
||||
|
||||
$filename = $changeset->getFilename();
|
||||
|
||||
$paths = PhabricatorEnv::getEnvConfig('differential.generated-paths');
|
||||
foreach ($paths as $regexp) {
|
||||
if (preg_match($regexp, $filename)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function isUntrustedGeneratedCode(
|
||||
DifferentialChangeset $changeset) {
|
||||
|
||||
if ($changeset->getHunks()) {
|
||||
$new_data = $changeset->makeNewFile();
|
||||
if (strpos($new_data, '@'.'generated') !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ final class DiffusionDocumentRenderingEngine
|
|||
return;
|
||||
}
|
||||
|
||||
protected function willRenderRef(PhabricatorDocumentRef $ref) {
|
||||
protected function willStageRef(PhabricatorDocumentRef $ref) {
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
|
||||
$blame_uri = (string)$drequest->generateURI(
|
||||
|
@ -78,9 +78,13 @@ final class DiffusionDocumentRenderingEngine
|
|||
'stable' => true,
|
||||
));
|
||||
|
||||
$ref
|
||||
->setSymbolMetadata($this->getSymbolMetadata())
|
||||
->setBlameURI($blame_uri);
|
||||
$ref->setBlameURI($blame_uri);
|
||||
}
|
||||
|
||||
protected function willRenderRef(PhabricatorDocumentRef $ref) {
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
|
||||
$ref->setSymbolMetadata($this->getSymbolMetadata());
|
||||
|
||||
$coverage = $drequest->loadCoverage();
|
||||
if (strlen($coverage)) {
|
||||
|
|
|
@ -84,12 +84,29 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
|
|||
$response->setMimeType($file->getViewableMimeType());
|
||||
} else {
|
||||
$is_post = $request->isHTTPPost();
|
||||
$is_public = !$viewer->isLoggedIn();
|
||||
|
||||
// NOTE: Require POST to download files from the primary domain. If the
|
||||
// request is not a POST request but arrives on the primary domain, we
|
||||
// render a confirmation dialog. For discussion, see T13094.
|
||||
|
||||
$is_safe = ($is_alternate_domain || $is_lfs || $is_post);
|
||||
// There are two exceptions to this rule:
|
||||
|
||||
// Git LFS requests can download with GET. This is safe (Git LFS won't
|
||||
// execute files it downloads) and necessary to support Git LFS.
|
||||
|
||||
// Requests with no credentials may also download with GET. This
|
||||
// primarily supports downloading files with `arc download` or other
|
||||
// API clients. This is only "mostly" safe: if you aren't logged in, you
|
||||
// are likely immune to XSS and CSRF. However, an attacker may still be
|
||||
// able to set cookies on this domain (for example, to fixate your
|
||||
// session). For now, we accept these risks because users running
|
||||
// Phabricator in this mode are knowingly accepting a security risk
|
||||
// against setup advice, and there's significant value in having
|
||||
// API development against test and production installs work the same
|
||||
// way.
|
||||
|
||||
$is_safe = ($is_alternate_domain || $is_post || $is_lfs || $is_public);
|
||||
if (!$is_safe) {
|
||||
return $this->newDialog()
|
||||
->setSubmitURI($file->getDownloadURI())
|
||||
|
|
|
@ -7,6 +7,7 @@ abstract class PhabricatorDocumentEngine
|
|||
private $highlightedLines = array();
|
||||
private $encodingConfiguration;
|
||||
private $highlightingConfiguration;
|
||||
private $blameConfiguration = true;
|
||||
|
||||
final public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
|
@ -60,6 +61,19 @@ abstract class PhabricatorDocumentEngine
|
|||
return $this->highlightingConfiguration;
|
||||
}
|
||||
|
||||
final public function setBlameConfiguration($blame_configuration) {
|
||||
$this->blameConfiguration = $blame_configuration;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getBlameConfiguration() {
|
||||
return $this->blameConfiguration;
|
||||
}
|
||||
|
||||
final protected function getBlameEnabled() {
|
||||
return $this->blameConfiguration;
|
||||
}
|
||||
|
||||
public function shouldRenderAsync(PhabricatorDocumentRef $ref) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ final class PhabricatorSourceDocumentEngine
|
|||
}
|
||||
|
||||
$options = array();
|
||||
if ($ref->getBlameURI()) {
|
||||
if ($ref->getBlameURI() && $this->getBlameEnabled()) {
|
||||
$content = phutil_split_lines($content);
|
||||
$blame = range(1, count($content));
|
||||
$blame = array_fuse($blame);
|
||||
|
|
|
@ -69,6 +69,9 @@ abstract class PhabricatorDocumentRenderingEngine
|
|||
$engine->setHighlightingConfiguration($highlight_setting);
|
||||
}
|
||||
|
||||
$blame_setting = ($request->getStr('blame') !== 'off');
|
||||
$engine->setBlameConfiguration($blame_setting);
|
||||
|
||||
$views = array();
|
||||
foreach ($engines as $candidate_key => $candidate_engine) {
|
||||
$label = $candidate_engine->getViewAsLabel($ref);
|
||||
|
@ -104,6 +107,8 @@ abstract class PhabricatorDocumentRenderingEngine
|
|||
'controlID' => $control_id,
|
||||
);
|
||||
|
||||
$this->willStageRef($ref);
|
||||
|
||||
if ($engine->shouldRenderAsync($ref)) {
|
||||
$content = $engine->newLoadingContent($ref);
|
||||
$config['next'] = 'render';
|
||||
|
@ -142,7 +147,11 @@ abstract class PhabricatorDocumentRenderingEngine
|
|||
'value' => $highlight_setting,
|
||||
),
|
||||
'blame' => array(
|
||||
'icon' => 'fa-backward',
|
||||
'hide' => pht('Hide Blame'),
|
||||
'show' => pht('Show Blame'),
|
||||
'uri' => $ref->getBlameURI(),
|
||||
'enabled' => $blame_setting,
|
||||
'value' => null,
|
||||
),
|
||||
'coverage' => array(
|
||||
|
@ -180,6 +189,7 @@ abstract class PhabricatorDocumentRenderingEngine
|
|||
}
|
||||
|
||||
final public function newRenderResponse(PhabricatorDocumentRef $ref) {
|
||||
$this->willStageRef($ref);
|
||||
$this->willRenderRef($ref);
|
||||
|
||||
$request = $this->getRequest();
|
||||
|
@ -207,6 +217,9 @@ abstract class PhabricatorDocumentRenderingEngine
|
|||
$engine->setHighlightingConfiguration($highlight_setting);
|
||||
}
|
||||
|
||||
$blame_setting = ($request->getStr('blame') !== 'off');
|
||||
$engine->setBlameConfiguration($blame_setting);
|
||||
|
||||
try {
|
||||
$content = $engine->newDocument($ref);
|
||||
} catch (Exception $ex) {
|
||||
|
@ -314,6 +327,10 @@ abstract class PhabricatorDocumentRenderingEngine
|
|||
return;
|
||||
}
|
||||
|
||||
protected function willStageRef(PhabricatorDocumentRef $ref) {
|
||||
return;
|
||||
}
|
||||
|
||||
protected function willRenderRef(PhabricatorDocumentRef $ref) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -260,6 +260,18 @@ final class PhabricatorEmbedFileRemarkupRule
|
|||
$autoplay = null;
|
||||
}
|
||||
|
||||
if ($is_video) {
|
||||
// See T13135. Chrome refuses to play videos with type "video/quicktime",
|
||||
// even though it may actually be able to play them. The least awful fix
|
||||
// based on available information is to simply omit the "type" attribute
|
||||
// from `<source />` tags. This causes Chrome to try to play the video
|
||||
// and realize it can, and does not appear to produce any bad behavior in
|
||||
// any other browser.
|
||||
$mime_type = null;
|
||||
} else {
|
||||
$mime_type = $file->getMimeType();
|
||||
}
|
||||
|
||||
return $this->newTag(
|
||||
$tag,
|
||||
array(
|
||||
|
@ -274,7 +286,7 @@ final class PhabricatorEmbedFileRemarkupRule
|
|||
'source',
|
||||
array(
|
||||
'src' => $file->getBestURI(),
|
||||
'type' => $file->getMimeType(),
|
||||
'type' => $mime_type,
|
||||
)));
|
||||
}
|
||||
|
||||
|
|
|
@ -106,6 +106,14 @@ EOTEXT
|
|||
),
|
||||
'meta_data' => array(
|
||||
'buildTargetPHID' => $build_target->getPHID(),
|
||||
|
||||
// See PHI611. These are undocumented secret magic.
|
||||
'phabricator:build:id' => (int)$build->getID(),
|
||||
'phabricator:build:url' =>
|
||||
PhabricatorEnv::getProductionURI($build->getURI()),
|
||||
'phabricator:buildable:id' => (int)$buildable->getID(),
|
||||
'phabricator:buildable:url' =>
|
||||
PhabricatorEnv::getProductionURI($buildable->getURI()),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -137,4 +137,18 @@ final class HeraldRuleEditor
|
|||
return pht('[Herald]');
|
||||
}
|
||||
|
||||
|
||||
protected function buildMailBody(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$body = parent::buildMailBody($object, $xactions);
|
||||
|
||||
$body->addLinkSection(
|
||||
pht('RULE DETAIL'),
|
||||
PhabricatorEnv::getProductionURI($object->getURI()));
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -254,6 +254,10 @@ final class HeraldRule extends HeraldDAO
|
|||
return 'H'.$this->getID();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
|
||||
/* -( Repetition Policies )------------------------------------------------ */
|
||||
|
||||
|
|
|
@ -585,7 +585,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
'task.ownerPHID IS NOT NULL');
|
||||
}
|
||||
|
||||
if ($this->ownerPHIDs) {
|
||||
if ($this->ownerPHIDs !== null) {
|
||||
$subclause[] = qsprintf(
|
||||
$conn,
|
||||
'task.ownerPHID IN (%Ls)',
|
||||
|
|
|
@ -201,6 +201,16 @@ final class PhabricatorOwnersDetailController
|
|||
}
|
||||
$view->addProperty(pht('Auditing'), $auditing);
|
||||
|
||||
$ignored = $package->getIgnoredPathAttributes();
|
||||
$ignored = array_keys($ignored);
|
||||
if ($ignored) {
|
||||
$ignored = implode(', ', $ignored);
|
||||
} else {
|
||||
$ignored = phutil_tag('em', array(), pht('None'));
|
||||
}
|
||||
|
||||
$view->addProperty(pht('Ignored Attributes'), $ignored);
|
||||
|
||||
$description = $package->getDescription();
|
||||
if (strlen($description)) {
|
||||
$description = new PHUIRemarkupView($viewer, $description);
|
||||
|
|
|
@ -162,6 +162,13 @@ EOTEXT
|
|||
->setIsConduitOnly(true)
|
||||
->setValue($object->getStatus())
|
||||
->setOptions($object->getStatusNameMap()),
|
||||
id(new PhabricatorStringListEditField())
|
||||
->setKey('ignored')
|
||||
->setLabel(pht('Ignored Attributes'))
|
||||
->setDescription(pht('Ignore paths with any of these attributes.'))
|
||||
->setTransactionType(
|
||||
PhabricatorOwnersPackageIgnoredTransaction::TRANSACTIONTYPE)
|
||||
->setValue(array_keys($object->getIgnoredPathAttributes())),
|
||||
id(new PhabricatorConduitEditField())
|
||||
->setKey('paths.set')
|
||||
->setLabel(pht('Paths'))
|
||||
|
|
|
@ -16,11 +16,11 @@ final class PhabricatorOwnersPackage
|
|||
protected $auditingEnabled;
|
||||
protected $autoReview;
|
||||
protected $description;
|
||||
protected $mailKey;
|
||||
protected $status;
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
protected $dominion;
|
||||
protected $properties = array();
|
||||
|
||||
private $paths = self::ATTACHABLE;
|
||||
private $owners = self::ATTACHABLE;
|
||||
|
@ -41,6 +41,8 @@ final class PhabricatorOwnersPackage
|
|||
const DOMINION_STRONG = 'strong';
|
||||
const DOMINION_WEAK = 'weak';
|
||||
|
||||
const PROPERTY_IGNORED = 'ignored';
|
||||
|
||||
public static function initializeNewPackage(PhabricatorUser $actor) {
|
||||
$app = id(new PhabricatorApplicationQuery())
|
||||
->setViewer($actor)
|
||||
|
@ -118,11 +120,13 @@ final class PhabricatorOwnersPackage
|
|||
// This information is better available from the history table.
|
||||
self::CONFIG_TIMESTAMPS => false,
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'properties' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'name' => 'sort',
|
||||
'description' => 'text',
|
||||
'auditingEnabled' => 'bool',
|
||||
'mailKey' => 'bytes20',
|
||||
'status' => 'text32',
|
||||
'autoReview' => 'text32',
|
||||
'dominion' => 'text32',
|
||||
|
@ -130,28 +134,36 @@ final class PhabricatorOwnersPackage
|
|||
) + 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 getPHIDType() {
|
||||
return PhabricatorOwnersPackagePHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function isArchived() {
|
||||
return ($this->getStatus() == self::STATUS_ARCHIVED);
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
public function getMustMatchUngeneratedPaths() {
|
||||
$ignore_attributes = $this->getIgnoredPathAttributes();
|
||||
return !empty($ignore_attributes['generated']);
|
||||
}
|
||||
|
||||
public function getPackageProperty($key, $default = null) {
|
||||
return idx($this->properties, $key, $default);
|
||||
}
|
||||
|
||||
public function setPackageProperty($key, $value) {
|
||||
$this->properties[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIgnoredPathAttributes() {
|
||||
return $this->getPackageProperty(self::PROPERTY_IGNORED, array());
|
||||
}
|
||||
|
||||
public function setIgnoredPathAttributes(array $attributes) {
|
||||
return $this->setPackageProperty(self::PROPERTY_IGNORED, $attributes);
|
||||
}
|
||||
|
||||
public function loadOwners() {
|
||||
if (!$this->getID()) {
|
||||
return array();
|
||||
|
@ -181,6 +193,82 @@ final class PhabricatorOwnersPackage
|
|||
return self::loadPackagesForPaths($repository, $paths);
|
||||
}
|
||||
|
||||
public static function loadAffectedPackagesForChangesets(
|
||||
PhabricatorRepository $repository,
|
||||
DifferentialDiff $diff,
|
||||
array $changesets) {
|
||||
assert_instances_of($changesets, 'DifferentialChangeset');
|
||||
|
||||
$paths_all = array();
|
||||
$paths_ungenerated = array();
|
||||
|
||||
foreach ($changesets as $changeset) {
|
||||
$path = $changeset->getAbsoluteRepositoryPath($repository, $diff);
|
||||
|
||||
$paths_all[] = $path;
|
||||
|
||||
if (!$changeset->isGeneratedChangeset()) {
|
||||
$paths_ungenerated[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$paths_all) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$packages_all = self::loadAffectedPackages(
|
||||
$repository,
|
||||
$paths_all);
|
||||
|
||||
// If there are no generated changesets, we can't possibly need to throw
|
||||
// away any packages for matching only generated paths. Just return the
|
||||
// full set of packages.
|
||||
if ($paths_ungenerated === $paths_all) {
|
||||
return $packages_all;
|
||||
}
|
||||
|
||||
$must_match_ungenerated = array();
|
||||
foreach ($packages_all as $package) {
|
||||
if ($package->getMustMatchUngeneratedPaths()) {
|
||||
$must_match_ungenerated[] = $package;
|
||||
}
|
||||
}
|
||||
|
||||
// If no affected packages have the "Ignore Generated Paths" flag set, we
|
||||
// can't possibly need to throw any away.
|
||||
if (!$must_match_ungenerated) {
|
||||
return $packages_all;
|
||||
}
|
||||
|
||||
if ($paths_ungenerated) {
|
||||
$packages_ungenerated = self::loadAffectedPackages(
|
||||
$repository,
|
||||
$paths_ungenerated);
|
||||
} else {
|
||||
$packages_ungenerated = array();
|
||||
}
|
||||
|
||||
// We have some generated paths, and some packages that ignore generated
|
||||
// paths. Take all the packages which:
|
||||
//
|
||||
// - ignore generated paths; and
|
||||
// - didn't match any ungenerated paths
|
||||
//
|
||||
// ...and remove them from the list.
|
||||
|
||||
$must_match_ungenerated = mpull($must_match_ungenerated, null, 'getID');
|
||||
$packages_ungenerated = mpull($packages_ungenerated, null, 'getID');
|
||||
$packages_all = mpull($packages_all, null, 'getID');
|
||||
|
||||
foreach ($must_match_ungenerated as $package_id => $package) {
|
||||
if (!isset($packages_ungenerated[$package_id])) {
|
||||
unset($packages_all[$package_id]);
|
||||
}
|
||||
}
|
||||
|
||||
return $packages_all;
|
||||
}
|
||||
|
||||
public static function loadOwningPackages($repository, $path) {
|
||||
if (empty($path)) {
|
||||
return array();
|
||||
|
@ -614,6 +702,10 @@ final class PhabricatorOwnersPackage
|
|||
->setKey('dominion')
|
||||
->setType('map<string, wild>')
|
||||
->setDescription(pht('Dominion setting information.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('ignored')
|
||||
->setType('map<string, wild>')
|
||||
->setDescription(pht('Ignored attribute information.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -667,6 +759,13 @@ final class PhabricatorOwnersPackage
|
|||
'short' => $dominion_short,
|
||||
);
|
||||
|
||||
// Force this to always emit as a JSON object even if empty, never as
|
||||
// a JSON list.
|
||||
$ignored = $this->getIgnoredPathAttributes();
|
||||
if (!$ignored) {
|
||||
$ignored = (object)array();
|
||||
}
|
||||
|
||||
return array(
|
||||
'name' => $this->getName(),
|
||||
'description' => $this->getDescription(),
|
||||
|
@ -675,6 +774,7 @@ final class PhabricatorOwnersPackage
|
|||
'review' => $review,
|
||||
'audit' => $audit,
|
||||
'dominion' => $dominion,
|
||||
'ignored' => $ignored,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorOwnersPackageIgnoredTransaction
|
||||
extends PhabricatorOwnersPackageTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'owners.ignored';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getIgnoredPathAttributes();
|
||||
}
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
return array_fill_keys($value, true);
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setIgnoredPathAttributes($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$old = array_keys($this->getOldValue());
|
||||
$new = array_keys($this->getNewValue());
|
||||
|
||||
$add = array_diff($new, $old);
|
||||
$rem = array_diff($old, $new);
|
||||
|
||||
$all_n = new PhutilNumber(count($add) + count($rem));
|
||||
$add_n = phutil_count($add);
|
||||
$rem_n = phutil_count($rem);
|
||||
|
||||
if ($new && $old) {
|
||||
return pht(
|
||||
'%s changed %s ignored attribute(s), added %s: %s; removed %s: %s.',
|
||||
$this->renderAuthor(),
|
||||
$all_n,
|
||||
$add_n,
|
||||
$this->renderValueList($add),
|
||||
$rem_n,
|
||||
$this->renderValueList($rem));
|
||||
} else if ($new) {
|
||||
return pht(
|
||||
'%s changed %s ignored attribute(s), added %s: %s.',
|
||||
$this->renderAuthor(),
|
||||
$all_n,
|
||||
$add_n,
|
||||
$this->rendervalueList($add));
|
||||
} else {
|
||||
return pht(
|
||||
'%s changed %s ignored attribute(s), removed %s: %s.',
|
||||
$this->renderAuthor(),
|
||||
$all_n,
|
||||
$rem_n,
|
||||
$this->rendervalueList($rem));
|
||||
}
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
$valid_attributes = array(
|
||||
'generated' => true,
|
||||
);
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
$new = $xaction->getNewValue();
|
||||
|
||||
foreach ($new as $attribute) {
|
||||
if (isset($valid_attributes[$attribute])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Changeset attribute "%s" is not valid. Valid changeset '.
|
||||
'attributes are: %s.',
|
||||
$attribute,
|
||||
implode(', ', array_keys($valid_attributes))),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -108,19 +108,23 @@ final class PhabricatorOwnersPackagePathsTransaction
|
|||
// paths now.
|
||||
|
||||
$display_map = array();
|
||||
$seen_map = array();
|
||||
foreach ($new as $key => $spec) {
|
||||
$display_path = $spec['path'];
|
||||
$raw_path = rtrim($display_path, '/').'/';
|
||||
|
||||
// If the user entered two paths which normalize to the same value
|
||||
// (like "src/main.c" and "src/main.c/"), discard the duplicates.
|
||||
if (isset($display_map[$raw_path])) {
|
||||
// If the user entered two paths in the same repository which normalize
|
||||
// to the same value (like "src/main.c" and "src/main.c/"), discard the
|
||||
// duplicates.
|
||||
$repository_phid = $spec['repositoryPHID'];
|
||||
if (isset($seen_map[$repository_phid][$raw_path])) {
|
||||
unset($new[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$new[$key]['path'] = $raw_path;
|
||||
$display_map[$raw_path] = $display_path;
|
||||
$seen_map[$repository_phid][$raw_path] = true;
|
||||
}
|
||||
|
||||
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
final class PhrictionDocumentPolicyCodex
|
||||
extends PhabricatorPolicyCodex {
|
||||
|
||||
public function getPolicySpecialRuleDescriptions() {
|
||||
$object = $this->getObject();
|
||||
$strongest_policy = $this->getStrongestPolicy();
|
||||
|
||||
$rules = array();
|
||||
$rules[] = $this->newRule()
|
||||
->setDescription(
|
||||
pht('To view a wiki document, you must also be able to view all '.
|
||||
'of its ancestors. The most-restrictive view policy of this '.
|
||||
'document\'s ancestors is "%s".',
|
||||
$strongest_policy->getShortName()))
|
||||
->setCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW));
|
||||
|
||||
$rules[] = $this->newRule()
|
||||
->setDescription(
|
||||
pht('To edit a wiki document, you must also be able to view all '.
|
||||
'of its ancestors.'))
|
||||
->setCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT));
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function getDefaultPolicy() {
|
||||
$ancestors = $this->getObject()->getAncestors();
|
||||
if ($ancestors) {
|
||||
$root = head($ancestors);
|
||||
} else {
|
||||
$root = $this->getObject();
|
||||
}
|
||||
|
||||
$root_policy_phid = $root->getPolicy($this->getCapability());
|
||||
|
||||
return id(new PhabricatorPolicyQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(array($root_policy_phid))
|
||||
->executeOne();
|
||||
}
|
||||
|
||||
public function compareToDefaultPolicy(PhabricatorPolicy $policy) {
|
||||
$root_policy = $this->getDefaultPolicy();
|
||||
$strongest_policy = $this->getStrongestPolicy();
|
||||
|
||||
// Note that we never return 'weaker', because Phriction documents can
|
||||
// never have weaker permissions than their parents. If this object has
|
||||
// been set to weaker permissions anyway, return 'adjusted'.
|
||||
if ($root_policy == $strongest_policy) {
|
||||
$strength = null;
|
||||
} else if ($strongest_policy->isStrongerThan($root_policy)) {
|
||||
$strength = PhabricatorPolicyStrengthConstants::STRONGER;
|
||||
} else {
|
||||
$strength = PhabricatorPolicyStrengthConstants::ADJUSTED;
|
||||
}
|
||||
return $strength;
|
||||
}
|
||||
|
||||
private function getStrongestPolicy() {
|
||||
$ancestors = $this->getObject()->getAncestors();
|
||||
$ancestors[] = $this->getObject();
|
||||
|
||||
$strongest_policy = $this->getDefaultPolicy();
|
||||
foreach ($ancestors as $ancestor) {
|
||||
$ancestor_policy_phid = $ancestor->getPolicy($this->getCapability());
|
||||
|
||||
$ancestor_policy = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(array($ancestor_policy_phid))
|
||||
->executeOne();
|
||||
|
||||
if ($ancestor_policy->isStrongerThan($strongest_policy)) {
|
||||
$strongest_policy = $ancestor_policy;
|
||||
}
|
||||
}
|
||||
|
||||
return $strongest_policy;
|
||||
}
|
||||
|
||||
}
|
|
@ -219,6 +219,13 @@ final class PhrictionEditController
|
|||
->execute();
|
||||
$view_capability = PhabricatorPolicyCapability::CAN_VIEW;
|
||||
$edit_capability = PhabricatorPolicyCapability::CAN_EDIT;
|
||||
$codex = id(PhabricatorPolicyCodex::newFromObject($document, $viewer))
|
||||
->setCapability($view_capability);
|
||||
|
||||
$view_capability_description = $codex->getPolicySpecialRuleForCapability(
|
||||
PhabricatorPolicyCapability::CAN_VIEW)->getDescription();
|
||||
$edit_capability_description = $codex->getPolicySpecialRuleForCapability(
|
||||
PhabricatorPolicyCapability::CAN_EDIT)->getDescription();
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
|
@ -264,16 +271,14 @@ final class PhrictionEditController
|
|||
->setPolicyObject($document)
|
||||
->setCapability($view_capability)
|
||||
->setPolicies($policies)
|
||||
->setCaption(
|
||||
$document->describeAutomaticCapability($view_capability)))
|
||||
->setCaption($view_capability_description))
|
||||
->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setName('editPolicy')
|
||||
->setPolicyObject($document)
|
||||
->setCapability($edit_capability)
|
||||
->setPolicies($policies)
|
||||
->setCaption(
|
||||
$document->describeAutomaticCapability($edit_capability)))
|
||||
->setCaption($edit_capability_description))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Edit Notes'))
|
||||
|
|
|
@ -11,7 +11,8 @@ final class PhrictionDocument extends PhrictionDAO
|
|||
PhabricatorFerretInterface,
|
||||
PhabricatorProjectInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorConduitResultInterface {
|
||||
PhabricatorConduitResultInterface,
|
||||
PhabricatorPolicyCodexInterface {
|
||||
|
||||
protected $slug;
|
||||
protected $depth;
|
||||
|
@ -200,22 +201,6 @@ final class PhrictionDocument extends PhrictionDAO
|
|||
return false;
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
return pht(
|
||||
'To view a wiki document, you must also be able to view all '.
|
||||
'of its parents.');
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
return pht(
|
||||
'To edit a wiki document, you must also be able to view all '.
|
||||
'of its parents.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||
|
||||
|
@ -328,4 +313,13 @@ final class PhrictionDocument extends PhrictionDAO
|
|||
->setAttachmentKey('content'),
|
||||
);
|
||||
}
|
||||
|
||||
/* -( PhabricatorPolicyCodexInterface )------------------------------------ */
|
||||
|
||||
|
||||
public function newPolicyCodex() {
|
||||
return new PhrictionDocumentPolicyCodex();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,27 @@ abstract class PhabricatorPolicyCodex
|
|||
return array();
|
||||
}
|
||||
|
||||
public function getDefaultPolicy() {
|
||||
return PhabricatorPolicyQuery::getDefaultPolicyForObject(
|
||||
$this->viewer,
|
||||
$this->object,
|
||||
$this->capability);
|
||||
}
|
||||
|
||||
public function compareToDefaultPolicy(PhabricatorPolicy $policy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final public function getPolicySpecialRuleForCapability($capability) {
|
||||
foreach ($this->getPolicySpecialRuleDescriptions() as $rule) {
|
||||
if (in_array($capability, $rule->getCapabilities())) {
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
final protected function newRule() {
|
||||
return new PhabricatorPolicyCodexRuleDescription();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPolicyStrengthConstants
|
||||
extends PhabricatorPolicyConstants {
|
||||
|
||||
const WEAKER = 'weaker';
|
||||
const STRONGER = 'stronger';
|
||||
const ADJUSTED = 'adjusted';
|
||||
}
|
|
@ -169,25 +169,47 @@ final class PhabricatorPolicyExplainController
|
|||
$capability) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject(
|
||||
$viewer,
|
||||
$object,
|
||||
$capability);
|
||||
if (!$default_policy) {
|
||||
|
||||
$strength = null;
|
||||
if ($object instanceof PhabricatorPolicyCodexInterface) {
|
||||
$codex = id(PhabricatorPolicyCodex::newFromObject($object, $viewer))
|
||||
->setCapability($capability);
|
||||
$strength = $codex->compareToDefaultPolicy($policy);
|
||||
$default_policy = $codex->getDefaultPolicy();
|
||||
} else {
|
||||
$default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject(
|
||||
$viewer,
|
||||
$object,
|
||||
$capability);
|
||||
|
||||
if ($default_policy) {
|
||||
if ($default_policy->getPHID() == $policy->getPHID()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($default_policy->getPHID() != $policy->getPHID()) {
|
||||
if ($default_policy->isStrongerThan($policy)) {
|
||||
$strength = PhabricatorPolicyStrengthConstants::WEAKER;
|
||||
} else if ($policy->isStrongerThan($default_policy)) {
|
||||
$strength = PhabricatorPolicyStrengthConstants::STRONGER;
|
||||
} else {
|
||||
$strength = PhabricatorPolicyStrengthConstants::ADJUSTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$strength) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($default_policy->getPHID() == $policy->getPHID()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($default_policy->isStrongerThan($policy)) {
|
||||
if ($strength == PhabricatorPolicyStrengthConstants::WEAKER) {
|
||||
$info = pht(
|
||||
'This object has a less restrictive policy ("%s") than the default '.
|
||||
'policy for similar objects (which is "%s").',
|
||||
$policy->getShortName(),
|
||||
$default_policy->getShortName());
|
||||
} else if ($policy->isStrongerThan($default_policy)) {
|
||||
} else if ($strength == PhabricatorPolicyStrengthConstants::STRONGER) {
|
||||
$info = pht(
|
||||
'This object has a more restrictive policy ("%s") than the default '.
|
||||
'policy for similar objects (which is "%s").',
|
||||
|
|
|
@ -434,11 +434,12 @@ final class PhabricatorPolicy
|
|||
$capability,
|
||||
$active_only) {
|
||||
|
||||
$exceptions = array();
|
||||
if ($object instanceof PhabricatorPolicyCodexInterface) {
|
||||
$codex = PhabricatorPolicyCodex::newFromObject($object, $viewer);
|
||||
$codex = id(PhabricatorPolicyCodex::newFromObject($object, $viewer))
|
||||
->setCapability($capability);
|
||||
$rules = $codex->getPolicySpecialRuleDescriptions();
|
||||
|
||||
$exceptions = array();
|
||||
foreach ($rules as $rule) {
|
||||
$is_active = $rule->getIsActive();
|
||||
if ($is_active) {
|
||||
|
@ -467,11 +468,13 @@ final class PhabricatorPolicy
|
|||
|
||||
$exceptions[] = $description;
|
||||
}
|
||||
} else if (method_exists($object, 'describeAutomaticCapability')) {
|
||||
$exceptions = (array)$object->describeAutomaticCapability($capability);
|
||||
$exceptions = array_filter($exceptions);
|
||||
} else {
|
||||
$exceptions = array();
|
||||
}
|
||||
|
||||
if (!$exceptions) {
|
||||
if (method_exists($object, 'describeAutomaticCapability')) {
|
||||
$exceptions = (array)$object->describeAutomaticCapability($capability);
|
||||
$exceptions = array_filter($exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
return $exceptions;
|
||||
|
|
|
@ -473,30 +473,47 @@ final class PHUIHeaderView extends AphrontTagView {
|
|||
// policy differs from the default policy. If it does, we'll call it out
|
||||
// as changed.
|
||||
if (!$use_space_policy) {
|
||||
$default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject(
|
||||
$viewer,
|
||||
$object,
|
||||
$view_capability);
|
||||
if ($default_policy) {
|
||||
if ($default_policy->getPHID() != $policy->getPHID()) {
|
||||
$container_classes[] = 'policy-adjusted';
|
||||
if ($default_policy->isStrongerThan($policy)) {
|
||||
// The policy has strictly been weakened. For example, the
|
||||
// default might be "All Users" and the current policy is "Public".
|
||||
$container_classes[] = 'policy-adjusted-weaker';
|
||||
} else if ($policy->isStrongerThan($default_policy)) {
|
||||
// The policy has strictly been strengthened, and is now more
|
||||
// restrictive than the default. For example, "All Users" has
|
||||
// been replaced with "No One".
|
||||
$container_classes[] = 'policy-adjusted-stronger';
|
||||
} else {
|
||||
// The policy has been adjusted but not strictly strengthened
|
||||
// or weakened. For example, "Members of X" has been replaced with
|
||||
// "Members of Y".
|
||||
$container_classes[] = 'policy-adjusted-different';
|
||||
$strength = null;
|
||||
if ($object instanceof PhabricatorPolicyCodexInterface) {
|
||||
$codex = id(PhabricatorPolicyCodex::newFromObject($object, $viewer))
|
||||
->setCapability($view_capability);
|
||||
$strength = $codex->compareToDefaultPolicy($policy);
|
||||
} else {
|
||||
$default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject(
|
||||
$viewer,
|
||||
$object,
|
||||
$view_capability);
|
||||
|
||||
if ($default_policy) {
|
||||
if ($default_policy->getPHID() != $policy->getPHID()) {
|
||||
if ($default_policy->isStrongerThan($policy)) {
|
||||
$strength = PhabricatorPolicyStrengthConstants::WEAKER;
|
||||
} else if ($policy->isStrongerThan($default_policy)) {
|
||||
$strength = PhabricatorPolicyStrengthConstants::STRONGER;
|
||||
} else {
|
||||
$strength = PhabricatorPolicyStrengthConstants::ADJUSTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($strength) {
|
||||
if ($strength == PhabricatorPolicyStrengthConstants::WEAKER) {
|
||||
// The policy has strictly been weakened. For example, the
|
||||
// default might be "All Users" and the current policy is "Public".
|
||||
$container_classes[] = 'policy-adjusted-weaker';
|
||||
} else if ($strength == PhabricatorPolicyStrengthConstants::STRONGER) {
|
||||
// The policy has strictly been strengthened, and is now more
|
||||
// restrictive than the default. For example, "All Users" has
|
||||
// been replaced with "No One".
|
||||
$container_classes[] = 'policy-adjusted-stronger';
|
||||
} else {
|
||||
// The policy has been adjusted but not strictly strengthened
|
||||
// or weakened. For example, "Members of X" has been replaced with
|
||||
// "Members of Y".
|
||||
$container_classes[] = 'policy-adjusted-different';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$policy_name = array($policy->getShortName());
|
||||
|
|
|
@ -105,6 +105,29 @@ JX.behavior('document-engine', function(config, statics) {
|
|||
|
||||
list.addItem(highlight_item);
|
||||
|
||||
var blame_item;
|
||||
if (data.blame.uri) {
|
||||
blame_item = new JX.PHUIXActionView()
|
||||
.setIcon(data.blame.icon);
|
||||
|
||||
var onblame = JX.bind(null, function(data, e) {
|
||||
e.prevent();
|
||||
|
||||
if (blame_item.getDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.blame.enabled = !data.blame.enabled;
|
||||
onview(data);
|
||||
|
||||
menu.close();
|
||||
}, data);
|
||||
|
||||
blame_item.setHandler(onblame);
|
||||
|
||||
list.addItem(blame_item);
|
||||
}
|
||||
|
||||
menu.setContent(list.getNode());
|
||||
|
||||
menu.listen('open', function() {
|
||||
|
@ -118,8 +141,22 @@ JX.behavior('document-engine', function(config, statics) {
|
|||
if (is_selected) {
|
||||
encode_item.setDisabled(!engine.spec.canEncode);
|
||||
highlight_item.setDisabled(!engine.spec.canHighlight);
|
||||
if (blame_item) {
|
||||
blame_item.setDisabled(!engine.spec.canBlame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blame_item) {
|
||||
var blame_label;
|
||||
if (data.blame.enabled) {
|
||||
blame_label = data.blame.hide;
|
||||
} else {
|
||||
blame_label = data.blame.show;
|
||||
}
|
||||
|
||||
blame_item.setName(blame_label);
|
||||
}
|
||||
});
|
||||
|
||||
data.menu = menu;
|
||||
|
@ -137,6 +174,12 @@ JX.behavior('document-engine', function(config, statics) {
|
|||
uri.setQueryParam('encode', data.encode.value);
|
||||
}
|
||||
|
||||
if (data.blame.enabled) {
|
||||
uri.setQueryParam('blame', null);
|
||||
} else {
|
||||
uri.setQueryParam('blame', 'off');
|
||||
}
|
||||
|
||||
return uri.toString();
|
||||
}
|
||||
|
||||
|
@ -211,7 +254,7 @@ JX.behavior('document-engine', function(config, statics) {
|
|||
JX.DOM.setContent(viewport, JX.$H(r.markup));
|
||||
|
||||
// If this engine supports rendering blame, populate or draw it.
|
||||
if (spec.canBlame) {
|
||||
if (spec.canBlame && data.blame.enabled) {
|
||||
blame(data);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue