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:
commit
9570b843ea
40 changed files with 792 additions and 240 deletions
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 )--------------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -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,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionQuickSearchEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new DiffusionRepositoryDatasource(),
|
||||
new DiffusionSymbolDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorQuickSearchApplicationEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new PhabricatorApplicationDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPeopleQuickSearchEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new PhabricatorPeopleDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
final class ProjectQuickSearchEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new PhabricatorProjectDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorQuickSearchEngine extends Phobject {
|
||||
|
||||
public function getAllDatasources() {
|
||||
return PhabricatorQuickSearchEngineExtension::getAllDatasources();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMonogramQuickSearchEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new PhabricatorTypeaheadMonogramDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -315,6 +315,10 @@
|
|||
background-color: {$violet};
|
||||
}
|
||||
|
||||
.phui-timeline-icon-fill-pink {
|
||||
background-color: {$pink};
|
||||
}
|
||||
|
||||
.phui-timeline-icon-fill-grey {
|
||||
background-color: #888;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue