1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-15 10:00:55 +01:00

(stable) Promote 2017 Week 48

This commit is contained in:
epriestley 2017-12-01 13:28:40 -08:00
commit 9570b843ea
40 changed files with 792 additions and 240 deletions

View file

@ -9,7 +9,7 @@ return array(
'names' => array(
'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65',
'core.pkg.css' => '1a4e0c25',
'core.pkg.css' => 'fdb27ef9',
'core.pkg.js' => '4c79d74f',
'darkconsole.pkg.js' => '1f9a31bc',
'differential.pkg.css' => '45951e9e',
@ -176,7 +176,7 @@ return array(
'rsrc/css/phui/phui-spacing.css' => '042804d6',
'rsrc/css/phui/phui-status.css' => 'd5263e49',
'rsrc/css/phui/phui-tag-view.css' => 'b4719c50',
'rsrc/css/phui/phui-timeline-view.css' => 'f21db7ca',
'rsrc/css/phui/phui-timeline-view.css' => 'e2ef62b1',
'rsrc/css/phui/phui-two-column-view.css' => '44ec4951',
'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5',
'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455',
@ -871,7 +871,7 @@ return array(
'phui-status-list-view-css' => 'd5263e49',
'phui-tag-view-css' => 'b4719c50',
'phui-theme-css' => '9f261c6b',
'phui-timeline-view-css' => 'f21db7ca',
'phui-timeline-view-css' => 'e2ef62b1',
'phui-two-column-view-css' => '44ec4951',
'phui-workboard-color-css' => '783cdff5',
'phui-workboard-view-css' => '3bc85455',

View file

@ -594,6 +594,7 @@ phutil_register_library_map(array(
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php',
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php',
'DifferentialRevisionWrongStateTransaction' => 'applications/differential/xaction/DifferentialRevisionWrongStateTransaction.php',
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php',
@ -831,6 +832,7 @@ phutil_register_library_map(array(
'DiffusionQueryCommitsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php',
'DiffusionQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php',
'DiffusionQueryPathsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php',
'DiffusionQuickSearchEngineExtension' => 'applications/diffusion/engineextension/DiffusionQuickSearchEngineExtension.php',
'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php',
'DiffusionRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php',
'DiffusionReadmeView' => 'applications/diffusion/view/DiffusionReadmeView.php',
@ -2535,6 +2537,8 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php',
'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php',
'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php',
'PhabricatorCustomFieldHeraldAction' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldAction.php',
'PhabricatorCustomFieldHeraldActionGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldActionGroup.php',
'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php',
'PhabricatorCustomFieldHeraldFieldGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldFieldGroup.php',
'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php',
@ -3200,6 +3204,7 @@ phutil_register_library_map(array(
'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php',
'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php',
'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php',
'PhabricatorMonogramQuickSearchEngineExtension' => 'applications/typeahead/engineextension/PhabricatorMonogramQuickSearchEngineExtension.php',
'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php',
'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php',
'PhabricatorMotivatorProfileMenuItem' => 'applications/search/menuitem/PhabricatorMotivatorProfileMenuItem.php',
@ -3523,6 +3528,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleProfileTasksController' => 'applications/people/controller/PhabricatorPeopleProfileTasksController.php',
'PhabricatorPeopleProfileViewController' => 'applications/people/controller/PhabricatorPeopleProfileViewController.php',
'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php',
'PhabricatorPeopleQuickSearchEngineExtension' => 'applications/people/engineextension/PhabricatorPeopleQuickSearchEngineExtension.php',
'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php',
'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php',
'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php',
@ -3783,6 +3789,9 @@ phutil_register_library_map(array(
'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php',
'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php',
'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php',
'PhabricatorQuickSearchApplicationEngineExtension' => 'applications/meta/engineextension/PhabricatorQuickSearchApplicationEngineExtension.php',
'PhabricatorQuickSearchEngine' => 'applications/search/engine/PhabricatorQuickSearchEngine.php',
'PhabricatorQuickSearchEngineExtension' => 'applications/search/engineextension/PhabricatorQuickSearchEngineExtension.php',
'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php',
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
@ -4815,6 +4824,7 @@ phutil_register_library_map(array(
'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php',
'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php',
'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php',
'ProjectQuickSearchEngineExtension' => 'applications/project/engineextension/ProjectQuickSearchEngineExtension.php',
'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php',
'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php',
'ProjectReplyHandler' => 'applications/project/mail/ProjectReplyHandler.php',
@ -5638,6 +5648,7 @@ phutil_register_library_map(array(
'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
'DifferentialRevisionViewController' => 'DifferentialController',
'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionWrongStateTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialStoredCustomField' => 'DifferentialCustomField',
@ -5878,6 +5889,7 @@ phutil_register_library_map(array(
'DiffusionQueryCommitsConduitAPIMethod' => 'DiffusionConduitAPIMethod',
'DiffusionQueryConduitAPIMethod' => 'DiffusionConduitAPIMethod',
'DiffusionQueryPathsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'DiffusionRawDiffQuery' => 'DiffusionFileFutureQuery',
'DiffusionRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionReadmeView' => 'DiffusionView',
@ -7868,6 +7880,8 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldEditField' => 'PhabricatorEditField',
'PhabricatorCustomFieldEditType' => 'PhabricatorEditType',
'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
'PhabricatorCustomFieldHeraldAction' => 'HeraldAction',
'PhabricatorCustomFieldHeraldActionGroup' => 'HeraldActionGroup',
'PhabricatorCustomFieldHeraldField' => 'HeraldField',
'PhabricatorCustomFieldHeraldFieldGroup' => 'HeraldFieldGroup',
'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception',
@ -8608,6 +8622,7 @@ phutil_register_library_map(array(
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorModularTransactionType' => 'Phobject',
'PhabricatorMonogramQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting',
'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting',
'PhabricatorMotivatorProfileMenuItem' => 'PhabricatorProfileMenuItem',
@ -9000,6 +9015,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleProfileTasksController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileViewController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorPeopleQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController',
'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine',
@ -9315,6 +9331,9 @@ phutil_register_library_map(array(
'Phobject',
'Iterator',
),
'PhabricatorQuickSearchApplicationEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'PhabricatorQuickSearchEngine' => 'Phobject',
'PhabricatorQuickSearchEngineExtension' => 'Phobject',
'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorRedirectController' => 'PhabricatorController',
@ -10601,6 +10620,7 @@ phutil_register_library_map(array(
'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability',
'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod',
'ProjectQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase',
'ProjectReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',

View file

@ -9,9 +9,27 @@ final class PhabricatorAuthNeedsMultiFactorController
return false;
}
public function shouldRequireEnabledUser() {
// Users who haven't been approved yet are allowed to enroll in MFA. We'll
// kick disabled users out later.
return false;
}
public function shouldRequireEmailVerification() {
// Users who haven't verified their email addresses yet can still enroll
// in MFA.
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
if ($viewer->getIsDisabled()) {
// We allowed unapproved and disabled users to hit this controller, but
// want to kick out disabled users now.
return new Aphront400Response();
}
$panel = id(new PhabricatorMultiFactorSettingsPanel())
->setUser($viewer)
->setViewer($viewer)

View file

@ -137,10 +137,6 @@ abstract class PhabricatorController extends AphrontController {
}
if ($this->shouldRequireEnabledUser()) {
if ($user->isLoggedIn() && !$user->getIsApproved()) {
$controller = new PhabricatorAuthNeedsApprovalController();
return $this->delegateToController($controller);
}
if ($user->getIsDisabled()) {
$controller = new PhabricatorDisabledUserController();
return $this->delegateToController($controller);
@ -160,6 +156,15 @@ abstract class PhabricatorController extends AphrontController {
}
}
// Require users sign Legalpad documents before we check if they have
// MFA. If we don't do this, they can get stuck in a state where they
// can't add MFA until they sign, and can't sign until they add MFA.
// See T13024 and PHI223.
$result = $this->requireLegalpadSignatures();
if ($result !== null) {
return $result;
}
// Check if the user needs to configure MFA.
$need_mfa = $this->shouldRequireMultiFactorEnrollment();
$have_mfa = $user->getIsEnrolledInMultiFactor();
@ -224,46 +229,15 @@ abstract class PhabricatorController extends AphrontController {
->withPHIDs(array($application->getPHID()))
->executeOne();
}
}
if (!$this->shouldAllowLegallyNonCompliantUsers()) {
$legalpad_class = 'PhabricatorLegalpadApplication';
$legalpad = id(new PhabricatorApplicationQuery())
->setViewer($user)
->withClasses(array($legalpad_class))
->withInstalled(true)
->execute();
$legalpad = head($legalpad);
$doc_query = id(new LegalpadDocumentQuery())
->setViewer($user)
->withSignatureRequired(1)
->needViewerSignatures(true);
if ($user->hasSession() &&
!$user->getSession()->getIsPartial() &&
!$user->getSession()->getSignedLegalpadDocuments() &&
$user->isLoggedIn() &&
$legalpad) {
$sign_docs = $doc_query->execute();
$must_sign_docs = array();
foreach ($sign_docs as $sign_doc) {
if (!$sign_doc->getUserSignature($user->getPHID())) {
$must_sign_docs[] = $sign_doc;
}
}
if ($must_sign_docs) {
$controller = new LegalpadDocumentSignController();
$this->getRequest()->setURIMap(array(
'id' => head($must_sign_docs)->getID(),
));
$this->setCurrentApplication($legalpad);
// If users need approval, require they wait here. We do this near the
// end so they can take other actions (like verifying email, signing
// documents, and enrolling in MFA) while waiting for an admin to take a
// look at things. See T13024 for more discussion.
if ($this->shouldRequireEnabledUser()) {
if ($user->isLoggedIn() && !$user->getIsApproved()) {
$controller = new PhabricatorAuthNeedsApprovalController();
return $this->delegateToController($controller);
} else {
$engine = id(new PhabricatorAuthSessionEngine())
->signLegalpadDocuments($user, $sign_docs);
}
}
}
@ -558,6 +532,81 @@ abstract class PhabricatorController extends AphrontController {
return $this->buildApplicationCrumbs();
}
private function requireLegalpadSignatures() {
if (!$this->shouldRequireLogin()) {
return null;
}
if ($this->shouldAllowLegallyNonCompliantUsers()) {
return null;
}
$viewer = $this->getViewer();
if (!$viewer->hasSession()) {
return null;
}
$session = $viewer->getSession();
if ($session->getIsPartial()) {
// If the user hasn't made it through MFA yet, require they survive
// MFA first.
return null;
}
if ($session->getSignedLegalpadDocuments()) {
return null;
}
if (!$viewer->isLoggedIn()) {
return null;
}
$legalpad_class = 'PhabricatorLegalpadApplication';
$legalpad_installed = PhabricatorApplication::isClassInstalledForViewer(
$legalpad_class,
$viewer);
if (!$legalpad_installed) {
return null;
}
$sign_docs = id(new LegalpadDocumentQuery())
->setViewer($viewer)
->withSignatureRequired(1)
->needViewerSignatures(true)
->setOrder('oldest')
->execute();
$must_sign_docs = array();
foreach ($sign_docs as $sign_doc) {
if (!$sign_doc->getUserSignature($viewer->getPHID())) {
$must_sign_docs[] = $sign_doc;
}
}
if (!$must_sign_docs) {
// If nothing needs to be signed (either because there are no documents
// which require a signature, or because the user has already signed
// all of them) mark the session as good and continue.
$engine = id(new PhabricatorAuthSessionEngine())
->signLegalpadDocuments($viewer, $sign_docs);
return null;
}
$request = $this->getRequest();
$request->setURIMap(
array(
'id' => head($must_sign_docs)->getID(),
));
$application = PhabricatorApplication::getByClass($legalpad_class);
$this->setCurrentApplication($application);
$controller = new LegalpadDocumentSignController();
return $this->delegateToController($controller);
}
/* -( Deprecated )--------------------------------------------------------- */

View file

@ -159,10 +159,10 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
$u_unverified,
$u_admin,
$u_public,
$u_notapproved,
),
array(
$u_disabled,
$u_notapproved,
));
@ -224,7 +224,7 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
));
$this->checkAccess(
pht('Application Controller'),
pht('Application Controller, No Login Required'),
id(clone $app_controller)->setConfig('login', false),
$request,
array(
@ -232,10 +232,10 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
$u_unverified,
$u_admin,
$u_public,
$u_notapproved,
),
array(
$u_disabled,
$u_notapproved,
));
}

View file

@ -10,6 +10,7 @@ final class DifferentialTransactionEditor
private $hasReviewTransaction = false;
private $affectedPaths;
private $firstBroadcast = false;
private $wasDraft = false;
public function getEditorApplicationClass() {
return 'PhabricatorDifferentialApplication';
@ -166,6 +167,8 @@ final class DifferentialTransactionEditor
}
}
$this->wasDraft = $object->isDraft();
return parent::expandTransactions($object, $xactions);
}
@ -1581,10 +1584,6 @@ final class DifferentialTransactionEditor
$this->setActingAsPHID($author_phid);
}
// Mark this as the first broadcast we're sending about the revision
// so mail can generate specially.
$this->firstBroadcast = true;
$xaction = $object->getApplicationTransactionTemplate()
->setAuthorPHID($author_phid)
->setTransactionType(
@ -1612,12 +1611,31 @@ final class DifferentialTransactionEditor
$xactions[] = $xaction;
}
} else {
// If this revision is being created into some state other than "Draft",
// this is the first broadcast and should include sections like "SUMMARY"
// and "TEST PLAN".
if ($this->getIsNewObject()) {
}
// If the revision is new or was a draft, and is no longer a draft, we
// might be sending the first email about it.
// This might mean it was created directly into a non-draft state, or
// it just automatically undrafted after builds finished, or a user
// explicitly promoted it out of the draft state with an action like
// "Request Review".
// If we haven't sent any email about it yet, mark this email as the first
// email so the mail gets enriched with "SUMMARY" and "TEST PLAN".
$is_new = $this->getIsNewObject();
$was_draft = $this->wasDraft;
if (!$object->isDraft() && ($was_draft || $is_new)) {
if (!$object->getHasBroadcast()) {
// Mark this as the first broadcast we're sending about the revision
// so mail can generate specially.
$this->firstBroadcast = true;
$object
->setHasBroadcast(true)
->save();
}
}

View file

@ -268,6 +268,16 @@ final class DifferentialDiffExtractionEngine extends Phobject {
$xactions = array();
// If the revision isn't closed or "Accepted", write a warning into the
// transaction log. This makes it more clear when users bend the rules.
if (!$revision->isClosed() && !$revision->isAccepted()) {
$wrong_type = DifferentialRevisionWrongStateTransaction::TRANSACTIONTYPE;
$xactions[] = id(new DifferentialTransaction())
->setTransactionType($wrong_type)
->setNewValue($revision->getModernRevisionStatus());
}
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_UPDATE)
->setIgnoreOnNoEffect(true)

View file

@ -60,6 +60,7 @@ final class DifferentialRevision extends DifferentialDAO
const PROPERTY_CLOSED_FROM_ACCEPTED = 'wasAcceptedBeforeClose';
const PROPERTY_DRAFT_HOLD = 'draft.hold';
const PROPERTY_HAS_BROADCAST = 'draft.broadcast';
public static function initializeNewRevision(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
@ -719,6 +720,15 @@ final class DifferentialRevision extends DifferentialDAO
return $this->setProperty(self::PROPERTY_DRAFT_HOLD, $hold);
}
public function getHasBroadcast() {
return $this->getProperty(self::PROPERTY_HAS_BROADCAST, false);
}
public function setHasBroadcast($has_broadcast) {
return $this->setProperty(self::PROPERTY_HAS_BROADCAST, $has_broadcast);
}
public function loadActiveBuilds(PhabricatorUser $viewer) {
$diff = $this->getActiveDiff();

View file

@ -162,6 +162,11 @@ final class DifferentialRevisionAcceptTransaction
'closed. Only open revisions can be accepted.'));
}
if ($object->isDraft()) {
throw new Exception(
pht('You can not accept a draft revision.'));
}
$config_key = 'differential.allow-self-accept';
if (!PhabricatorEnv::getEnvConfig($config_key)) {
if ($this->isViewerRevisionAuthor($object, $viewer)) {

View file

@ -64,11 +64,6 @@ final class DifferentialRevisionResignTransaction
'been closed. You can only resign from open revisions.'));
}
if ($object->isDraft()) {
throw new Exception(
pht('You can not resign from a draft revision.'));
}
$resigned = DifferentialReviewerStatus::STATUS_RESIGNED;
if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) {
throw new Exception(

View file

@ -0,0 +1,41 @@
<?php
final class DifferentialRevisionWrongStateTransaction
extends DifferentialRevisionTransactionType {
const TRANSACTIONTYPE = 'differential.revision.wrong';
public function generateOldValue($object) {
return null;
}
public function generateNewValue($object, $value) {
return $value;
}
public function getIcon() {
return 'fa-exclamation';
}
public function getColor() {
return 'pink';
}
public function getActionStrength() {
return 4;
}
public function getTitle() {
$new_value = $this->getNewValue();
$status = DifferentialRevisionStatus::newForStatus($new_value);
return pht(
'This revision was not accepted when it landed; it landed in state %s.',
$this->renderValue($status->getDisplayName()));
}
public function getTitleForFeed() {
return null;
}
}

View file

@ -438,7 +438,8 @@ final class DiffusionCommitController extends DiffusionController {
$repository = $drequest->getRepository();
$view = id(new PHUIPropertyListView())
->setUser($this->getRequest()->getUser());
->setUser($this->getRequest()->getUser())
->setObject($commit);
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($commit_phid))

View file

@ -46,8 +46,20 @@ final class DiffusionRepositoryController extends DiffusionController {
->withRepositoryPHIDs(array($repository->getPHID()))
->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))
->withRefNames(array($drequest->getBranch()))
->needPositions(true)
->execute();
if ($ref_cursors) {
// It's possible that this branch previously existed, but has been
// deleted. Make sure we have valid cursor positions, not just cursors.
$any_positions = false;
foreach ($ref_cursors as $ref_cursor) {
if ($ref_cursor->getPositions()) {
$any_positions = true;
break;
}
}
if ($any_positions) {
// This is a valid branch, so we necessarily have some content.
$page_has_content = true;
} else {

View file

@ -347,11 +347,11 @@ final class DiffusionURIEditor
continue;
}
$io_type = $uri->getIoType();
$io_type = $uri->getEffectiveIOType();
if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) {
if ($no_readwrite) {
$readwite_conflict = $uri;
$readwrite_conflict = $uri;
break;
}
}

View file

@ -0,0 +1,12 @@
<?php
final class DiffusionQuickSearchEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new DiffusionRepositoryDatasource(),
new DiffusionSymbolDatasource(),
);
}
}

View file

@ -58,6 +58,14 @@ final class PhabricatorFilesComposeAvatarBuiltinFile
}
private function composeImage($color, $image, $border) {
// If we don't have the GD extension installed, just return a static
// default profile image rather than trying to compose a dynamic one.
if (!function_exists('imagecreatefromstring')) {
$root = dirname(phutil_get_library_root('phabricator'));
$default_path = $root.'/resources/builtin/profile.png';
return Filesystem::readFile($default_path);
}
$color_const = hexdec(trim($color, '#'));
$true_border = self::rgba2gd($border);
$image_map = self::getImageMap();

View file

@ -10,6 +10,10 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
return false;
}
public function shouldAllowPartialSessions() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$this->phid = $request->getURIData('phid');

View file

@ -77,23 +77,12 @@ final class LegalpadDocumentQuery
return $this;
}
public function newResultObject() {
return new LegalpadDocument();
}
protected function loadPage() {
$table = new LegalpadDocument();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT d.* FROM %T d %Q %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildGroupClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$documents = $table->loadAllFromArray($data);
return $documents;
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $documents) {
@ -134,12 +123,12 @@ final class LegalpadDocumentQuery
return $documents;
}
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
$joins = array();
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->contributorPHIDs !== null) {
$joins[] = qsprintf(
$conn_r,
$conn,
'JOIN edge contributor ON contributor.src = d.phid
AND contributor.type = %d',
PhabricatorObjectHasContributorEdgeType::EDGECONST);
@ -147,79 +136,81 @@ final class LegalpadDocumentQuery
if ($this->signerPHIDs !== null) {
$joins[] = qsprintf(
$conn_r,
$conn,
'JOIN %T signer ON signer.documentPHID = d.phid
AND signer.signerPHID IN (%Ls)',
id(new LegalpadDocumentSignature())->getTableName(),
$this->signerPHIDs);
}
return implode(' ', $joins);
return $joins;
}
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
if ($this->contributorPHIDs || $this->signerPHIDs) {
return 'GROUP BY d.id';
} else {
return '';
protected function shouldGroupQueryResultRows() {
if ($this->contributorPHIDs) {
return true;
}
if ($this->signerPHIDs) {
return true;
}
return parent::shouldGroupQueryResultRows();
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'd.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'd.phid IN (%Ls)',
$this->phids);
}
if ($this->creatorPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'd.creatorPHID IN (%Ls)',
$this->creatorPHIDs);
}
if ($this->dateCreatedAfter !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'd.dateCreated >= %d',
$this->dateCreatedAfter);
}
if ($this->dateCreatedBefore !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'd.dateCreated <= %d',
$this->dateCreatedBefore);
}
if ($this->contributorPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'contributor.dst IN (%Ls)',
$this->contributorPHIDs);
}
if ($this->signatureRequired !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'd.requireSignature = %d',
$this->signatureRequired);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
return $where;
}
private function loadDocumentBodies(array $documents) {
@ -275,4 +266,8 @@ final class LegalpadDocumentQuery
return 'PhabricatorLegalpadApplication';
}
protected function getPrimaryTableAlias() {
return 'd';
}
}

View file

@ -11,105 +11,66 @@ final class LegalpadDocumentSearchEngine
return 'PhabricatorLegalpadApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'creatorPHIDs',
$this->readUsersFromRequest($request, 'creators'));
$saved->setParameter(
'contributorPHIDs',
$this->readUsersFromRequest($request, 'contributors'));
$saved->setParameter(
'withViewerSignature',
$request->getBool('withViewerSignature'));
$saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
return $saved;
public function newQuery() {
return id(new LegalpadDocumentQuery())
->needViewerSignatures(true);
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new LegalpadDocumentQuery())
->needViewerSignatures(true);
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorUsersSearchField())
->setLabel(pht('Signed By'))
->setKey('signerPHIDs')
->setAliases(array('signer', 'signers', 'signerPHID'))
->setDescription(
pht('Search for documents signed by given users.')),
id(new PhabricatorUsersSearchField())
->setLabel(pht('Creators'))
->setKey('creatorPHIDs')
->setAliases(array('creator', 'creators', 'creatorPHID'))
->setDescription(
pht('Search for documents with given creators.')),
id(new PhabricatorUsersSearchField())
->setLabel(pht('Contributors'))
->setKey('contributorPHIDs')
->setAliases(array('contributor', 'contributors', 'contributorPHID'))
->setDescription(
pht('Search for documents with given contributors.')),
id(new PhabricatorSearchDateField())
->setLabel(pht('Created After'))
->setKey('createdStart'),
id(new PhabricatorSearchDateField())
->setLabel(pht('Created Before'))
->setKey('createdEnd'),
);
}
$creator_phids = $saved->getParameter('creatorPHIDs', array());
if ($creator_phids) {
$query->withCreatorPHIDs($creator_phids);
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['signerPHIDs']) {
$query->withSignerPHIDs($map['signerPHIDs']);
}
$contributor_phids = $saved->getParameter('contributorPHIDs', array());
if ($contributor_phids) {
$query->withContributorPHIDs($contributor_phids);
if ($map['contributorPHIDs']) {
$query->withContributorPHIDs($map['creatorPHIDs']);
}
if ($saved->getParameter('withViewerSignature')) {
$viewer_phid = $this->requireViewer()->getPHID();
if ($viewer_phid) {
$query->withSignerPHIDs(array($viewer_phid));
}
if ($map['creatorPHIDs']) {
$query->withCreatorPHIDs($map['creatorPHIDs']);
}
$start = $this->parseDateTime($saved->getParameter('createdStart'));
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
if ($start) {
$query->withDateCreatedAfter($start);
if ($map['createdStart']) {
$query->withDateCreatedAfter($map['createdStart']);
}
if ($end) {
$query->withDateCreatedBefore($end);
if ($map['createdEnd']) {
$query->withDateCreatedAfter($map['createdStart']);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$creator_phids = $saved_query->getParameter('creatorPHIDs', array());
$contributor_phids = $saved_query->getParameter(
'contributorPHIDs', array());
$viewer_signature = $saved_query->getParameter('withViewerSignature');
if (!$this->requireViewer()->getPHID()) {
$viewer_signature = false;
}
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'withViewerSignature',
1,
pht('Show only documents I have signed.'),
$viewer_signature)
->setDisabled(!$this->requireViewer()->getPHID()))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('creators')
->setLabel(pht('Creators'))
->setValue($creator_phids))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('contributors')
->setLabel(pht('Contributors'))
->setValue($contributor_phids));
$this->buildDateRange(
$form,
$saved_query,
'createdStart',
pht('Created After'),
'createdEnd',
pht('Created Before'));
}
protected function getURI($path) {
return '/legalpad/'.$path;
}
@ -130,10 +91,11 @@ final class LegalpadDocumentSearchEngine
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
$viewer = $this->requireViewer();
switch ($query_key) {
case 'signed':
return $query
->setParameter('withViewerSignature', true);
return $query->setParameter('signerPHIDs', array($viewer->getPHID()));
case 'all':
return $query;
}
@ -141,12 +103,6 @@ final class LegalpadDocumentSearchEngine
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $documents,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList(
array $documents,
PhabricatorSavedQuery $query,

View file

@ -0,0 +1,11 @@
<?php
final class PhabricatorQuickSearchApplicationEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new PhabricatorApplicationDatasource(),
);
}
}

View file

@ -9,6 +9,10 @@ final class PeopleMainMenuBarExtension
return $viewer->isLoggedIn();
}
public function shouldAllowPartialSessions() {
return true;
}
public function getExtensionOrder() {
return 1200;
}
@ -65,42 +69,44 @@ final class PeopleMainMenuBarExtension
$view = id(new PhabricatorActionListView())
->setViewer($viewer);
$view->addAction(
id(new PhabricatorActionView())
->appendChild($user_view));
if ($this->getIsFullSession()) {
$view->addAction(
id(new PhabricatorActionView())
->appendChild($user_view));
$view->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER));
$view->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Profile'))
->setHref('/p/'.$viewer->getUsername().'/'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Profile'))
->setHref('/p/'.$viewer->getUsername().'/'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Settings'))
->setHref('/settings/user/'.$viewer->getUsername().'/'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Settings'))
->setHref('/settings/user/'.$viewer->getUsername().'/'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Manage'))
->setHref('/people/manage/'.$viewer->getID().'/'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Manage'))
->setHref('/people/manage/'.$viewer->getID().'/'));
if ($application) {
$help_links = $application->getHelpMenuItems($viewer);
if ($help_links) {
foreach ($help_links as $link) {
$view->addAction($link);
if ($application) {
$help_links = $application->getHelpMenuItems($viewer);
if ($help_links) {
foreach ($help_links as $link) {
$view->addAction($link);
}
}
}
}
$view->addAction(
id(new PhabricatorActionView())
->addSigil('logout-item')
->setType(PhabricatorActionView::TYPE_DIVIDER));
$view->addAction(
id(new PhabricatorActionView())
->addSigil('logout-item')
->setType(PhabricatorActionView::TYPE_DIVIDER));
}
$view->addAction(
id(new PhabricatorActionView())

View file

@ -0,0 +1,11 @@
<?php
final class PhabricatorPeopleQuickSearchEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new PhabricatorPeopleDatasource(),
);
}
}

View file

@ -67,7 +67,9 @@ final class PhabricatorBoardRenderingEngine extends Phobject {
$project_phids = $object->getProjectPHIDs();
$project_handles = array_select_keys($this->handles, $project_phids);
if ($project_handles) {
$card->setProjectHandles($project_handles);
$card
->setHideArchivedProjects(true)
->setProjectHandles($project_handles);
}
$cover_phid = $object->getCoverImageThumbnailPHID();

View file

@ -0,0 +1,11 @@
<?php
final class ProjectQuickSearchEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new PhabricatorProjectDatasource(),
);
}
}

View file

@ -8,8 +8,16 @@ final class PhabricatorProjectUIEventListener
}
public function handleEvent(PhutilEvent $event) {
$object = $event->getValue('object');
switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES:
// Hacky solution so that property list view on Diffusion
// commits shows build status, but not Projects, Subscriptions,
// or Tokens.
if ($object instanceof PhabricatorRepositoryCommit) {
return;
}
$this->handlePropertyEvent($event);
break;
}

View file

@ -8,6 +8,7 @@ final class ProjectBoardTaskCard extends Phobject {
private $owner;
private $canEdit;
private $coverImageFile;
private $hideArchivedProjects;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
@ -35,6 +36,15 @@ final class ProjectBoardTaskCard extends Phobject {
return $this->coverImageFile;
}
public function setHideArchivedProjects($hide_archived_projects) {
$this->hideArchivedProjects = $hide_archived_projects;
return $this;
}
public function getHideArchivedProjects() {
return $this->hideArchivedProjects;
}
public function setTask(ManiphestTask $task) {
$this->task = $task;
return $this;
@ -126,10 +136,12 @@ final class ProjectBoardTaskCard extends Phobject {
$project_handles = $this->getProjectHandles();
// Remove any archived projects from the list.
if ($project_handles) {
foreach ($project_handles as $key => $handle) {
if ($handle->getStatus() == PhabricatorObjectHandle::STATUS_CLOSED) {
unset($project_handles[$key]);
if ($this->hideArchivedProjects) {
if ($project_handles) {
foreach ($project_handles as $key => $handle) {
if ($handle->getStatus() == PhabricatorObjectHandle::STATUS_CLOSED) {
unset($project_handles[$key]);
}
}
}
}

View file

@ -0,0 +1,8 @@
<?php
final class PhabricatorQuickSearchEngine extends Phobject {
public function getAllDatasources() {
return PhabricatorQuickSearchEngineExtension::getAllDatasources();
}
}

View file

@ -0,0 +1,18 @@
<?php
abstract class PhabricatorQuickSearchEngineExtension extends Phobject {
abstract public function newQuickSearchDatasources();
final public static function getAllDatasources() {
$extensions = id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->execute();
$datasources = array();
foreach ($extensions as $extension) {
$datasources[] = $extension->newQuickSearchDatasources();
}
return array_mergev($datasources);
}
}

View file

@ -16,14 +16,8 @@ final class PhabricatorSearchDatasource
}
public function getComponentDatasources() {
$sources = array(
new PhabricatorPeopleDatasource(),
new PhabricatorProjectDatasource(),
new PhabricatorApplicationDatasource(),
new PhabricatorTypeaheadMonogramDatasource(),
new DiffusionRepositoryDatasource(),
new DiffusionSymbolDatasource(),
);
$sources = id(new PhabricatorQuickSearchEngine())
->getAllDatasources();
// These results are always rendered in the full browse display mode, so
// set the browse flag on all component sources.

View file

@ -9,11 +9,19 @@ final class PhabricatorSubscriptionsUIEventListener
}
public function handleEvent(PhutilEvent $event) {
$object = $event->getValue('object');
switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
$this->handleActionEvent($event);
break;
case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES:
// Hacky solution so that property list view on Diffusion
// commits shows build status, but not Projects, Subscriptions,
// or Tokens.
if ($object instanceof PhabricatorRepositoryCommit) {
return;
}
$this->handlePropertyEvent($event);
break;
}

View file

@ -9,11 +9,19 @@ final class PhabricatorTokenUIEventListener
}
public function handleEvent(PhutilEvent $event) {
$object = $event->getValue('object');
switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
$this->handleActionEvent($event);
break;
case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES:
// Hacky solution so that property list view on Diffusion
// commits shows build status, but not Projects, Subscriptions,
// or Tokens.
if ($object instanceof PhabricatorRepositoryCommit) {
return;
}
$this->handlePropertyEvent($event);
break;
}

View file

@ -0,0 +1,11 @@
<?php
final class PhabricatorMonogramQuickSearchEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new PhabricatorTypeaheadMonogramDatasource(),
);
}
}

View file

@ -239,6 +239,11 @@ You can not add a URI in Observe mode if an existing builtin URI is in
authorities: the observed remote copy and the hosted local copy. Take the
other URI out of //Read/Write// mode first.
WARNING: If you observe a remote repository, the entire state of the working
copy that Phabricator maintains will be deleted and replaced with the state of
the remote. If some changes are present only in Phabricator's working copy,
they will be unrecoverably destroyed.
**Mirror**: Phabricator will push any changes made to this repository to the
remote URI, keeping a read-only mirror hosted at that URI up to date.
@ -251,6 +256,11 @@ It is possible to mirror a repository to another repository that is also
hosted by Phabricator by adding that other repository's URI, although this is
silly and probably very rarely of any use.
WARNING: If you mirror to a remote repository, the entire state of that remote
will be replaced with the state of the working copy Phabricator maintains. If
some changes are present only in the remote, they will be unrecoverably
destroyed.
**None**: Phabricator will not fetch changes from or push changes to this URI.
For builtin URIs, it will not let users fetch changes from or push changes to
this URI.

View file

@ -34,6 +34,7 @@ abstract class PhabricatorCustomField extends Phobject {
const ROLE_CONDUIT = 'conduit';
const ROLE_HERALD = 'herald';
const ROLE_EDITENGINE = 'EditEngine';
const ROLE_HERALDACTION = 'herald.action';
/* -( Building Applications with Custom Fields )--------------------------- */
@ -293,6 +294,8 @@ abstract class PhabricatorCustomField extends Phobject {
return $this->shouldAppearInTransactionMail();
case self::ROLE_HERALD:
return $this->shouldAppearInHerald();
case self::ROLE_HERALDACTION:
return $this->shouldAppearInHeraldActions();
case self::ROLE_EDITENGINE:
return $this->shouldAppearInEditView() ||
$this->shouldAppearInEditEngine();
@ -1476,4 +1479,56 @@ abstract class PhabricatorCustomField extends Phobject {
}
public function shouldAppearInHeraldActions() {
if ($this->proxy) {
return $this->proxy->shouldAppearInHeraldActions();
}
return false;
}
public function getHeraldActionName() {
if ($this->proxy) {
return $this->proxy->getHeraldActionName();
}
return null;
}
public function getHeraldActionStandardType() {
if ($this->proxy) {
return $this->proxy->getHeraldActionStandardType();
}
return null;
}
public function getHeraldActionDescription($value) {
if ($this->proxy) {
return $this->proxy->getHeraldActionDescription($value);
}
return null;
}
public function getHeraldActionEffectDescription($value) {
if ($this->proxy) {
return $this->proxy->getHeraldActionEffectDescription($value);
}
return null;
}
public function getHeraldActionDatasource() {
if ($this->proxy) {
return $this->proxy->getHeraldActionDatasource();
}
return null;
}
}

View file

@ -0,0 +1,105 @@
<?php
final class PhabricatorCustomFieldHeraldAction extends HeraldAction {
const ACTIONCONST = 'herald.action.custom';
const DO_SET_FIELD = 'do.set-custom-field';
private $customField;
public function setCustomField(PhabricatorCustomField $custom_field) {
$this->customField = $custom_field;
return $this;
}
public function getCustomField() {
return $this->customField;
}
public function getActionGroupKey() {
return PhabricatorCustomFieldHeraldActionGroup::ACTIONGROUPKEY;
}
public function supportsObject($object) {
return ($object instanceof PhabricatorCustomFieldInterface);
}
public function supportsRuleType($rule_type) {
return true;
}
public function getActionsForObject($object) {
$viewer = PhabricatorUser::getOmnipotentUser();
$role = PhabricatorCustomField::ROLE_HERALDACTION;
$field_list = PhabricatorCustomField::getObjectFields($object, $role)
->setViewer($viewer)
->readFieldsFromStorage($object);
$map = array();
foreach ($field_list->getFields() as $field) {
$key = $field->getFieldKey();
$map[$key] = id(new self())
->setCustomField($field);
}
return $map;
}
public function applyEffect($object, HeraldEffect $effect) {
$field = $this->getCustomField();
$value = $effect->getTarget();
$adapter = $this->getAdapter();
$old_value = $field->getOldValueForApplicationTransactions();
$new_value = id(clone $field)
->setValueFromApplicationTransactions($value)
->getValueForStorage();
$xaction = $adapter->newTransaction()
->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD)
->setMetadataValue('customfield:key', $field->getFieldKey())
->setOldValue($old_value)
->setNewValue($new_value);
$adapter->queueTransaction($xaction);
$this->logEffect(self::DO_SET_FIELD, $value);
}
public function getHeraldActionName() {
return $this->getCustomField()->getHeraldActionName();
}
public function getHeraldActionStandardType() {
return $this->getCustomField()->getHeraldActionStandardType();
}
protected function getDatasource() {
return $this->getCustomField()->getHeraldActionDatasource();
}
public function renderActionDescription($value) {
return $this->getCustomField()->getHeraldActionDescription($value);
}
protected function getActionEffectMap() {
return array(
self::DO_SET_FIELD => array(
'icon' => 'fa-pencil',
'color' => 'green',
'name' => pht('Set Field Value'),
),
);
}
protected function renderActionEffectDescription($type, $data) {
switch ($type) {
case self::DO_SET_FIELD:
return $this->getCustomField()->getHeraldActionEffectDescription($data);
}
}
}

View file

@ -0,0 +1,16 @@
<?php
final class PhabricatorCustomFieldHeraldActionGroup
extends HeraldActionGroup {
const ACTIONGROUPKEY = 'customfield';
public function getGroupLabel() {
return pht('Custom Fields');
}
protected function getGroupOrder() {
return 2000;
}
}

View file

@ -65,4 +65,40 @@ abstract class PhabricatorStandardCustomFieldTokenizer
return new ConduitPHIDListParameterType();
}
public function shouldAppearInHeraldActions() {
return true;
}
public function getHeraldActionName() {
return pht('Set "%s" to', $this->getFieldName());
}
public function getHeraldActionDescription($value) {
$list = $this->renderHeraldHandleList($value);
return pht('Set "%s" to: %s.', $this->getFieldName(), $list);
}
public function getHeraldActionEffectDescription($value) {
return $this->renderHeraldHandleList($value);
}
public function getHeraldActionStandardType() {
return HeraldAction::STANDARD_PHID_LIST;
}
public function getHeraldActionDatasource() {
return $this->getDatasource();
}
private function renderHeraldHandleList($value) {
if (!is_array($value)) {
return pht('(Invalid List)');
} else {
return $this->getViewer()
->renderHandleList($value)
->setAsInline(true)
->render();
}
}
}

View file

@ -5,6 +5,7 @@ abstract class PhabricatorMainMenuBarExtension extends Phobject {
private $viewer;
private $application;
private $controller;
private $isFullSession;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
@ -33,6 +34,15 @@ abstract class PhabricatorMainMenuBarExtension extends Phobject {
return $this->controller;
}
public function setIsFullSession($is_full_session) {
$this->isFullSession = $is_full_session;
return $this;
}
public function getIsFullSession() {
return $this->isFullSession;
}
final public function getExtensionKey() {
return $this->getPhobjectClassConstant('MAINMENUBARKEY');
}
@ -41,6 +51,10 @@ abstract class PhabricatorMainMenuBarExtension extends Phobject {
return true;
}
public function shouldAllowPartialSessions() {
return false;
}
public function isExtensionEnabledForViewer(PhabricatorUser $viewer) {
if (!$viewer->isLoggedIn()) {
return false;

View file

@ -46,7 +46,9 @@ final class PhabricatorMainMenuView extends AphrontView {
$app_button = '';
$aural = null;
if ($viewer->isLoggedIn() && $viewer->isUserActivated()) {
$is_full = $this->isFullSession($viewer);
if ($is_full) {
list($menu, $dropdowns, $aural) = $this->renderNotificationMenu();
if (array_filter($menu)) {
$alerts[] = $menu;
@ -54,14 +56,18 @@ final class PhabricatorMainMenuView extends AphrontView {
$menu_bar = array_merge($menu_bar, $dropdowns);
$app_button = $this->renderApplicationMenuButton();
$search_button = $this->renderSearchMenuButton($header_id);
} else {
} else if (!$viewer->isLoggedIn()) {
$app_button = $this->renderApplicationMenuButton();
if (PhabricatorEnv::getEnvConfig('policy.allow-public')) {
$search_button = $this->renderSearchMenuButton($header_id);
}
}
$search_menu = $this->renderPhabricatorSearchMenu();
if ($search_button) {
$search_menu = $this->renderPhabricatorSearchMenu();
} else {
$search_menu = null;
}
if ($alerts) {
$alerts = javelin_tag(
@ -84,7 +90,9 @@ final class PhabricatorMainMenuView extends AphrontView {
$extensions = PhabricatorMainMenuBarExtension::getAllEnabledExtensions();
foreach ($extensions as $extension) {
$extension->setViewer($viewer);
$extension
->setViewer($viewer)
->setIsFullSession($is_full);
$controller = $this->getController();
if ($controller) {
@ -96,6 +104,14 @@ final class PhabricatorMainMenuView extends AphrontView {
}
}
if (!$is_full) {
foreach ($extensions as $key => $extension) {
if (!$extension->shouldAllowPartialSessions()) {
unset($extensions[$key]);
}
}
}
foreach ($extensions as $key => $extension) {
if (!$extension->isExtensionEnabledForViewer($extension->getViewer())) {
unset($extensions[$key]);
@ -677,4 +693,38 @@ final class PhabricatorMainMenuView extends AphrontView {
);
}
private function isFullSession(PhabricatorUser $viewer) {
if (!$viewer->isLoggedIn()) {
return false;
}
if (!$viewer->isUserActivated()) {
return false;
}
if (!$viewer->hasSession()) {
return false;
}
$session = $viewer->getSession();
if ($session->getIsPartial()) {
return false;
}
if (!$session->getSignedLegalpadDocuments()) {
return false;
}
$mfa_key = 'security.require-multi-factor-auth';
$need_mfa = PhabricatorEnv::getEnvConfig($mfa_key);
if ($need_mfa) {
$have_mfa = $viewer->getIsEnrolledInMultiFactor();
if (!$have_mfa) {
return false;
}
}
return true;
}
}

View file

@ -315,6 +315,10 @@
background-color: {$violet};
}
.phui-timeline-icon-fill-pink {
background-color: {$pink};
}
.phui-timeline-icon-fill-grey {
background-color: #888;
}