diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -8,7 +8,7 @@ return array( 'names' => array( 'core.pkg.css' => '35e4a99a', - 'core.pkg.js' => '8a616602', + 'core.pkg.js' => '08b41036', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '7ba78475', 'differential.pkg.js' => 'd0cd0df6', @@ -494,7 +494,7 @@ 'rsrc/js/core/behavior-oncopy.js' => '2926fff2', 'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03', 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '340c8eff', - 'rsrc/js/core/behavior-read-only-warning.js' => 'f8ea359c', + 'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207', 'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b', 'rsrc/js/core/behavior-remarkup-preview.js' => '4b700e9e', 'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e', @@ -667,7 +667,7 @@ 'javelin-behavior-project-boards' => '14a1faae', 'javelin-behavior-project-create' => '065227cc', 'javelin-behavior-quicksand-blacklist' => '7927a7d3', - 'javelin-behavior-read-only-warning' => 'f8ea359c', + 'javelin-behavior-read-only-warning' => 'ba158207', 'javelin-behavior-recurring-edit' => '5f1c4d5f', 'javelin-behavior-refresh-csrf' => 'ab2f381b', 'javelin-behavior-releeph-preview-branch' => 'b2b4fbaf', @@ -1783,6 +1783,11 @@ 'javelin-json', 'phabricator-draggable-list', ), + 'ba158207' => array( + 'javelin-behavior', + 'javelin-uri', + 'phabricator-notification', + ), 'bae58312' => array( 'javelin-install', 'javelin-workboard-card', @@ -2111,11 +2116,6 @@ 'javelin-util', 'phabricator-busy', ), - 'f8ea359c' => array( - 'javelin-behavior', - 'javelin-uri', - 'phabricator-notification', - ), 'fa0f4fc2' => array( 'javelin-behavior', 'javelin-dom', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3425,6 +3425,7 @@ 'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php', 'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php', 'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php', + 'PhabricatorSystemReadOnlyController' => 'applications/system/controller/PhabricatorSystemReadOnlyController.php', 'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php', 'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php', 'PhabricatorSystemRemoveWorkflow' => 'applications/system/management/PhabricatorSystemRemoveWorkflow.php', @@ -8076,6 +8077,7 @@ 'PhabricatorSystemDAO' => 'PhabricatorLiskDAO', 'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector', 'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO', + 'PhabricatorSystemReadOnlyController' => 'PhabricatorController', 'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow', 'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow', 'PhabricatorSystemRemoveWorkflow' => 'PhabricatorManagementWorkflow', diff --git a/src/applications/system/application/PhabricatorSystemApplication.php b/src/applications/system/application/PhabricatorSystemApplication.php --- a/src/applications/system/application/PhabricatorSystemApplication.php +++ b/src/applications/system/application/PhabricatorSystemApplication.php @@ -23,6 +23,9 @@ 'encoding/' => 'PhabricatorSystemSelectEncodingController', 'highlight/' => 'PhabricatorSystemSelectHighlightController', ), + '/readonly/' => array( + '(?P[^/]+)/' => 'PhabricatorSystemReadOnlyController', + ), ); } diff --git a/src/applications/system/controller/PhabricatorSystemReadOnlyController.php b/src/applications/system/controller/PhabricatorSystemReadOnlyController.php new file mode 100644 --- /dev/null +++ b/src/applications/system/controller/PhabricatorSystemReadOnlyController.php @@ -0,0 +1,50 @@ +getURIData('reason'); + + $body = array(); + switch ($reason) { + case PhabricatorEnv::READONLY_CONFIG: + $title = pht('Administrative Read-Only Mode'); + $body[] = pht( + 'An administrator has placed Phabricator into read-only mode.'); + $body[] = pht( + 'This mode may be used to perform temporary maintenance, test '. + 'configuration, or archive an installation permanently.'); + $body[] = pht( + 'Read-only mode was enabled by the explicit action of a human '. + 'administrator, so you can get more information about why it '. + 'has been turned on by rolling your chair away from your desk and '. + 'yelling "Hey! Why is Phabricator in read-only mode??!" using '. + 'your very loudest outside voice.'); + $button = pht('Wait Patiently'); + break; + default: + return new Aphront404Response(); + } + + $body[] = pht( + 'In read-only mode you can read existing information, but you will not '. + 'be able to edit information or create new information until this mode '. + 'is disabled.'); + + $dialog = $this->newDialog() + ->setTitle($title) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->addCancelButton('/', $button); + + foreach ($body as $paragraph) { + $dialog->appendParagraph($paragraph); + } + + return $dialog; + } +} diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -57,6 +57,9 @@ private static $cache; private static $localeCode; private static $readOnly; + private static $readOnlyReason; + + const READONLY_CONFIG = 'config'; /** * @phutil-external-symbol class PhabricatorStartup @@ -447,8 +450,31 @@ return self::getEnvConfig('cluster.read-only'); } - public static function setReadOnly($read_only) { + public static function setReadOnly($read_only, $reason) { self::$readOnly = $read_only; + self::$readOnlyReason = $reason; + } + + public static function getReadOnlyMessage() { + return pht('Phabricator is currently in read-only mode.'); + } + + public static function getReadOnlyURI() { + return urisprintf( + '/readonly/%s/', + self::getReadOnlyReason()); + } + + public static function getReadOnlyReason() { + if (!self::isReadOnly()) { + return null; + } + + if (self::$readOnlyReason !== null) { + return self::$readOnlyReason; + } + + return self::READONLY_CONFIG; } diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php @@ -52,7 +52,7 @@ if (PhabricatorEnv::isReadOnly()) { if ($this->isForce()) { - PhabricatorEnv::setReadOnly(false); + PhabricatorEnv::setReadOnly(false, null); } else { throw new PhutilArgumentUsageException( pht( diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -276,7 +276,8 @@ Javelin::initBehavior( 'read-only-warning', array( - 'message' => pht('Phabricator is currently in read-only mode.'), + 'message' => PhabricatorEnv::getReadOnlyMessage(), + 'uri' => PhabricatorEnv::getReadOnlyURI(), )); } diff --git a/webroot/rsrc/js/core/behavior-read-only-warning.js b/webroot/rsrc/js/core/behavior-read-only-warning.js --- a/webroot/rsrc/js/core/behavior-read-only-warning.js +++ b/webroot/rsrc/js/core/behavior-read-only-warning.js @@ -7,10 +7,17 @@ JX.behavior('read-only-warning', function(config) { - new JX.Notification() + var n = new JX.Notification() .setContent(config.message) .setDuration(0) - .alterClassName('jx-notification-read-only', true) - .show(); + .alterClassName('jx-notification-read-only', true); + + n.listen( + 'activate', + function() { + JX.$U(config.uri).go(); + }); + + n.show(); });