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

View file

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

View file

@ -9,9 +9,27 @@ final class PhabricatorAuthNeedsMultiFactorController
return false; 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) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); $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()) $panel = id(new PhabricatorMultiFactorSettingsPanel())
->setUser($viewer) ->setUser($viewer)
->setViewer($viewer) ->setViewer($viewer)

View file

@ -137,10 +137,6 @@ abstract class PhabricatorController extends AphrontController {
} }
if ($this->shouldRequireEnabledUser()) { if ($this->shouldRequireEnabledUser()) {
if ($user->isLoggedIn() && !$user->getIsApproved()) {
$controller = new PhabricatorAuthNeedsApprovalController();
return $this->delegateToController($controller);
}
if ($user->getIsDisabled()) { if ($user->getIsDisabled()) {
$controller = new PhabricatorDisabledUserController(); $controller = new PhabricatorDisabledUserController();
return $this->delegateToController($controller); 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. // Check if the user needs to configure MFA.
$need_mfa = $this->shouldRequireMultiFactorEnrollment(); $need_mfa = $this->shouldRequireMultiFactorEnrollment();
$have_mfa = $user->getIsEnrolledInMultiFactor(); $have_mfa = $user->getIsEnrolledInMultiFactor();
@ -224,46 +229,15 @@ abstract class PhabricatorController extends AphrontController {
->withPHIDs(array($application->getPHID())) ->withPHIDs(array($application->getPHID()))
->executeOne(); ->executeOne();
} }
}
// If users need approval, require they wait here. We do this near the
if (!$this->shouldAllowLegallyNonCompliantUsers()) { // end so they can take other actions (like verifying email, signing
$legalpad_class = 'PhabricatorLegalpadApplication'; // documents, and enrolling in MFA) while waiting for an admin to take a
$legalpad = id(new PhabricatorApplicationQuery()) // look at things. See T13024 for more discussion.
->setViewer($user) if ($this->shouldRequireEnabledUser()) {
->withClasses(array($legalpad_class)) if ($user->isLoggedIn() && !$user->getIsApproved()) {
->withInstalled(true) $controller = new PhabricatorAuthNeedsApprovalController();
->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);
return $this->delegateToController($controller); 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(); 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 )--------------------------------------------------------- */ /* -( Deprecated )--------------------------------------------------------- */

View file

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

View file

@ -10,6 +10,7 @@ final class DifferentialTransactionEditor
private $hasReviewTransaction = false; private $hasReviewTransaction = false;
private $affectedPaths; private $affectedPaths;
private $firstBroadcast = false; private $firstBroadcast = false;
private $wasDraft = false;
public function getEditorApplicationClass() { public function getEditorApplicationClass() {
return 'PhabricatorDifferentialApplication'; return 'PhabricatorDifferentialApplication';
@ -166,6 +167,8 @@ final class DifferentialTransactionEditor
} }
} }
$this->wasDraft = $object->isDraft();
return parent::expandTransactions($object, $xactions); return parent::expandTransactions($object, $xactions);
} }
@ -1581,10 +1584,6 @@ final class DifferentialTransactionEditor
$this->setActingAsPHID($author_phid); $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() $xaction = $object->getApplicationTransactionTemplate()
->setAuthorPHID($author_phid) ->setAuthorPHID($author_phid)
->setTransactionType( ->setTransactionType(
@ -1612,12 +1611,31 @@ final class DifferentialTransactionEditor
$xactions[] = $xaction; $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" // If the revision is new or was a draft, and is no longer a draft, we
// and "TEST PLAN". // might be sending the first email about it.
if ($this->getIsNewObject()) {
// 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; $this->firstBroadcast = true;
$object
->setHasBroadcast(true)
->save();
} }
} }

View file

@ -268,6 +268,16 @@ final class DifferentialDiffExtractionEngine extends Phobject {
$xactions = array(); $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()) $xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_UPDATE) ->setTransactionType(DifferentialTransaction::TYPE_UPDATE)
->setIgnoreOnNoEffect(true) ->setIgnoreOnNoEffect(true)

View file

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

View file

@ -162,6 +162,11 @@ final class DifferentialRevisionAcceptTransaction
'closed. Only open revisions can be accepted.')); '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'; $config_key = 'differential.allow-self-accept';
if (!PhabricatorEnv::getEnvConfig($config_key)) { if (!PhabricatorEnv::getEnvConfig($config_key)) {
if ($this->isViewerRevisionAuthor($object, $viewer)) { if ($this->isViewerRevisionAuthor($object, $viewer)) {

View file

@ -64,11 +64,6 @@ final class DifferentialRevisionResignTransaction
'been closed. You can only resign from open revisions.')); '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; $resigned = DifferentialReviewerStatus::STATUS_RESIGNED;
if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) { if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) {
throw new Exception( 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(); $repository = $drequest->getRepository();
$view = id(new PHUIPropertyListView()) $view = id(new PHUIPropertyListView())
->setUser($this->getRequest()->getUser()); ->setUser($this->getRequest()->getUser())
->setObject($commit);
$edge_query = id(new PhabricatorEdgeQuery()) $edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($commit_phid)) ->withSourcePHIDs(array($commit_phid))

View file

@ -46,8 +46,20 @@ final class DiffusionRepositoryController extends DiffusionController {
->withRepositoryPHIDs(array($repository->getPHID())) ->withRepositoryPHIDs(array($repository->getPHID()))
->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH)) ->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))
->withRefNames(array($drequest->getBranch())) ->withRefNames(array($drequest->getBranch()))
->needPositions(true)
->execute(); ->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. // This is a valid branch, so we necessarily have some content.
$page_has_content = true; $page_has_content = true;
} else { } else {

View file

@ -347,11 +347,11 @@ final class DiffusionURIEditor
continue; continue;
} }
$io_type = $uri->getIoType(); $io_type = $uri->getEffectiveIOType();
if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) { if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) {
if ($no_readwrite) { if ($no_readwrite) {
$readwite_conflict = $uri; $readwrite_conflict = $uri;
break; 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) { 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, '#')); $color_const = hexdec(trim($color, '#'));
$true_border = self::rgba2gd($border); $true_border = self::rgba2gd($border);
$image_map = self::getImageMap(); $image_map = self::getImageMap();

View file

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

View file

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

View file

@ -11,105 +11,66 @@ final class LegalpadDocumentSearchEngine
return 'PhabricatorLegalpadApplication'; return 'PhabricatorLegalpadApplication';
} }
public function buildSavedQueryFromRequest(AphrontRequest $request) { public function newQuery() {
$saved = new PhabricatorSavedQuery(); return id(new LegalpadDocumentQuery())
$saved->setParameter( ->needViewerSignatures(true);
'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 buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { protected function buildCustomSearchFields() {
$query = id(new LegalpadDocumentQuery()) return array(
->needViewerSignatures(true); 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()); protected function buildQueryFromParameters(array $map) {
if ($creator_phids) { $query = $this->newQuery();
$query->withCreatorPHIDs($creator_phids);
if ($map['signerPHIDs']) {
$query->withSignerPHIDs($map['signerPHIDs']);
} }
$contributor_phids = $saved->getParameter('contributorPHIDs', array()); if ($map['contributorPHIDs']) {
if ($contributor_phids) { $query->withContributorPHIDs($map['creatorPHIDs']);
$query->withContributorPHIDs($contributor_phids);
} }
if ($saved->getParameter('withViewerSignature')) { if ($map['creatorPHIDs']) {
$viewer_phid = $this->requireViewer()->getPHID(); $query->withCreatorPHIDs($map['creatorPHIDs']);
if ($viewer_phid) {
$query->withSignerPHIDs(array($viewer_phid));
}
} }
$start = $this->parseDateTime($saved->getParameter('createdStart')); if ($map['createdStart']) {
$end = $this->parseDateTime($saved->getParameter('createdEnd')); $query->withDateCreatedAfter($map['createdStart']);
if ($start) {
$query->withDateCreatedAfter($start);
} }
if ($end) { if ($map['createdEnd']) {
$query->withDateCreatedBefore($end); $query->withDateCreatedAfter($map['createdStart']);
} }
return $query; 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) { protected function getURI($path) {
return '/legalpad/'.$path; return '/legalpad/'.$path;
} }
@ -130,10 +91,11 @@ final class LegalpadDocumentSearchEngine
$query = $this->newSavedQuery(); $query = $this->newSavedQuery();
$query->setQueryKey($query_key); $query->setQueryKey($query_key);
$viewer = $this->requireViewer();
switch ($query_key) { switch ($query_key) {
case 'signed': case 'signed':
return $query return $query->setParameter('signerPHIDs', array($viewer->getPHID()));
->setParameter('withViewerSignature', true);
case 'all': case 'all':
return $query; return $query;
} }
@ -141,12 +103,6 @@ final class LegalpadDocumentSearchEngine
return parent::buildSavedQueryFromBuiltin($query_key); return parent::buildSavedQueryFromBuiltin($query_key);
} }
protected function getRequiredHandlePHIDsForResultList(
array $documents,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList( protected function renderResultList(
array $documents, array $documents,
PhabricatorSavedQuery $query, 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(); return $viewer->isLoggedIn();
} }
public function shouldAllowPartialSessions() {
return true;
}
public function getExtensionOrder() { public function getExtensionOrder() {
return 1200; return 1200;
} }
@ -65,42 +69,44 @@ final class PeopleMainMenuBarExtension
$view = id(new PhabricatorActionListView()) $view = id(new PhabricatorActionListView())
->setViewer($viewer); ->setViewer($viewer);
$view->addAction( if ($this->getIsFullSession()) {
id(new PhabricatorActionView()) $view->addAction(
->appendChild($user_view)); id(new PhabricatorActionView())
->appendChild($user_view));
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER)); ->setType(PhabricatorActionView::TYPE_DIVIDER));
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->setName(pht('Profile')) ->setName(pht('Profile'))
->setHref('/p/'.$viewer->getUsername().'/')); ->setHref('/p/'.$viewer->getUsername().'/'));
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->setName(pht('Settings')) ->setName(pht('Settings'))
->setHref('/settings/user/'.$viewer->getUsername().'/')); ->setHref('/settings/user/'.$viewer->getUsername().'/'));
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->setName(pht('Manage')) ->setName(pht('Manage'))
->setHref('/people/manage/'.$viewer->getID().'/')); ->setHref('/people/manage/'.$viewer->getID().'/'));
if ($application) { if ($application) {
$help_links = $application->getHelpMenuItems($viewer); $help_links = $application->getHelpMenuItems($viewer);
if ($help_links) { if ($help_links) {
foreach ($help_links as $link) { foreach ($help_links as $link) {
$view->addAction($link); $view->addAction($link);
}
} }
} }
}
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->addSigil('logout-item') ->addSigil('logout-item')
->setType(PhabricatorActionView::TYPE_DIVIDER)); ->setType(PhabricatorActionView::TYPE_DIVIDER));
}
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) 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_phids = $object->getProjectPHIDs();
$project_handles = array_select_keys($this->handles, $project_phids); $project_handles = array_select_keys($this->handles, $project_phids);
if ($project_handles) { if ($project_handles) {
$card->setProjectHandles($project_handles); $card
->setHideArchivedProjects(true)
->setProjectHandles($project_handles);
} }
$cover_phid = $object->getCoverImageThumbnailPHID(); $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) { public function handleEvent(PhutilEvent $event) {
$object = $event->getValue('object');
switch ($event->getType()) { switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES: 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); $this->handlePropertyEvent($event);
break; break;
} }

View file

@ -8,6 +8,7 @@ final class ProjectBoardTaskCard extends Phobject {
private $owner; private $owner;
private $canEdit; private $canEdit;
private $coverImageFile; private $coverImageFile;
private $hideArchivedProjects;
public function setViewer(PhabricatorUser $viewer) { public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer; $this->viewer = $viewer;
@ -35,6 +36,15 @@ final class ProjectBoardTaskCard extends Phobject {
return $this->coverImageFile; 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) { public function setTask(ManiphestTask $task) {
$this->task = $task; $this->task = $task;
return $this; return $this;
@ -126,10 +136,12 @@ final class ProjectBoardTaskCard extends Phobject {
$project_handles = $this->getProjectHandles(); $project_handles = $this->getProjectHandles();
// Remove any archived projects from the list. // Remove any archived projects from the list.
if ($project_handles) { if ($this->hideArchivedProjects) {
foreach ($project_handles as $key => $handle) { if ($project_handles) {
if ($handle->getStatus() == PhabricatorObjectHandle::STATUS_CLOSED) { foreach ($project_handles as $key => $handle) {
unset($project_handles[$key]); 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() { public function getComponentDatasources() {
$sources = array( $sources = id(new PhabricatorQuickSearchEngine())
new PhabricatorPeopleDatasource(), ->getAllDatasources();
new PhabricatorProjectDatasource(),
new PhabricatorApplicationDatasource(),
new PhabricatorTypeaheadMonogramDatasource(),
new DiffusionRepositoryDatasource(),
new DiffusionSymbolDatasource(),
);
// These results are always rendered in the full browse display mode, so // These results are always rendered in the full browse display mode, so
// set the browse flag on all component sources. // set the browse flag on all component sources.

View file

@ -9,11 +9,19 @@ final class PhabricatorSubscriptionsUIEventListener
} }
public function handleEvent(PhutilEvent $event) { public function handleEvent(PhutilEvent $event) {
$object = $event->getValue('object');
switch ($event->getType()) { switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS: case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
$this->handleActionEvent($event); $this->handleActionEvent($event);
break; break;
case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES: 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); $this->handlePropertyEvent($event);
break; break;
} }

View file

@ -9,11 +9,19 @@ final class PhabricatorTokenUIEventListener
} }
public function handleEvent(PhutilEvent $event) { public function handleEvent(PhutilEvent $event) {
$object = $event->getValue('object');
switch ($event->getType()) { switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS: case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
$this->handleActionEvent($event); $this->handleActionEvent($event);
break; break;
case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES: 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); $this->handlePropertyEvent($event);
break; 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 authorities: the observed remote copy and the hosted local copy. Take the
other URI out of //Read/Write// mode first. 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 **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. 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 hosted by Phabricator by adding that other repository's URI, although this is
silly and probably very rarely of any use. 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. **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 For builtin URIs, it will not let users fetch changes from or push changes to
this URI. this URI.

View file

@ -34,6 +34,7 @@ abstract class PhabricatorCustomField extends Phobject {
const ROLE_CONDUIT = 'conduit'; const ROLE_CONDUIT = 'conduit';
const ROLE_HERALD = 'herald'; const ROLE_HERALD = 'herald';
const ROLE_EDITENGINE = 'EditEngine'; const ROLE_EDITENGINE = 'EditEngine';
const ROLE_HERALDACTION = 'herald.action';
/* -( Building Applications with Custom Fields )--------------------------- */ /* -( Building Applications with Custom Fields )--------------------------- */
@ -293,6 +294,8 @@ abstract class PhabricatorCustomField extends Phobject {
return $this->shouldAppearInTransactionMail(); return $this->shouldAppearInTransactionMail();
case self::ROLE_HERALD: case self::ROLE_HERALD:
return $this->shouldAppearInHerald(); return $this->shouldAppearInHerald();
case self::ROLE_HERALDACTION:
return $this->shouldAppearInHeraldActions();
case self::ROLE_EDITENGINE: case self::ROLE_EDITENGINE:
return $this->shouldAppearInEditView() || return $this->shouldAppearInEditView() ||
$this->shouldAppearInEditEngine(); $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(); 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 $viewer;
private $application; private $application;
private $controller; private $controller;
private $isFullSession;
public function setViewer(PhabricatorUser $viewer) { public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer; $this->viewer = $viewer;
@ -33,6 +34,15 @@ abstract class PhabricatorMainMenuBarExtension extends Phobject {
return $this->controller; 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() { final public function getExtensionKey() {
return $this->getPhobjectClassConstant('MAINMENUBARKEY'); return $this->getPhobjectClassConstant('MAINMENUBARKEY');
} }
@ -41,6 +51,10 @@ abstract class PhabricatorMainMenuBarExtension extends Phobject {
return true; return true;
} }
public function shouldAllowPartialSessions() {
return false;
}
public function isExtensionEnabledForViewer(PhabricatorUser $viewer) { public function isExtensionEnabledForViewer(PhabricatorUser $viewer) {
if (!$viewer->isLoggedIn()) { if (!$viewer->isLoggedIn()) {
return false; return false;

View file

@ -46,7 +46,9 @@ final class PhabricatorMainMenuView extends AphrontView {
$app_button = ''; $app_button = '';
$aural = null; $aural = null;
if ($viewer->isLoggedIn() && $viewer->isUserActivated()) { $is_full = $this->isFullSession($viewer);
if ($is_full) {
list($menu, $dropdowns, $aural) = $this->renderNotificationMenu(); list($menu, $dropdowns, $aural) = $this->renderNotificationMenu();
if (array_filter($menu)) { if (array_filter($menu)) {
$alerts[] = $menu; $alerts[] = $menu;
@ -54,14 +56,18 @@ final class PhabricatorMainMenuView extends AphrontView {
$menu_bar = array_merge($menu_bar, $dropdowns); $menu_bar = array_merge($menu_bar, $dropdowns);
$app_button = $this->renderApplicationMenuButton(); $app_button = $this->renderApplicationMenuButton();
$search_button = $this->renderSearchMenuButton($header_id); $search_button = $this->renderSearchMenuButton($header_id);
} else { } else if (!$viewer->isLoggedIn()) {
$app_button = $this->renderApplicationMenuButton(); $app_button = $this->renderApplicationMenuButton();
if (PhabricatorEnv::getEnvConfig('policy.allow-public')) { if (PhabricatorEnv::getEnvConfig('policy.allow-public')) {
$search_button = $this->renderSearchMenuButton($header_id); $search_button = $this->renderSearchMenuButton($header_id);
} }
} }
$search_menu = $this->renderPhabricatorSearchMenu(); if ($search_button) {
$search_menu = $this->renderPhabricatorSearchMenu();
} else {
$search_menu = null;
}
if ($alerts) { if ($alerts) {
$alerts = javelin_tag( $alerts = javelin_tag(
@ -84,7 +90,9 @@ final class PhabricatorMainMenuView extends AphrontView {
$extensions = PhabricatorMainMenuBarExtension::getAllEnabledExtensions(); $extensions = PhabricatorMainMenuBarExtension::getAllEnabledExtensions();
foreach ($extensions as $extension) { foreach ($extensions as $extension) {
$extension->setViewer($viewer); $extension
->setViewer($viewer)
->setIsFullSession($is_full);
$controller = $this->getController(); $controller = $this->getController();
if ($controller) { 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) { foreach ($extensions as $key => $extension) {
if (!$extension->isExtensionEnabledForViewer($extension->getViewer())) { if (!$extension->isExtensionEnabledForViewer($extension->getViewer())) {
unset($extensions[$key]); 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}; background-color: {$violet};
} }
.phui-timeline-icon-fill-pink {
background-color: {$pink};
}
.phui-timeline-icon-fill-grey { .phui-timeline-icon-fill-grey {
background-color: #888; background-color: #888;
} }