From 2c713b2d25fdde05297b2f94c445ffae9250b4ea Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 17 Jan 2019 10:47:13 -0800 Subject: [PATCH] Add "Auth Messages" to support customizing onboarding/welcome flows Summary: Ref T13222. Long ago, we had a Config option (`welcome.html`) to let you dump HTML onto the login screen, but this was relatively hard to use and not good from a security perspective. In some cases this was obsoleted by Dashboards, but there's at least some remaining set of use cases for actual login instructions on the login screen. For example, WMF has some guidance on //which// SSO mechanism to use based on what types of account you have. On `secure`, users assume they can register by clicking "Log In With GitHub" or whatever, and it might reduce frustration to tell them upfront that registration is closed. Some other types of auth messaging could also either use customization or defaults (e.g., the invite/welcome/approve mail). We could do this with a bunch of Config options, but I'd generally like to move to a world where there's less stuff in Config and more configuration is contextual. I think it tends to be easier to use, and we get a lot of fringe benefits (granular permissions, API, normal transaction logs, more abililty to customize workflows and provide contextual help/hints, etc). Here, for example, we can provide a remarkup preview, which would be trickier with Config. This does not actually do anything yet. Test Plan: {F6137541} Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13222 Differential Revision: https://secure.phabricator.com/D19992 --- .../20190117.authmessage.01.message.sql | 8 ++ .../20190117.authmessage.02.xaction.sql | 19 +++ src/__phutil_library_map__.php | 37 ++++++ .../PhabricatorAuthApplication.php | 10 ++ .../PhabricatorAuthProviderController.php | 14 +++ .../PhabricatorAuthMessageController.php | 11 ++ .../PhabricatorAuthMessageEditController.php | 31 +++++ .../PhabricatorAuthMessageListController.php | 77 ++++++++++++ .../PhabricatorAuthMessageViewController.php | 104 ++++++++++++++++ ...icatorAuthFactorProviderEditController.php | 2 +- .../PhabricatorAuthMessageEditEngine.php | 108 ++++++++++++++++ .../editor/PhabricatorAuthMessageEditor.php | 22 ++++ .../PhabricatorAuthLoginMessageType.php | 18 +++ .../message/PhabricatorAuthMessageType.php | 32 +++++ .../PhabricatorAuthWelcomeMailMessageType.php | 18 +++ .../phid/PhabricatorAuthMessagePHIDType.php | 32 +++++ .../query/PhabricatorAuthMessageQuery.php | 83 +++++++++++++ ...PhabricatorAuthMessageTransactionQuery.php | 10 ++ .../auth/storage/PhabricatorAuthMessage.php | 116 ++++++++++++++++++ .../PhabricatorAuthMessageTransaction.php | 18 +++ .../PhabricatorAuthMessageTextTransaction.php | 39 ++++++ .../PhabricatorAuthMessageTransactionType.php | 4 + 22 files changed, 812 insertions(+), 1 deletion(-) create mode 100644 resources/sql/autopatches/20190117.authmessage.01.message.sql create mode 100644 resources/sql/autopatches/20190117.authmessage.02.xaction.sql create mode 100644 src/applications/auth/controller/message/PhabricatorAuthMessageController.php create mode 100644 src/applications/auth/controller/message/PhabricatorAuthMessageEditController.php create mode 100644 src/applications/auth/controller/message/PhabricatorAuthMessageListController.php create mode 100644 src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php create mode 100644 src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php create mode 100644 src/applications/auth/editor/PhabricatorAuthMessageEditor.php create mode 100644 src/applications/auth/message/PhabricatorAuthLoginMessageType.php create mode 100644 src/applications/auth/message/PhabricatorAuthMessageType.php create mode 100644 src/applications/auth/message/PhabricatorAuthWelcomeMailMessageType.php create mode 100644 src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php create mode 100644 src/applications/auth/query/PhabricatorAuthMessageQuery.php create mode 100644 src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php create mode 100644 src/applications/auth/storage/PhabricatorAuthMessage.php create mode 100644 src/applications/auth/storage/PhabricatorAuthMessageTransaction.php create mode 100644 src/applications/auth/xaction/PhabricatorAuthMessageTextTransaction.php create mode 100644 src/applications/auth/xaction/PhabricatorAuthMessageTransactionType.php diff --git a/resources/sql/autopatches/20190117.authmessage.01.message.sql b/resources/sql/autopatches/20190117.authmessage.01.message.sql new file mode 100644 index 0000000000..9f4afa2646 --- /dev/null +++ b/resources/sql/autopatches/20190117.authmessage.01.message.sql @@ -0,0 +1,8 @@ +CREATE TABLE {$NAMESPACE}_auth.auth_message ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + messageKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT}, + messageText LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20190117.authmessage.02.xaction.sql b/resources/sql/autopatches/20190117.authmessage.02.xaction.sql new file mode 100644 index 0000000000..944de129a0 --- /dev/null +++ b/resources/sql/autopatches/20190117.authmessage.02.xaction.sql @@ -0,0 +1,19 @@ +CREATE TABLE {$NAMESPACE}_auth.auth_messagetransaction ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + authorPHID VARBINARY(64) NOT NULL, + objectPHID VARBINARY(64) NOT NULL, + viewPolicy VARBINARY(64) NOT NULL, + editPolicy VARBINARY(64) NOT NULL, + commentPHID VARBINARY(64) DEFAULT NULL, + commentVersion INT UNSIGNED NOT NULL, + transactionType VARCHAR(32) NOT NULL, + oldValue LONGTEXT NOT NULL, + newValue LONGTEXT NOT NULL, + contentSource LONGTEXT NOT NULL, + metadata LONGTEXT NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 82a639f61f..8ee28d39a7 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2246,6 +2246,7 @@ phutil_register_library_map(array( 'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php', 'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php', 'PhabricatorAuthLoginHandler' => 'applications/auth/handler/PhabricatorAuthLoginHandler.php', + 'PhabricatorAuthLoginMessageType' => 'applications/auth/message/PhabricatorAuthLoginMessageType.php', 'PhabricatorAuthLogoutConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthLogoutConduitAPIMethod.php', 'PhabricatorAuthMFAEditEngineExtension' => 'applications/auth/engineextension/PhabricatorAuthMFAEditEngineExtension.php', 'PhabricatorAuthMainMenuBarExtension' => 'applications/auth/extension/PhabricatorAuthMainMenuBarExtension.php', @@ -2261,6 +2262,20 @@ phutil_register_library_map(array( 'PhabricatorAuthManagementUntrustOAuthClientWorkflow' => 'applications/auth/management/PhabricatorAuthManagementUntrustOAuthClientWorkflow.php', 'PhabricatorAuthManagementVerifyWorkflow' => 'applications/auth/management/PhabricatorAuthManagementVerifyWorkflow.php', 'PhabricatorAuthManagementWorkflow' => 'applications/auth/management/PhabricatorAuthManagementWorkflow.php', + 'PhabricatorAuthMessage' => 'applications/auth/storage/PhabricatorAuthMessage.php', + 'PhabricatorAuthMessageController' => 'applications/auth/controller/message/PhabricatorAuthMessageController.php', + 'PhabricatorAuthMessageEditController' => 'applications/auth/controller/message/PhabricatorAuthMessageEditController.php', + 'PhabricatorAuthMessageEditEngine' => 'applications/auth/editor/PhabricatorAuthMessageEditEngine.php', + 'PhabricatorAuthMessageEditor' => 'applications/auth/editor/PhabricatorAuthMessageEditor.php', + 'PhabricatorAuthMessageListController' => 'applications/auth/controller/message/PhabricatorAuthMessageListController.php', + 'PhabricatorAuthMessagePHIDType' => 'applications/auth/phid/PhabricatorAuthMessagePHIDType.php', + 'PhabricatorAuthMessageQuery' => 'applications/auth/query/PhabricatorAuthMessageQuery.php', + 'PhabricatorAuthMessageTextTransaction' => 'applications/auth/xaction/PhabricatorAuthMessageTextTransaction.php', + 'PhabricatorAuthMessageTransaction' => 'applications/auth/storage/PhabricatorAuthMessageTransaction.php', + 'PhabricatorAuthMessageTransactionQuery' => 'applications/auth/query/PhabricatorAuthMessageTransactionQuery.php', + 'PhabricatorAuthMessageTransactionType' => 'applications/auth/xaction/PhabricatorAuthMessageTransactionType.php', + 'PhabricatorAuthMessageType' => 'applications/auth/message/PhabricatorAuthMessageType.php', + 'PhabricatorAuthMessageViewController' => 'applications/auth/controller/message/PhabricatorAuthMessageViewController.php', 'PhabricatorAuthNeedsApprovalController' => 'applications/auth/controller/PhabricatorAuthNeedsApprovalController.php', 'PhabricatorAuthNeedsMultiFactorController' => 'applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php', 'PhabricatorAuthNewController' => 'applications/auth/controller/config/PhabricatorAuthNewController.php', @@ -2337,6 +2352,7 @@ phutil_register_library_map(array( 'PhabricatorAuthTryFactorAction' => 'applications/auth/action/PhabricatorAuthTryFactorAction.php', 'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php', 'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php', + 'PhabricatorAuthWelcomeMailMessageType' => 'applications/auth/message/PhabricatorAuthWelcomeMailMessageType.php', 'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php', 'PhabricatorAutoEventListener' => 'infrastructure/events/PhabricatorAutoEventListener.php', 'PhabricatorBadgesApplication' => 'applications/badges/application/PhabricatorBadgesApplication.php', @@ -7920,6 +7936,7 @@ phutil_register_library_map(array( 'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController', 'PhabricatorAuthLoginController' => 'PhabricatorAuthController', 'PhabricatorAuthLoginHandler' => 'Phobject', + 'PhabricatorAuthLoginMessageType' => 'PhabricatorAuthMessageType', 'PhabricatorAuthLogoutConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod', 'PhabricatorAuthMFAEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorAuthMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension', @@ -7935,6 +7952,25 @@ phutil_register_library_map(array( 'PhabricatorAuthManagementUntrustOAuthClientWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementVerifyWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementWorkflow' => 'PhabricatorManagementWorkflow', + 'PhabricatorAuthMessage' => array( + 'PhabricatorAuthDAO', + 'PhabricatorApplicationTransactionInterface', + 'PhabricatorPolicyInterface', + 'PhabricatorDestructibleInterface', + ), + 'PhabricatorAuthMessageController' => 'PhabricatorAuthProviderController', + 'PhabricatorAuthMessageEditController' => 'PhabricatorAuthMessageController', + 'PhabricatorAuthMessageEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorAuthMessageEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhabricatorAuthMessageListController' => 'PhabricatorAuthProviderController', + 'PhabricatorAuthMessagePHIDType' => 'PhabricatorPHIDType', + 'PhabricatorAuthMessageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorAuthMessageTextTransaction' => 'PhabricatorAuthMessageTransactionType', + 'PhabricatorAuthMessageTransaction' => 'PhabricatorModularTransaction', + 'PhabricatorAuthMessageTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhabricatorAuthMessageTransactionType' => 'PhabricatorModularTransactionType', + 'PhabricatorAuthMessageType' => 'Phobject', + 'PhabricatorAuthMessageViewController' => 'PhabricatorAuthMessageController', 'PhabricatorAuthNeedsApprovalController' => 'PhabricatorAuthController', 'PhabricatorAuthNeedsMultiFactorController' => 'PhabricatorAuthController', 'PhabricatorAuthNewController' => 'PhabricatorAuthProviderConfigController', @@ -8030,6 +8066,7 @@ phutil_register_library_map(array( 'PhabricatorAuthTryFactorAction' => 'PhabricatorSystemAction', 'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController', 'PhabricatorAuthValidateController' => 'PhabricatorAuthController', + 'PhabricatorAuthWelcomeMailMessageType' => 'PhabricatorAuthMessageType', 'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorAutoEventListener' => 'PhabricatorEventListener', 'PhabricatorBadgesApplication' => 'PhabricatorApplication', diff --git a/src/applications/auth/application/PhabricatorAuthApplication.php b/src/applications/auth/application/PhabricatorAuthApplication.php index 62f86a00f8..2c36e935ee 100644 --- a/src/applications/auth/application/PhabricatorAuthApplication.php +++ b/src/applications/auth/application/PhabricatorAuthApplication.php @@ -94,6 +94,16 @@ final class PhabricatorAuthApplication extends PhabricatorApplication { '(?P[1-9]\d*)/' => 'PhabricatorAuthFactorProviderViewController', ), + + 'message/' => array( + $this->getQueryRoutePattern() => + 'PhabricatorAuthMessageListController', + $this->getEditRoutePattern('edit/') => + 'PhabricatorAuthMessageEditController', + '(?P[1-9]\d*)/' => + 'PhabricatorAuthMessageViewController', + ), + ), '/oauth/(?P\w+)/login/' diff --git a/src/applications/auth/controller/config/PhabricatorAuthProviderController.php b/src/applications/auth/controller/config/PhabricatorAuthProviderController.php index 2fb4386ef4..2668da1218 100644 --- a/src/applications/auth/controller/config/PhabricatorAuthProviderController.php +++ b/src/applications/auth/controller/config/PhabricatorAuthProviderController.php @@ -31,6 +31,20 @@ abstract class PhabricatorAuthProviderController ->setHref($this->getApplicationURI('mfa/')) ->setIcon('fa-mobile')); + $nav->addMenuItem( + id(new PHUIListItemView()) + ->setName(pht('Onboarding')) + ->setType(PHUIListItemView::TYPE_LABEL)); + + $nav->addMenuItem( + id(new PHUIListItemView()) + ->setKey('message') + ->setName(pht('Customize Messages')) + ->setType(PHUIListItemView::TYPE_LINK) + ->setHref($this->getApplicationURI('message/')) + ->setIcon('fa-commenting-o')); + + $nav->selectFilter(null); return $nav; diff --git a/src/applications/auth/controller/message/PhabricatorAuthMessageController.php b/src/applications/auth/controller/message/PhabricatorAuthMessageController.php new file mode 100644 index 0000000000..98bb908cfe --- /dev/null +++ b/src/applications/auth/controller/message/PhabricatorAuthMessageController.php @@ -0,0 +1,11 @@ +addTextCrumb(pht('Messages'), $this->getApplicationURI('message/')); + } + +} diff --git a/src/applications/auth/controller/message/PhabricatorAuthMessageEditController.php b/src/applications/auth/controller/message/PhabricatorAuthMessageEditController.php new file mode 100644 index 0000000000..3cb4a4b0af --- /dev/null +++ b/src/applications/auth/controller/message/PhabricatorAuthMessageEditController.php @@ -0,0 +1,31 @@ +requireApplicationCapability( + AuthManageProvidersCapability::CAPABILITY); + + $engine = id(new PhabricatorAuthMessageEditEngine()) + ->setController($this); + + $id = $request->getURIData('id'); + if (!$id) { + $message_key = $request->getStr('messageKey'); + + $message_types = PhabricatorAuthMessageType::getAllMessageTypes(); + $message_type = idx($message_types, $message_key); + if (!$message_type) { + return new Aphront404Response(); + } + + $engine + ->addContextParameter('messageKey', $message_key) + ->setMessageType($message_type); + } + + return $engine->buildResponse(); + } + +} diff --git a/src/applications/auth/controller/message/PhabricatorAuthMessageListController.php b/src/applications/auth/controller/message/PhabricatorAuthMessageListController.php new file mode 100644 index 0000000000..a3c518ab36 --- /dev/null +++ b/src/applications/auth/controller/message/PhabricatorAuthMessageListController.php @@ -0,0 +1,77 @@ +getViewer(); + + $can_manage = $this->hasApplicationCapability( + AuthManageProvidersCapability::CAPABILITY); + + $types = PhabricatorAuthMessageType::getAllMessageTypes(); + + $messages = id(new PhabricatorAuthMessageQuery()) + ->setViewer($viewer) + ->execute(); + $messages = mpull($messages, null, 'getMessageKey'); + + $list = new PHUIObjectItemListView(); + foreach ($types as $type) { + $message = idx($messages, $type->getMessageTypeKey()); + if ($message) { + $href = $message->getURI(); + $name = $message->getMessageTypeDisplayName(); + } else { + $href = '/auth/message/edit/?messageKey='.$type->getMessageTypeKey(); + $name = $type->getDisplayName(); + } + + $item = id(new PHUIObjectItemView()) + ->setHeader($name) + ->setHref($href) + ->addAttribute($type->getShortDescription()); + + if ($message) { + $item->addIcon('fa-circle', pht('Customized')); + } else { + $item->addIcon('fa-circle-o grey', pht('Default')); + } + + $list->addItem($item); + } + + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb(pht('Messages')) + ->setBorder(true); + + $list->setFlush(true); + $list = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Auth Messages')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($list); + + $title = pht('Auth Messages'); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-commenting-o'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter( + array( + $list, + )); + + $nav = $this->newNavigation() + ->setCrumbs($crumbs) + ->appendChild($view); + + $nav->selectFilter('message'); + + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); + } + +} diff --git a/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php b/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php new file mode 100644 index 0000000000..db7e7e65e0 --- /dev/null +++ b/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php @@ -0,0 +1,104 @@ +getViewer(); + + $this->requireApplicationCapability( + AuthManageProvidersCapability::CAPABILITY); + + $message = id(new PhabricatorAuthMessageQuery()) + ->setViewer($viewer) + ->withIDs(array($request->getURIData('id'))) + ->executeOne(); + if (!$message) { + return new Aphront404Response(); + } + + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb($message->getObjectName()) + ->setBorder(true); + + $header = $this->buildHeaderView($message); + $properties = $this->buildPropertiesView($message); + $curtain = $this->buildCurtain($message); + + $timeline = $this->buildTransactionTimeline( + $message, + new PhabricatorAuthMessageTransactionQuery()); + $timeline->setShouldTerminate(true); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn( + array( + $timeline, + )) + ->addPropertySection(pht('Details'), $properties); + + return $this->newPage() + ->setTitle($message->getMessageTypeDisplayName()) + ->setCrumbs($crumbs) + ->setPageObjectPHIDs( + array( + $message->getPHID(), + )) + ->appendChild($view); + } + + private function buildHeaderView(PhabricatorAuthMessage $message) { + $viewer = $this->getViewer(); + + $view = id(new PHUIHeaderView()) + ->setViewer($viewer) + ->setHeader($message->getMessageTypeDisplayName()); + + return $view; + } + + private function buildPropertiesView(PhabricatorAuthMessage $message) { + $viewer = $this->getViewer(); + + $view = id(new PHUIPropertyListView()) + ->setViewer($viewer); + + $view->addProperty( + pht('Description'), + $message->getMessageType()->getShortDescription()); + + $view->addSectionHeader( + pht('Message Preview'), + PHUIPropertyListView::ICON_SUMMARY); + + $view->addTextContent( + new PHUIRemarkupView($viewer, $message->getMessageText())); + + return $view; + } + + private function buildCurtain(PhabricatorAuthMessage $message) { + $viewer = $this->getViewer(); + $id = $message->getID(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $message, + PhabricatorPolicyCapability::CAN_EDIT); + + $curtain = $this->newCurtainView($message); + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Message')) + ->setIcon('fa-pencil') + ->setHref($this->getApplicationURI("message/edit/{$id}/")) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $curtain; + } + +} diff --git a/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderEditController.php b/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderEditController.php index 5108eaaefd..0dde1b3c6f 100644 --- a/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderEditController.php +++ b/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderEditController.php @@ -23,7 +23,7 @@ final class PhabricatorAuthFactorProviderEditController $engine ->addContextParameter('providerFactorKey', $factor_key) ->setProviderFactor($factor); - } + } return $engine->buildResponse(); } diff --git a/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php b/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php new file mode 100644 index 0000000000..0a9aa32de4 --- /dev/null +++ b/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php @@ -0,0 +1,108 @@ +messageType = $type; + return $this; + } + + public function getMessageType() { + return $this->messageType; + } + + protected function newEditableObject() { + $type = $this->getMessageType(); + + if ($type) { + $message = PhabricatorAuthMessage::initializeNewMessage($type); + } else { + $message = new PhabricatorAuthMessage(); + } + + return $message; + } + + protected function newObjectQuery() { + return new PhabricatorAuthMessageQuery(); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create Auth Message'); + } + + protected function getObjectCreateButtonText($object) { + return pht('Create Auth Message'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Auth Message'); + } + + protected function getObjectEditShortText($object) { + return $object->getObjectName(); + } + + protected function getObjectCreateShortText() { + return pht('Create Auth Message'); + } + + protected function getObjectName() { + return pht('Auth Message'); + } + + protected function getEditorURI() { + return '/auth/message/edit/'; + } + + protected function getObjectCreateCancelURI($object) { + return '/auth/message/'; + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function getCreateNewObjectPolicy() { + return $this->getApplication()->getPolicy( + AuthManageProvidersCapability::CAPABILITY); + } + + protected function buildCustomEditFields($object) { + return array( + id(new PhabricatorRemarkupEditField()) + ->setKey('messageText') + ->setTransactionType( + PhabricatorAuthMessageTextTransaction::TRANSACTIONTYPE) + ->setLabel(pht('Message Text')) + ->setDescription(pht('Custom text for the message.')) + ->setValue($object->getMessageText()), + ); + } + +} diff --git a/src/applications/auth/editor/PhabricatorAuthMessageEditor.php b/src/applications/auth/editor/PhabricatorAuthMessageEditor.php new file mode 100644 index 0000000000..56e8e716cd --- /dev/null +++ b/src/applications/auth/editor/PhabricatorAuthMessageEditor.php @@ -0,0 +1,22 @@ +getPhobjectClassConstant('MESSAGEKEY', 64); + } + + final public static function getAllMessageTypes() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getMessageTypeKey') + ->execute(); + } + + final public static function newFromKey($key) { + $types = self::getAllMessageTypes(); + + if (empty($types[$key])) { + throw new Exception( + pht( + 'No message type exists with key "%s".', + $key)); + } + + return clone $types[$key]; + } + + abstract public function getDisplayName(); + +} diff --git a/src/applications/auth/message/PhabricatorAuthWelcomeMailMessageType.php b/src/applications/auth/message/PhabricatorAuthWelcomeMailMessageType.php new file mode 100644 index 0000000000..fe4b25cbb2 --- /dev/null +++ b/src/applications/auth/message/PhabricatorAuthWelcomeMailMessageType.php @@ -0,0 +1,18 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + public function withMessageKeys(array $keys) { + $this->messageKeys = $keys; + return $this; + } + + public function newResultObject() { + return new PhabricatorAuthMessage(); + } + + protected function loadPage() { + return $this->loadStandardPage($this->newResultObject()); + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'id IN (%Ld)', + $this->ids); + } + + if ($this->phids !== null) { + $where[] = qsprintf( + $conn, + 'phid IN (%Ls)', + $this->phids); + } + + if ($this->messageKeys !== null) { + $where[] = qsprintf( + $conn, + 'messageKey IN (%Ls)', + $this->messageKeys); + } + + return $where; + } + + protected function willFilterPage(array $messages) { + $message_types = PhabricatorAuthMessageType::getAllMessageTypes(); + + foreach ($messages as $key => $message) { + $message_key = $message->getMessageKey(); + + $message_type = idx($message_types, $message_key); + if (!$message_type) { + unset($messages[$key]); + $this->didRejectResult($message); + continue; + } + + $message->attachMessageType($message_type); + } + + return $messages; + } + + public function getQueryApplicationClass() { + return 'PhabricatorAuthApplication'; + } + +} diff --git a/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php new file mode 100644 index 0000000000..0b2ce79db3 --- /dev/null +++ b/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php @@ -0,0 +1,10 @@ +setMessageKey($type->getMessageTypeKey()) + ->attachMessageType($type); + } + + protected function getConfiguration() { + return array( + self::CONFIG_AUX_PHID => true, + self::CONFIG_COLUMN_SCHEMA => array( + 'messageKey' => 'text64', + 'messageText' => 'text', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_type' => array( + 'columns' => array('messageKey'), + 'unique' => true, + ), + ), + ) + parent::getConfiguration(); + } + + public function getPHIDType() { + return PhabricatorAuthMessagePHIDType::TYPECONST; + } + + public function getObjectName() { + return pht('Auth Message %d', $this->getID()); + } + + public function getURI() { + return urisprintf('/auth/message/%s', $this->getID()); + } + + public function attachMessageType(PhabricatorAuthMessageType $type) { + $this->messageType = $type; + return $this; + } + + public function getMessageType() { + return $this->assertAttached($this->messageType); + } + + public function getMessageTypeDisplayName() { + return $this->getMessageType()->getDisplayName(); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::getMostOpenPolicy(); + default: + return false; + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + // Even if an install doesn't allow public users, you can still view + // auth messages: otherwise, we can't do things like show you + // guidance on the login screen. + return true; + default: + return false; + } + } + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new PhabricatorAuthMessageEditor(); + } + + public function getApplicationTransactionTemplate() { + return new PhabricatorAuthMessageTransaction(); + } + + +/* -( PhabricatorDestructibleInterface )----------------------------------- */ + + + public function destroyObjectPermanently( + PhabricatorDestructionEngine $engine) { + $this->delete(); + } + +} diff --git a/src/applications/auth/storage/PhabricatorAuthMessageTransaction.php b/src/applications/auth/storage/PhabricatorAuthMessageTransaction.php new file mode 100644 index 0000000000..407a9735c6 --- /dev/null +++ b/src/applications/auth/storage/PhabricatorAuthMessageTransaction.php @@ -0,0 +1,18 @@ +getMessageText(); + } + + public function applyInternalEffects($object, $value) { + $object->setMessageText($value); + } + + public function getTitle() { + return pht( + '%s updated the message text.', + $this->renderAuthor()); + } + + public function hasChangeDetailView() { + return true; + } + + public function getMailDiffSectionHeader() { + return pht('CHANGES TO MESSAGE'); + } + + public function newChangeDetailView() { + $viewer = $this->getViewer(); + + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) + ->setViewer($viewer) + ->setOldText($this->getOldValue()) + ->setNewText($this->getNewValue()); + } + +} diff --git a/src/applications/auth/xaction/PhabricatorAuthMessageTransactionType.php b/src/applications/auth/xaction/PhabricatorAuthMessageTransactionType.php new file mode 100644 index 0000000000..eeb1b350f3 --- /dev/null +++ b/src/applications/auth/xaction/PhabricatorAuthMessageTransactionType.php @@ -0,0 +1,4 @@ +