1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-23 12:09:12 +01:00

(stable) Promote 2021 Week 11

This commit is contained in:
epriestley 2021-03-12 21:49:30 -08:00
commit e7d3bae2cc
44 changed files with 549 additions and 287 deletions

View file

@ -11,12 +11,20 @@ $chunk_size = 4096;
$temporary_table = 'tmp_20210215_changeset_id_map';
queryfx(
$conn,
'CREATE TEMPORARY TABLE %T (
changeset_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
changeset_phid VARBINARY(64) NOT NULL)',
$temporary_table);
try {
queryfx(
$conn,
'CREATE TEMPORARY TABLE %T (
changeset_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
changeset_phid VARBINARY(64) NOT NULL)',
$temporary_table);
} catch (AphrontAccessDeniedQueryException $ex) {
throw new PhutilProxyException(
pht(
'Failed to "CREATE TEMPORARY TABLE". You may need to "GRANT" the '.
'current MySQL user this permission.'),
$ex);
}
$table_iterator = id(new LiskRawMigrationIterator($conn, $table_name))
->setPageSize($chunk_size);

View file

@ -0,0 +1,5 @@
UPDATE {$NAMESPACE}_repository.repository_auditrequest
SET auditStatus = 'accepted' WHERE auditStatus = 'closed';
DELETE FROM {$NAMESPACE}_repository.repository_auditrequest
WHERE auditStatus IN ('', 'cc', 'audit-not-required');

View file

@ -724,6 +724,7 @@ phutil_register_library_map(array(
'DiffusionAuditorsAddAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php',
'DiffusionAuditorsAddSelfHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php',
'DiffusionAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsHeraldAction.php',
'DiffusionAuditorsSearchEngineAttachment' => 'applications/diffusion/engineextension/DiffusionAuditorsSearchEngineAttachment.php',
'DiffusionBlameConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBlameConduitAPIMethod.php',
'DiffusionBlameController' => 'applications/diffusion/controller/DiffusionBlameController.php',
'DiffusionBlameQuery' => 'applications/diffusion/query/blame/DiffusionBlameQuery.php',
@ -2304,7 +2305,7 @@ phutil_register_library_map(array(
'PhabricatorAuditManagementDeleteWorkflow' => 'applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php',
'PhabricatorAuditManagementWorkflow' => 'applications/audit/management/PhabricatorAuditManagementWorkflow.php',
'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php',
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/PhabricatorAuditStatusConstants.php',
'PhabricatorAuditRequestStatus' => 'applications/audit/constants/PhabricatorAuditRequestStatus.php',
'PhabricatorAuditSynchronizeManagementWorkflow' => 'applications/audit/management/PhabricatorAuditSynchronizeManagementWorkflow.php',
'PhabricatorAuditTransaction' => 'applications/audit/storage/PhabricatorAuditTransaction.php',
'PhabricatorAuditTransactionComment' => 'applications/audit/storage/PhabricatorAuditTransactionComment.php',
@ -3208,6 +3209,7 @@ phutil_register_library_map(array(
'PhabricatorDocumentEngineBlock' => 'applications/files/diff/PhabricatorDocumentEngineBlock.php',
'PhabricatorDocumentEngineBlockDiff' => 'applications/files/diff/PhabricatorDocumentEngineBlockDiff.php',
'PhabricatorDocumentEngineBlocks' => 'applications/files/diff/PhabricatorDocumentEngineBlocks.php',
'PhabricatorDocumentEngineParserException' => 'applications/files/document/exception/PhabricatorDocumentEngineParserException.php',
'PhabricatorDocumentRef' => 'applications/files/document/PhabricatorDocumentRef.php',
'PhabricatorDocumentRenderingEngine' => 'applications/files/document/render/PhabricatorDocumentRenderingEngine.php',
'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php',
@ -4251,6 +4253,7 @@ phutil_register_library_map(array(
'PhabricatorPhurlURLViewController' => 'applications/phurl/controller/PhabricatorPhurlURLViewController.php',
'PhabricatorPinnedApplicationsSetting' => 'applications/settings/setting/PhabricatorPinnedApplicationsSetting.php',
'PhabricatorPirateEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorPirateEnglishTranslation.php',
'PhabricatorPlatform404Controller' => 'applications/base/controller/PhabricatorPlatform404Controller.php',
'PhabricatorPlatformSite' => 'aphront/site/PhabricatorPlatformSite.php',
'PhabricatorPointsEditField' => 'applications/transactions/editfield/PhabricatorPointsEditField.php',
'PhabricatorPointsFact' => 'applications/fact/fact/PhabricatorPointsFact.php',
@ -4686,7 +4689,11 @@ phutil_register_library_map(array(
'PhabricatorRequestExceptionHandler' => 'aphront/handler/PhabricatorRequestExceptionHandler.php',
'PhabricatorResetPasswordUserLogType' => 'applications/people/userlog/PhabricatorResetPasswordUserLogType.php',
'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php',
'PhabricatorRobotsController' => 'applications/system/controller/PhabricatorRobotsController.php',
'PhabricatorRobotsBlogController' => 'applications/system/controller/robots/PhabricatorRobotsBlogController.php',
'PhabricatorRobotsController' => 'applications/system/controller/robots/PhabricatorRobotsController.php',
'PhabricatorRobotsPlatformController' => 'applications/system/controller/robots/PhabricatorRobotsPlatformController.php',
'PhabricatorRobotsResourceController' => 'applications/system/controller/robots/PhabricatorRobotsResourceController.php',
'PhabricatorRobotsShortController' => 'applications/system/controller/robots/PhabricatorRobotsShortController.php',
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php',
'PhabricatorSMSAuthFactor' => 'applications/auth/factor/PhabricatorSMSAuthFactor.php',
'PhabricatorSQLPatchList' => 'infrastructure/storage/patch/PhabricatorSQLPatchList.php',
@ -6843,6 +6850,7 @@ phutil_register_library_map(array(
'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction',
'DiffusionAuditorsAddSelfHeraldAction' => 'DiffusionAuditorsHeraldAction',
'DiffusionAuditorsHeraldAction' => 'HeraldAction',
'DiffusionAuditorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'DiffusionBlameConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionBlameController' => 'DiffusionController',
'DiffusionBlameQuery' => 'DiffusionQuery',
@ -8649,7 +8657,7 @@ phutil_register_library_map(array(
'PhabricatorAuditManagementDeleteWorkflow' => 'PhabricatorAuditManagementWorkflow',
'PhabricatorAuditManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorAuditReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhabricatorAuditStatusConstants' => 'Phobject',
'PhabricatorAuditRequestStatus' => 'Phobject',
'PhabricatorAuditSynchronizeManagementWorkflow' => 'PhabricatorAuditManagementWorkflow',
'PhabricatorAuditTransaction' => 'PhabricatorModularTransaction',
'PhabricatorAuditTransactionComment' => array(
@ -9705,6 +9713,7 @@ phutil_register_library_map(array(
'PhabricatorDocumentEngineBlock' => 'Phobject',
'PhabricatorDocumentEngineBlockDiff' => 'Phobject',
'PhabricatorDocumentEngineBlocks' => 'Phobject',
'PhabricatorDocumentEngineParserException' => 'Exception',
'PhabricatorDocumentRef' => 'Phobject',
'PhabricatorDocumentRenderingEngine' => 'Phobject',
'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication',
@ -10910,6 +10919,7 @@ phutil_register_library_map(array(
'PhabricatorPhurlURLViewController' => 'PhabricatorPhurlController',
'PhabricatorPinnedApplicationsSetting' => 'PhabricatorInternalSetting',
'PhabricatorPirateEnglishTranslation' => 'PhutilTranslation',
'PhabricatorPlatform404Controller' => 'PhabricatorController',
'PhabricatorPlatformSite' => 'PhabricatorSite',
'PhabricatorPointsEditField' => 'PhabricatorEditField',
'PhabricatorPointsFact' => 'PhabricatorFact',
@ -11466,7 +11476,11 @@ phutil_register_library_map(array(
'PhabricatorRequestExceptionHandler' => 'AphrontRequestExceptionHandler',
'PhabricatorResetPasswordUserLogType' => 'PhabricatorUserLogType',
'PhabricatorResourceSite' => 'PhabricatorSite',
'PhabricatorRobotsBlogController' => 'PhabricatorRobotsController',
'PhabricatorRobotsController' => 'PhabricatorController',
'PhabricatorRobotsPlatformController' => 'PhabricatorRobotsController',
'PhabricatorRobotsResourceController' => 'PhabricatorRobotsController',
'PhabricatorRobotsShortController' => 'PhabricatorRobotsController',
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorSMSAuthFactor' => 'PhabricatorAuthFactor',
'PhabricatorSQLPatchList' => 'Phobject',

View file

@ -32,10 +32,6 @@ final class AphrontApplicationConfiguration
return $request;
}
public function build404Controller() {
return array(new Phabricator404Controller(), array());
}
public function buildRedirectController($uri, $external) {
return array(
new PhabricatorRedirectController(),
@ -504,7 +500,10 @@ final class AphrontApplicationConfiguration
return array($result, array());
}
return $this->build404Controller();
throw new Exception(
pht(
'Aphront site ("%s") failed to build a 404 controller.',
get_class($site)));
}
/**

View file

@ -10,10 +10,17 @@ final class Aphront404Response extends AphrontHTMLResponse {
$request = $this->getRequest();
$viewer = $request->getViewer();
// See T13636. Note that this response may be served from a Site other than
// the primary PlatformSite. For now, always link to the PlatformSite.
// (This may not be the best possible place to send users who are currently
// on "real" sites, like the BlogSite.)
$return_uri = PhabricatorEnv::getURI('/');
$dialog = id(new AphrontDialogView())
->setViewer($viewer)
->setTitle(pht('404 Not Found'))
->addCancelButton('/', pht('Return to Charted Waters'))
->addCancelButton($return_uri, pht('Return to Charted Waters'))
->appendParagraph(
pht(
'You arrive at your destination, but there is nothing here.'))

View file

@ -10,7 +10,7 @@ abstract class AphrontSite extends Phobject {
abstract public function getRoutingMaps();
public function new404Controller(AphrontRequest $request) {
return null;
return new Phabricator404Controller();
}
protected function isHostMatch($host, array $uris) {

View file

@ -50,4 +50,8 @@ final class PhabricatorPlatformSite extends PhabricatorSite {
return $maps;
}
public function new404Controller(AphrontRequest $request) {
return new PhabricatorPlatform404Controller();
}
}

View file

@ -0,0 +1,89 @@
<?php
final class PhabricatorAuditRequestStatus extends Phobject {
const AUDIT_REQUIRED = 'audit-required';
const CONCERNED = 'concerned';
const ACCEPTED = 'accepted';
const AUDIT_REQUESTED = 'requested';
const RESIGNED = 'resigned';
private $key;
public static function newForStatus($status) {
$result = new self();
$result->key = $status;
return $result;
}
public function getIconIcon() {
return $this->getMapProperty('icon');
}
public function getIconColor() {
return $this->getMapProperty('icon.color');
}
public function getStatusName() {
$name = $this->getMapProperty('name');
if ($name !== null) {
return $name;
}
return pht('Unknown Audit Request Status ("%s")', $this->key);
}
public function getStatusValue() {
return $this->key;
}
public function getStatusValueForConduit() {
return $this->getMapProperty('value.conduit');
}
public function isResigned() {
return ($this->key === self::RESIGNED);
}
private function getMapProperty($key, $default = null) {
$map = self::newStatusMap();
$spec = idx($map, $this->key, array());
return idx($spec, $key, $default);
}
private static function newStatusMap() {
return array(
self::AUDIT_REQUIRED => array(
'name' => pht('Audit Required'),
'icon' => 'fa-exclamation-circle',
'icon.color' => 'orange',
'value.conduit' => 'audit-required',
),
self::AUDIT_REQUESTED => array(
'name' => pht('Audit Requested'),
'icon' => 'fa-exclamation-circle',
'icon.color' => 'orange',
'value.conduit' => 'audit-requested',
),
self::CONCERNED => array(
'name' => pht('Concern Raised'),
'icon' => 'fa-times-circle',
'icon.color' => 'red',
'value.conduit' => 'concern-raised',
),
self::ACCEPTED => array(
'name' => pht('Accepted'),
'icon' => 'fa-check-circle',
'icon.color' => 'green',
'value.conduit' => 'accepted',
),
self::RESIGNED => array(
'name' => pht('Resigned'),
'icon' => 'fa-times',
'icon.color' => 'grey',
'value.conduit' => 'resigned',
),
);
}
}

View file

@ -1,108 +0,0 @@
<?php
final class PhabricatorAuditStatusConstants extends Phobject {
const NONE = '';
const AUDIT_NOT_REQUIRED = 'audit-not-required';
const AUDIT_REQUIRED = 'audit-required';
const CONCERNED = 'concerned';
const ACCEPTED = 'accepted';
const AUDIT_REQUESTED = 'requested';
const RESIGNED = 'resigned';
const CLOSED = 'closed';
const CC = 'cc';
public static function getStatusNameMap() {
$map = array(
self::NONE => pht('Not Applicable'),
self::AUDIT_NOT_REQUIRED => pht('Audit Not Required'),
self::AUDIT_REQUIRED => pht('Audit Required'),
self::CONCERNED => pht('Concern Raised'),
self::ACCEPTED => pht('Accepted'),
self::AUDIT_REQUESTED => pht('Audit Requested'),
self::RESIGNED => pht('Resigned'),
self::CLOSED => pht('Closed'),
self::CC => pht("Was CC'd"),
);
return $map;
}
public static function getActionRequiredStatusConstants() {
return array(
self::AUDIT_REQUIRED,
self::AUDIT_REQUESTED,
);
}
public static function getStatusName($code) {
return idx(self::getStatusNameMap(), $code, pht('Unknown'));
}
public static function getStatusColor($code) {
switch ($code) {
case self::CONCERNED:
$color = 'red';
break;
case self::AUDIT_REQUIRED:
case self::AUDIT_REQUESTED:
$color = 'orange';
break;
case self::ACCEPTED:
$color = 'green';
break;
case self::AUDIT_NOT_REQUIRED:
$color = 'blue';
break;
case self::CLOSED:
$color = 'dark';
break;
case self::RESIGNED:
$color = 'grey';
break;
default:
$color = 'bluegrey';
break;
}
return $color;
}
public static function getStatusIcon($code) {
switch ($code) {
case self::AUDIT_NOT_REQUIRED:
$icon = PHUIStatusItemView::ICON_OPEN;
break;
case self::AUDIT_REQUIRED:
case self::AUDIT_REQUESTED:
$icon = PHUIStatusItemView::ICON_WARNING;
break;
case self::CONCERNED:
$icon = PHUIStatusItemView::ICON_REJECT;
break;
case self::ACCEPTED:
case self::CLOSED:
$icon = PHUIStatusItemView::ICON_ACCEPT;
break;
case self::RESIGNED:
$icon = 'fa-times';
break;
default:
$icon = PHUIStatusItemView::ICON_QUESTION;
break;
}
return $icon;
}
public static function getOpenStatusConstants() {
return array(
self::AUDIT_REQUIRED,
self::AUDIT_REQUESTED,
self::CONCERNED,
);
}
public static function isOpenStatus($status) {
return in_array($status, self::getOpenStatusConstants());
}
}

View file

@ -178,12 +178,6 @@ final class PhabricatorAuditEditor
}
$object->attachAudits($commit->getAudits());
$status_concerned = PhabricatorAuditStatusConstants::CONCERNED;
$status_closed = PhabricatorAuditStatusConstants::CLOSED;
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
$status_accepted = PhabricatorAuditStatusConstants::ACCEPTED;
$status_concerned = PhabricatorAuditStatusConstants::CONCERNED;
$actor_phid = $this->getActingAsPHID();
$actor_is_author = ($object->getAuthorPHID()) &&
($actor_phid == $object->getAuthorPHID());
@ -491,12 +485,6 @@ final class PhabricatorAuditEditor
}
foreach ($object->getAudits() as $audit) {
if (!$audit->isInteresting()) {
// Don't send mail to uninteresting auditors, like packages which
// own this code but which audits have not triggered for.
continue;
}
if (!$audit->isResigned()) {
$phids[] = $audit->getAuditorPHID();
}

View file

@ -153,13 +153,13 @@ final class PhabricatorAuditManagementDeleteWorkflow
foreach ($commit_audits as $audit) {
$audit_id = $audit->getID();
$status = $audit->getAuditRequestStatusObject();
$description = sprintf(
'%10d %-16s %-16s %s: %s',
$audit_id,
$handles[$audit->getAuditorPHID()]->getName(),
PhabricatorAuditStatusConstants::getStatusName(
$audit->getAuditStatus()),
$status->getStatusName(),
$commit->getRepository()->formatCommitName(
$commit->getCommitIdentifier()),
trim($commit->getSummary()));

View file

@ -39,7 +39,13 @@ final class PhabricatorAuthMainMenuBarExtension
private function buildLoginMenu() {
$controller = $this->getController();
$uri = new PhutilURI('/auth/start/');
// See T13636. This button may be rendered by the 404 controller on sites
// other than the primary PlatformSite. Link the button to the primary
// site.
$uri = '/auth/start/';
$uri = PhabricatorEnv::getURI($uri);
$uri = new PhutilURI($uri);
if ($controller) {
$path = $controller->getRequest()->getPath();
$uri->replaceQueryParam('next', $path);

View file

@ -1,6 +1,11 @@
<?php
final class Phabricator404Controller extends PhabricatorController {
final class Phabricator404Controller
extends PhabricatorController {
public function shouldRequireLogin() {
return false;
}
public function processRequest() {
return new Aphront404Response();

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorPlatform404Controller
extends PhabricatorController {
public function processRequest() {
return new Aphront404Response();
}
}

View file

@ -61,16 +61,22 @@ final class PhabricatorDiffusionConfigOptions
->setDescription(pht('Hard byte limit on including patches in email.')),
$this->newOption('metamta.diffusion.time-limit', 'int', 60)
->setDescription(pht('Hard time limit on generating patches.')),
$this->newOption(
'audit.can-author-close-audit',
'bool',
false)
->setBoolOptions(
array(
pht('Enable Closing Audits'),
pht('Disable Closing Audits'),
pht('Enable Self-Accept'),
pht('Disable Self-Accept'),
))
->setDescription(pht('Controls whether Author can Close Audits.')),
->setDescription(
pht(
'Allows the author of a commit to be an auditor and accept their '.
'own commits. Note that this behavior is different from the '.
'behavior implied by the name of the option: long ago, it did '.
'something else.')),
$this->newOption('bugtraq.url', 'string', null)
->addExample('https://bugs.php.net/%BUGID%', pht('PHP bugs'))

View file

@ -598,10 +598,6 @@ final class DiffusionCommitController extends DiffusionController {
$other_requests = array();
foreach ($audit_requests as $audit_request) {
if (!$audit_request->isInteresting()) {
continue;
}
if ($audit_request->isUser()) {
$user_requests[] = $audit_request;
} else {
@ -902,12 +898,13 @@ final class DiffusionCommitController extends DiffusionController {
$view = new PHUIStatusListView();
foreach ($audit_requests as $request) {
$code = $request->getAuditStatus();
$status = $request->getAuditRequestStatusObject();
$item = new PHUIStatusItemView();
$item->setIcon(
PhabricatorAuditStatusConstants::getStatusIcon($code),
PhabricatorAuditStatusConstants::getStatusColor($code),
PhabricatorAuditStatusConstants::getStatusName($code));
$status->getIconIcon(),
$status->getIconColor(),
$status->getStatusName());
$auditor_phid = $request->getAuditorPHID();
$target = $viewer->renderHandle($auditor_phid);

View file

@ -98,9 +98,9 @@ final class DiffusionDoorkeeperCommitFeedStoryPublisher
}
switch ($status) {
case PhabricatorAuditStatusConstants::AUDIT_REQUIRED:
case PhabricatorAuditStatusConstants::AUDIT_REQUESTED:
case PhabricatorAuditStatusConstants::CONCERNED:
case PhabricatorAuditRequestStatus::AUDIT_REQUIRED:
case PhabricatorAuditRequestStatus::AUDIT_REQUESTED:
case PhabricatorAuditRequestStatus::CONCERNED:
$active += array_fuse($request_phids);
break;
default:

View file

@ -0,0 +1,36 @@
<?php
final class DiffusionAuditorsSearchEngineAttachment
extends PhabricatorSearchEngineAttachment {
public function getAttachmentName() {
return pht('Diffusion Auditors');
}
public function getAttachmentDescription() {
return pht('Get the auditors for each commit.');
}
public function willLoadAttachmentData($query, $spec) {
$query->needAuditRequests(true);
}
public function getAttachmentForObject($object, $data, $spec) {
$auditors = $object->getAudits();
$list = array();
foreach ($auditors as $auditor) {
$status = $auditor->getAuditRequestStatusObject();
$list[] = array(
'auditorPHID' => $auditor->getAuditorPHID(),
'status' => $status->getStatusValueForConduit(),
);
}
return array(
'auditors' => $list,
);
}
}

View file

@ -38,9 +38,7 @@ abstract class DiffusionAuditorsHeraldAction
$current = array();
foreach ($auditors as $auditor) {
if ($auditor->isInteresting()) {
$current[] = $auditor->getAuditorPHID();
}
$current[] = $auditor->getAuditorPHID();
}
$allowed_types = array(

View file

@ -22,9 +22,11 @@ final class DiffusionCommitAuditorsHeraldField
$phids = array();
foreach ($audits as $audit) {
if ($audit->isActiveAudit()) {
$phids[] = $audit->getAuditorPHID();
if ($audit->isResigned()) {
continue;
}
$phids[] = $audit->getAuditorPHID();
}
return $phids;

View file

@ -166,8 +166,8 @@ final class HeraldCommitAdapter
public function loadAuditNeededPackages() {
if ($this->auditNeededPackages === null) {
$status_arr = array(
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
PhabricatorAuditStatusConstants::CONCERNED,
PhabricatorAuditRequestStatus::AUDIT_REQUIRED,
PhabricatorAuditRequestStatus::CONCERNED,
);
$requests = id(new PhabricatorRepositoryAuditRequest())
->loadAllWhere(

View file

@ -90,7 +90,7 @@ final class DiffusionCommitRequiredActionResultBucket
$objects = $this->objects;
$has_concern = array(
PhabricatorAuditStatusConstants::CONCERNED,
PhabricatorAuditRequestStatus::CONCERNED,
);
$has_concern = array_fuse($has_concern);
@ -119,8 +119,8 @@ final class DiffusionCommitRequiredActionResultBucket
$objects = $this->objects;
$should_audit = array(
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
PhabricatorAuditStatusConstants::AUDIT_REQUESTED,
PhabricatorAuditRequestStatus::AUDIT_REQUIRED,
PhabricatorAuditRequestStatus::AUDIT_REQUESTED,
);
$should_audit = array_fuse($should_audit);

View file

@ -31,7 +31,7 @@ final class DiffusionCommitAcceptTransaction
}
public function applyExternalEffects($object, $value) {
$status = PhabricatorAuditStatusConstants::ACCEPTED;
$status = PhabricatorAuditRequestStatus::ACCEPTED;
$actor = $this->getActor();
$this->applyAuditorEffect($object, $actor, $value, $status);
}

View file

@ -21,12 +21,12 @@ abstract class DiffusionCommitAuditTransaction
PhabricatorRepositoryCommit $commit,
PhabricatorUser $viewer) {
// This omits various inactive states like "Resigned" and "Not Required".
// This omits inactive states; currently just "Resigned".
$active = array(
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
PhabricatorAuditStatusConstants::CONCERNED,
PhabricatorAuditStatusConstants::ACCEPTED,
PhabricatorAuditStatusConstants::AUDIT_REQUESTED,
PhabricatorAuditRequestStatus::AUDIT_REQUIRED,
PhabricatorAuditRequestStatus::CONCERNED,
PhabricatorAuditRequestStatus::ACCEPTED,
PhabricatorAuditRequestStatus::AUDIT_REQUESTED,
);
$active = array_fuse($active);
@ -42,7 +42,7 @@ abstract class DiffusionCommitAuditTransaction
$commit,
$viewer,
array(
PhabricatorAuditStatusConstants::ACCEPTED,
PhabricatorAuditRequestStatus::ACCEPTED,
));
}
@ -53,7 +53,7 @@ abstract class DiffusionCommitAuditTransaction
$commit,
$viewer,
array(
PhabricatorAuditStatusConstants::CONCERNED,
PhabricatorAuditRequestStatus::CONCERNED,
));
}
@ -117,7 +117,7 @@ abstract class DiffusionCommitAuditTransaction
$map = array();
$with_authority = ($status != PhabricatorAuditStatusConstants::RESIGNED);
$with_authority = ($status != PhabricatorAuditRequestStatus::RESIGNED);
if ($with_authority) {
foreach ($audits as $audit) {
if ($commit->hasAuditAuthority($actor, $audit, $acting_phid)) {

View file

@ -16,7 +16,7 @@ final class DiffusionCommitAuditorsTransaction
$auditors = $this->generateOldValue($object);
$old_auditors = $auditors;
$request_status = PhabricatorAuditStatusConstants::AUDIT_REQUESTED;
$request_status = PhabricatorAuditRequestStatus::AUDIT_REQUESTED;
$rem = idx($value, '-', array());
foreach ($rem as $phid) {

View file

@ -37,7 +37,7 @@ final class DiffusionCommitConcernTransaction
}
public function applyExternalEffects($object, $value) {
$status = PhabricatorAuditStatusConstants::CONCERNED;
$status = PhabricatorAuditRequestStatus::CONCERNED;
$actor = $this->getActor();
$this->applyAuditorEffect($object, $actor, $value, $status);
}

View file

@ -36,7 +36,7 @@ final class DiffusionCommitResignTransaction
}
public function applyExternalEffects($object, $value) {
$status = PhabricatorAuditStatusConstants::RESIGNED;
$status = PhabricatorAuditRequestStatus::RESIGNED;
$actor = $this->getActor();
$this->applyAuditorEffect($object, $actor, $value, $status);
}

View file

@ -31,6 +31,17 @@ final class PhabricatorJSONDocumentEngine
try {
$data = phutil_json_decode($raw_data);
// See T13635. "phutil_json_decode()" always turns JSON into a PHP array,
// and we lose the distinction between "{}" and "[]". This distinction is
// important when rendering a document.
$data = json_decode($raw_data, false);
if (!$data) {
throw new PhabricatorDocumentEngineParserException(
pht(
'Failed to "json_decode(...)" JSON document after successfully '.
'decoding it with "phutil_json_decode(...).'));
}
if (preg_match('/^\s*\[/', $raw_data)) {
$content = id(new PhutilJSON())->encodeAsList($data);
} else {
@ -47,6 +58,13 @@ final class PhabricatorJSONDocumentEngine
'This document is not valid JSON: %s',
$ex->getMessage()));
$content = $raw_data;
} catch (PhabricatorDocumentEngineParserException $ex) {
$message = $this->newMessage(
pht(
'Unable to parse this document as JSON: %s',
$ex->getMessage()));
$content = $raw_data;
}

View file

@ -0,0 +1,4 @@
<?php
final class PhabricatorDocumentEngineParserException
extends Exception {}

View file

@ -63,25 +63,7 @@ final class PhabricatorNotificationQuery
$this->buildWhereClause($conn),
$this->buildLimitClause($conn));
// See T13623. Although most queries for notifications return unique
// stories, this isn't a guarantee.
$story_map = ipull($data, null, 'chronologicalKey');
$stories = PhabricatorFeedStory::loadAllFromRows(
$story_map,
$this->getViewer());
$stories = mpull($stories, null, 'getChronologicalKey');
$results = array();
foreach ($data as $row) {
$story_key = $row['chronologicalKey'];
$has_viewed = $row['hasViewed'];
$results[] = id(clone $stories[$story_key])
->setHasViewed($has_viewed);
}
return $results;
return $data;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
@ -111,14 +93,47 @@ final class PhabricatorNotificationQuery
return $where;
}
protected function willFilterPage(array $stories) {
foreach ($stories as $key => $story) {
if (!$story->isVisibleInNotifications()) {
unset($stories[$key]);
protected function willFilterPage(array $rows) {
// See T13623. The policy model here is outdated and awkward.
// Users may have notifications about objects they can no longer see.
// Two ways this can arise: destroy an object; or change an object's
// view policy to exclude a user.
// "PhabricatorFeedStory::loadAllFromRows()" does its own policy filtering.
// This doesn't align well with modern query sequencing, but we should be
// able to get away with it by loading here.
// See T13623. Although most queries for notifications return unique
// stories, this isn't a guarantee.
$story_map = ipull($rows, null, 'chronologicalKey');
$viewer = $this->getViewer();
$stories = PhabricatorFeedStory::loadAllFromRows($story_map, $viewer);
$stories = mpull($stories, null, 'getChronologicalKey');
$results = array();
foreach ($rows as $row) {
$story_key = $row['chronologicalKey'];
$has_viewed = $row['hasViewed'];
if (!isset($stories[$story_key])) {
// NOTE: We can't call "didRejectResult()" here because we don't have
// a policy object to pass.
continue;
}
$story = id(clone $stories[$story_key])
->setHasViewed($has_viewed);
if (!$story->isVisibleInNotifications()) {
continue;
}
$results[] = $story;
}
return $stories;
return $results;
}
protected function getDefaultOrderVector() {
@ -166,7 +181,7 @@ final class PhabricatorNotificationQuery
protected function newPagingMapFromPartialObject($object) {
return array(
'key' => $object->getChronologicalKey(),
'key' => $object['chronologicalKey'],
);
}

View file

@ -70,7 +70,11 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
}
public function getBlogRoutes() {
return $this->getLiveRoutes();
return $this->getLiveRoutes() + array(
'/status/' => 'PhabricatorStatusController',
'/favicon.ico' => 'PhabricatorFaviconController',
'/robots.txt' => 'PhabricatorRobotsBlogController',
);
}
private function getLiveRoutes() {

View file

@ -55,6 +55,10 @@ final class PhabricatorPhurlApplication extends PhabricatorApplication {
public function getShortRoutes() {
return array(
'/status/' => 'PhabricatorStatusController',
'/favicon.ico' => 'PhabricatorFaviconController',
'/robots.txt' => 'PhabricatorRobotsShortController',
'/u/(?P<append>[^/]+)' => 'PhabricatorPhurlShortURLController',
'.*' => 'PhabricatorPhurlShortURLDefaultController',
);

View file

@ -49,38 +49,14 @@ final class PhabricatorRepositoryAuditRequest
return $this->assertAttached($this->commit);
}
public function isActiveAudit() {
switch ($this->getAuditStatus()) {
case PhabricatorAuditStatusConstants::NONE:
case PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED:
case PhabricatorAuditStatusConstants::RESIGNED:
case PhabricatorAuditStatusConstants::CLOSED:
case PhabricatorAuditStatusConstants::CC:
return false;
}
return true;
}
public function isInteresting() {
switch ($this->getAuditStatus()) {
case PhabricatorAuditStatusConstants::NONE:
case PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED:
return false;
}
return true;
}
public function isResigned() {
switch ($this->getAuditStatus()) {
case PhabricatorAuditStatusConstants::RESIGNED:
return true;
}
return false;
return $this->getAuditRequestStatusObject()->isResigned();
}
public function getAuditRequestStatusObject() {
$status = $this->getAuditStatus();
return PhabricatorAuditRequestStatus::newForStatus($status);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -312,14 +312,14 @@ final class PhabricatorRepositoryCommit
foreach ($requests as $request) {
switch ($request->getAuditStatus()) {
case PhabricatorAuditStatusConstants::AUDIT_REQUIRED:
case PhabricatorAuditStatusConstants::AUDIT_REQUESTED:
case PhabricatorAuditRequestStatus::AUDIT_REQUIRED:
case PhabricatorAuditRequestStatus::AUDIT_REQUESTED:
$any_need = true;
break;
case PhabricatorAuditStatusConstants::ACCEPTED:
case PhabricatorAuditRequestStatus::ACCEPTED:
$any_accept = true;
break;
case PhabricatorAuditStatusConstants::CONCERNED:
case PhabricatorAuditRequestStatus::CONCERNED:
$any_concern = true;
break;
}
@ -945,7 +945,10 @@ final class PhabricatorRepositoryCommit
}
public function getConduitSearchAttachments() {
return array();
return array(
id(new DiffusionAuditorsSearchEngineAttachment())
->setAttachmentKey('auditors'),
);
}

View file

@ -284,11 +284,24 @@ final class PhutilSearchQueryCompiler
$operator = self::OPERATOR_AND;
break;
case '':
// See T12995. If this query term contains Chinese, Japanese or
// Korean characters, treat the term as a substring term by default.
// These languages do not separate words with spaces, so the term
// search mode is normally useless.
if ($enable_functions && !$is_quoted && phutil_utf8_is_cjk($value)) {
$use_substring = false;
if ($enable_functions && !$is_quoted) {
// See T12995. If this query term contains Chinese, Japanese or
// Korean characters, treat the term as a substring term by default.
// These languages do not separate words with spaces, so the term
// search mode is normally useless.
if (phutil_utf8_is_cjk($value)) {
$use_substring = true;
} else if (phutil_preg_match('/^_/', $value)) {
// See T13632. Assume users searching for any term that begins
// with an undescore intend to perform substring search if they
// don't provide an explicit search function.
$use_substring = true;
}
}
if ($use_substring) {
$operator = self::OPERATOR_SUBSTRING;
} else {
$operator = self::OPERATOR_AND;

View file

@ -205,6 +205,20 @@ final class PhutilSearchQueryCompilerTestCase
'xyz',
),
),
// See T12995. Interpret CJK tokens as substring queries since these
// languages do not use spaces as word separators.
"\xE7\x8C\xAB" => array(
array(null, $op_sub, "\xE7\x8C\xAB"),
),
// See T13632. Interpret tokens that begin with "_" as substring tokens
// if no function is specified.
'_x _y_ "_z_"' => array(
array(null, $op_sub, '_x'),
array(null, $op_sub, '_y_'),
array(null, $op_and, '_z_'),
),
);
$this->assertCompileFunctionQueries($function_tests);

View file

@ -25,7 +25,7 @@ final class PhabricatorSystemApplication extends PhabricatorApplication {
'/status/' => 'PhabricatorStatusController',
'/debug/' => 'PhabricatorDebugController',
'/favicon.ico' => 'PhabricatorFaviconController',
'/robots.txt' => 'PhabricatorRobotsController',
'/robots.txt' => 'PhabricatorRobotsPlatformController',
'/services/' => array(
'encoding/' => 'PhabricatorSystemSelectEncodingController',
'highlight/' => 'PhabricatorSystemSelectHighlightController',
@ -38,4 +38,12 @@ final class PhabricatorSystemApplication extends PhabricatorApplication {
);
}
public function getResourceRoutes() {
return array(
'/status/' => 'PhabricatorStatusController',
'/favicon.ico' => 'PhabricatorFaviconController',
'/robots.txt' => 'PhabricatorRobotsResourceController',
);
}
}

View file

@ -0,0 +1,17 @@
<?php
final class PhabricatorRobotsBlogController
extends PhabricatorRobotsController {
protected function newRobotsRules() {
$out = array();
// Allow everything on blog domains to be indexed.
$out[] = 'User-Agent: *';
$out[] = 'Crawl-delay: 1';
return $out;
}
}

View file

@ -0,0 +1,22 @@
<?php
abstract class PhabricatorRobotsController extends PhabricatorController {
public function shouldRequireLogin() {
return false;
}
final public function processRequest() {
$out = $this->newRobotsRules();
$content = implode("\n", $out)."\n";
return id(new AphrontPlainTextResponse())
->setContent($content)
->setCacheDurationInSeconds(phutil_units('2 hours in seconds'))
->setCanCDN(true);
}
abstract protected function newRobotsRules();
}

View file

@ -1,12 +1,9 @@
<?php
final class PhabricatorRobotsController extends PhabricatorController {
final class PhabricatorRobotsPlatformController
extends PhabricatorRobotsController {
public function shouldRequireLogin() {
return false;
}
public function processRequest() {
protected function newRobotsRules() {
$out = array();
// Prevent indexing of '/diffusion/', since the content is not generally
@ -29,11 +26,7 @@ final class PhabricatorRobotsController extends PhabricatorController {
// probably not hugely concerned about cutting-edge SEO.
$out[] = 'Crawl-delay: 1';
$content = implode("\n", $out)."\n";
return id(new AphrontPlainTextResponse())
->setContent($content)
->setCacheDurationInSeconds(phutil_units('2 hours in seconds'))
->setCanCDN(true);
return $out;
}
}

View file

@ -0,0 +1,18 @@
<?php
final class PhabricatorRobotsResourceController
extends PhabricatorRobotsController {
protected function newRobotsRules() {
$out = array();
// See T13636. Prevent indexing of any content on resource domains.
$out[] = 'User-Agent: *';
$out[] = 'Disallow: /';
$out[] = 'Crawl-delay: 1';
return $out;
}
}

View file

@ -0,0 +1,18 @@
<?php
final class PhabricatorRobotsShortController
extends PhabricatorRobotsController {
protected function newRobotsRules() {
$out = array();
// See T13636. Prevent indexing of any content on short domains.
$out[] = 'User-Agent: *';
$out[] = 'Disallow: /';
$out[] = 'Crawl-delay: 1';
return $out;
}
}

View file

@ -8,7 +8,9 @@ final class TransactionSearchConduitAPIMethod
}
public function getMethodDescription() {
return pht('Read transactions and comments for an object.');
return pht(
'Read transactions and comments for a particular object '.
'or an entire object type.');
}
public function getMethodDocumentation() {
@ -23,6 +25,26 @@ One common reason to call this method is that you're implmenting a webhook and
just received a notification that an object has changed. See the Webhooks
documentation for more detailed discussion of this use case.
One Object Type at a Time
=========================
This API method can query transactions for any type of object which supports
transactions, but only one type of object can be queried per call. For example:
you can retrieve transactions affecting Tasks, or you can retrieve transactions
affecting Revisions, but a single call can not retrieve both.
This is a technical limitation arising because (among other reasons) there is
no global ordering on transactions.
To find transactions for a specific object (like a particular task), pass the
object PHID or an appropriate object identifier (like `T123`) as an
`objectIdentifier`.
To find all transactions for an object type, pass the object type constant as
an `objectType`. For example, the correct identifier for tasks is `TASK`. (You
can quickly find an unknown type constant by looking at the PHID of an object
of that type.)
Constraints
===========
@ -64,8 +86,9 @@ EOREMARKUP
protected function defineParamTypes() {
return array(
'objectIdentifier' => 'phid|string',
'constraints' => 'map<string, wild>',
'objectIdentifier' => 'optional phid|string',
'objectType' => 'optional string',
'constraints' => 'optional map<string, wild>',
) + $this->getPagerParamTypes();
}
@ -81,43 +104,19 @@ EOREMARKUP
$viewer = $request->getUser();
$pager = $this->newPager($request);
$object_name = $request->getValue('objectIdentifier', null);
if (!strlen($object_name)) {
throw new Exception(
pht(
'When calling "transaction.search", you must provide an object to '.
'retrieve transactions for.'));
}
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames(array($object_name))
->executeOne();
if (!$object) {
throw new Exception(
pht(
'No object "%s" exists.',
$object_name));
}
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
throw new Exception(
pht(
'Object "%s" (of type "%s") does not implement "%s", so '.
'transactions can not be loaded for it.',
$object_name,
get_class($object),
'PhabricatorApplicationTransactionInterface'));
}
$object = $this->loadTemplateObject($request);
$xaction_query = PhabricatorApplicationTransactionQuery::newQueryForObject(
$object);
$xaction_query
->needHandles(false)
->withObjectPHIDs(array($object->getPHID()))
->setViewer($viewer);
if ($object->getPHID()) {
$xaction_query->withObjectPHIDs(array($object->getPHID()));
}
$constraints = $request->getValue('constraints', array());
$xaction_query = $this->applyConstraints($constraints, $xaction_query);
@ -355,4 +354,65 @@ EOREMARKUP
);
}
private function loadTemplateObject(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$object_identifier = $request->getValue('objectIdentifier');
$object_type = $request->getValue('objectType');
$has_identifier = ($object_identifier !== null);
$has_type = ($object_type !== null);
if (!$has_type && !$has_identifier) {
throw new Exception(
pht(
'Calls to "transaction.search" must specify either an "objectType" '.
'or an "objectIdentifier"'));
} else if ($has_type && $has_identifier) {
throw new Exception(
pht(
'Calls to "transaction.search" must not specify both an '.
'"objectType" and an "objectIdentifier".'));
}
if ($has_type) {
$all_types = PhabricatorPHIDType::getAllTypes();
if (!isset($all_types[$object_type])) {
ksort($all_types);
throw new Exception(
pht(
'In call to "transaction.search", specified "objectType" ("%s") '.
'is unknown. Valid object types are: %s.',
$object_type,
implode(', ', array_keys($all_types))));
}
$object = $all_types[$object_type]->newObject();
} else {
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames(array($object_identifier))
->executeOne();
if (!$object) {
throw new Exception(
pht(
'In call to "transaction.search", specified "objectIdentifier" '.
'("%s") does not exist.',
$object_identifier));
}
}
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
throw new Exception(
pht(
'In call to "transaction.search", selected object (of type "%s") '.
'does not implement "%s", so transactions can not be loaded for it.',
get_class($object),
'PhabricatorApplicationTransactionInterface'));
}
return $object;
}
}

View file

@ -347,7 +347,16 @@ abstract class AphrontBaseMySQLDatabaseConnection
case 1142: // Access denied to table
case 1143: // Access denied to column
case 1227: // Access denied (e.g., no SUPER for SHOW SLAVE STATUS).
throw new AphrontAccessDeniedQueryException($message);
// See T13622. Try to help users figure out that this is a GRANT
// problem.
$more = pht(
'This error usually indicates that you need to "GRANT" the '.
'MySQL user additional permissions. See "GRANT" in the MySQL '.
'manual for help.');
throw new AphrontAccessDeniedQueryException("{$message}\n\n{$more}");
case 1045: // Access denied (auth)
throw new AphrontInvalidCredentialsQueryException($message);
case 1146: // No such table