mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 18:22:41 +01:00
Replace old hard-coded URI-based "changes saved" jank with new overgeneralized cookie-based "changes saved" jank
Summary: Ref T13515. Settings currently has some highly specialized code for rendering "Changes saved." messages. The "saved" state is communicated across a redirect-after-POST by adding `/saved/` to the end of the URI. This isn't great. It needs a lot of moving pieces, including special accommodations in routing rules. It's user-visible. It has the wrong behavior if you reload the page or navigate directly to the "saved" URI. Try this scheme, which is also pretty sketchy but seems like an upgrade on the balance: - Set a cookie on the redirect which identifies the form we just saved. - On page startup: if this cookie exists, save the value and clear it. - If the current page started with a cookie identifying the form on the page, treat the page as a "saved" page. This supports passing a small amount of state across the redirect-after-POST flow, and when you reload the page it doesn't keep the message around. Applications don't need to coordinate it, either. Seems somewhat cleaner? Test Plan: In Firefox, Safari, and Chrome: saved settings, saw a "Saved changes" banner without any URI junk. Reloaded page, saw banner vanish properly. Maniphest Tasks: T13515 Differential Revision: https://secure.phabricator.com/D21144
This commit is contained in:
parent
3984c14260
commit
7a79131bf2
12 changed files with 128 additions and 28 deletions
|
@ -3251,6 +3251,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEditEngineMFAInterface' => 'applications/transactions/editengine/PhabricatorEditEngineMFAInterface.php',
|
'PhabricatorEditEngineMFAInterface' => 'applications/transactions/editengine/PhabricatorEditEngineMFAInterface.php',
|
||||||
'PhabricatorEditEngineNameTransaction' => 'applications/transactions/xaction/PhabricatorEditEngineNameTransaction.php',
|
'PhabricatorEditEngineNameTransaction' => 'applications/transactions/xaction/PhabricatorEditEngineNameTransaction.php',
|
||||||
'PhabricatorEditEngineOrderTransaction' => 'applications/transactions/xaction/PhabricatorEditEngineOrderTransaction.php',
|
'PhabricatorEditEngineOrderTransaction' => 'applications/transactions/xaction/PhabricatorEditEngineOrderTransaction.php',
|
||||||
|
'PhabricatorEditEnginePageState' => 'applications/transactions/editengine/PhabricatorEditEnginePageState.php',
|
||||||
'PhabricatorEditEnginePointsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEnginePointsCommentAction.php',
|
'PhabricatorEditEnginePointsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEnginePointsCommentAction.php',
|
||||||
'PhabricatorEditEnginePreambleTransaction' => 'applications/transactions/xaction/PhabricatorEditEnginePreambleTransaction.php',
|
'PhabricatorEditEnginePreambleTransaction' => 'applications/transactions/xaction/PhabricatorEditEnginePreambleTransaction.php',
|
||||||
'PhabricatorEditEngineProfileMenuItem' => 'applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php',
|
'PhabricatorEditEngineProfileMenuItem' => 'applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php',
|
||||||
|
@ -9714,6 +9715,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEditEngineMFAEngine' => 'Phobject',
|
'PhabricatorEditEngineMFAEngine' => 'Phobject',
|
||||||
'PhabricatorEditEngineNameTransaction' => 'PhabricatorEditEngineTransactionType',
|
'PhabricatorEditEngineNameTransaction' => 'PhabricatorEditEngineTransactionType',
|
||||||
'PhabricatorEditEngineOrderTransaction' => 'PhabricatorEditEngineTransactionType',
|
'PhabricatorEditEngineOrderTransaction' => 'PhabricatorEditEngineTransactionType',
|
||||||
|
'PhabricatorEditEnginePageState' => 'Phobject',
|
||||||
'PhabricatorEditEnginePointsCommentAction' => 'PhabricatorEditEngineCommentAction',
|
'PhabricatorEditEnginePointsCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||||
'PhabricatorEditEnginePreambleTransaction' => 'PhabricatorEditEngineTransactionType',
|
'PhabricatorEditEnginePreambleTransaction' => 'PhabricatorEditEngineTransactionType',
|
||||||
'PhabricatorEditEngineProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
'PhabricatorEditEngineProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||||
|
|
|
@ -30,6 +30,7 @@ final class AphrontRequest extends Phobject {
|
||||||
private $controller;
|
private $controller;
|
||||||
private $uriData = array();
|
private $uriData = array();
|
||||||
private $cookiePrefix;
|
private $cookiePrefix;
|
||||||
|
private $submitKey;
|
||||||
|
|
||||||
public function __construct($host, $path) {
|
public function __construct($host, $path) {
|
||||||
$this->host = $host;
|
$this->host = $host;
|
||||||
|
@ -914,5 +915,19 @@ final class AphrontRequest extends Phobject {
|
||||||
return $future;
|
return $future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateEphemeralCookies() {
|
||||||
|
$submit_cookie = PhabricatorCookies::COOKIE_SUBMIT;
|
||||||
|
|
||||||
|
$submit_key = $this->getCookie($submit_cookie);
|
||||||
|
if (strlen($submit_key)) {
|
||||||
|
$this->clearCookie($submit_cookie);
|
||||||
|
$this->submitKey = $submit_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubmitKey() {
|
||||||
|
return $this->submitKey;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ final class AphrontApplicationConfiguration
|
||||||
$request->setApplicationConfiguration($this);
|
$request->setApplicationConfiguration($this);
|
||||||
$request->setCookiePrefix($cookie_prefix);
|
$request->setCookiePrefix($cookie_prefix);
|
||||||
|
|
||||||
|
$request->updateEphemeralCookies();
|
||||||
|
|
||||||
return $request;
|
return $request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
|
||||||
'session/downgrade/'
|
'session/downgrade/'
|
||||||
=> 'PhabricatorAuthDowngradeSessionController',
|
=> 'PhabricatorAuthDowngradeSessionController',
|
||||||
'enroll/' => array(
|
'enroll/' => array(
|
||||||
'(?:(?P<pageKey>[^/]+)/)?(?:(?P<formSaved>saved)/)?'
|
'(?:(?P<pageKey>[^/]+)/)?'
|
||||||
=> 'PhabricatorAuthNeedsMultiFactorController',
|
=> 'PhabricatorAuthNeedsMultiFactorController',
|
||||||
),
|
),
|
||||||
'sshkey/' => array(
|
'sshkey/' => array(
|
||||||
|
|
|
@ -63,6 +63,13 @@ final class PhabricatorCookies extends Phobject {
|
||||||
const COOKIE_INVITE = 'invite';
|
const COOKIE_INVITE = 'invite';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a workflow completion across a redirect-after-POST following a
|
||||||
|
* form submission. This can be used to show "Changes Saved" messages.
|
||||||
|
*/
|
||||||
|
const COOKIE_SUBMIT = 'phfrm';
|
||||||
|
|
||||||
|
|
||||||
/* -( Client ID Cookie )--------------------------------------------------- */
|
/* -( Client ID Cookie )--------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoutes() {
|
public function getRoutes() {
|
||||||
$panel_pattern = '(?:page/(?P<pageKey>[^/]+)/(?:(?P<formSaved>saved)/)?)?';
|
$panel_pattern = '(?:page/(?P<pageKey>[^/]+)/)?';
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'/settings/' => array(
|
'/settings/' => array(
|
||||||
|
|
|
@ -128,10 +128,6 @@ final class PhabricatorSettingsEditEngine
|
||||||
return PhabricatorPolicies::POLICY_ADMIN;
|
return PhabricatorPolicies::POLICY_ADMIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEffectiveObjectEditDoneURI($object) {
|
|
||||||
return parent::getEffectiveObjectViewURI($object).'saved/';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEffectiveObjectEditCancelURI($object) {
|
public function getEffectiveObjectEditCancelURI($object) {
|
||||||
if (!$object->getUser()) {
|
if (!$object->getUser()) {
|
||||||
return '/settings/';
|
return '/settings/';
|
||||||
|
@ -253,4 +249,16 @@ final class PhabricatorSettingsEditEngine
|
||||||
return parent::getValidationExceptionShortMessage($ex, $field);
|
return parent::getValidationExceptionShortMessage($ex, $field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newEditFormHeadContent(
|
||||||
|
PhabricatorEditEnginePageState $state) {
|
||||||
|
|
||||||
|
if ($state->getIsSave()) {
|
||||||
|
return id(new PHUIInfoView())
|
||||||
|
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
||||||
|
->appendChild(pht('Changes saved.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,6 @@ final class PhabricatorEmailPreferencesSettingsPanel
|
||||||
|
|
||||||
$form_box = id(new PHUIObjectBoxView())
|
$form_box = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('Email Preferences'))
|
->setHeaderText(pht('Email Preferences'))
|
||||||
->setFormSaved($request->getStr('saved'))
|
|
||||||
->setFormErrors($errors)
|
->setFormErrors($errors)
|
||||||
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
|
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
|
||||||
->setForm($form);
|
->setForm($form);
|
||||||
|
|
|
@ -206,7 +206,6 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
|
||||||
$algo_box = $this->newBox(pht('Password Algorithms'), $properties);
|
$algo_box = $this->newBox(pht('Password Algorithms'), $properties);
|
||||||
$form_box = id(new PHUIObjectBoxView())
|
$form_box = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('Change Password'))
|
->setHeaderText(pht('Change Password'))
|
||||||
->setFormSaved($request->getStr('saved'))
|
|
||||||
->setFormErrors($errors)
|
->setFormErrors($errors)
|
||||||
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
|
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
|
||||||
->setForm($form);
|
->setForm($form);
|
||||||
|
|
|
@ -1036,9 +1036,13 @@ abstract class PhabricatorEditEngine
|
||||||
$fields = $this->buildEditFields($object);
|
$fields = $this->buildEditFields($object);
|
||||||
$template = $object->getApplicationTransactionTemplate();
|
$template = $object->getApplicationTransactionTemplate();
|
||||||
|
|
||||||
|
$page_state = new PhabricatorEditEnginePageState();
|
||||||
|
|
||||||
if ($this->getIsCreate()) {
|
if ($this->getIsCreate()) {
|
||||||
$cancel_uri = $this->getObjectCreateCancelURI($object);
|
$cancel_uri = $this->getObjectCreateCancelURI($object);
|
||||||
$submit_button = $this->getObjectCreateButtonText($object);
|
$submit_button = $this->getObjectCreateButtonText($object);
|
||||||
|
|
||||||
|
$page_state->setIsCreate(true);
|
||||||
} else {
|
} else {
|
||||||
$cancel_uri = $this->getEffectiveObjectEditCancelURI($object);
|
$cancel_uri = $this->getEffectiveObjectEditCancelURI($object);
|
||||||
$submit_button = $this->getObjectEditButtonText($object);
|
$submit_button = $this->getObjectEditButtonText($object);
|
||||||
|
@ -1069,6 +1073,8 @@ abstract class PhabricatorEditEngine
|
||||||
|
|
||||||
$validation_exception = null;
|
$validation_exception = null;
|
||||||
if ($request->isFormOrHisecPost() && $request->getBool('editEngine')) {
|
if ($request->isFormOrHisecPost() && $request->getBool('editEngine')) {
|
||||||
|
$page_state->setIsSubmit(true);
|
||||||
|
|
||||||
$submit_fields = $fields;
|
$submit_fields = $fields;
|
||||||
|
|
||||||
foreach ($submit_fields as $key => $field) {
|
foreach ($submit_fields as $key => $field) {
|
||||||
|
@ -1154,6 +1160,8 @@ abstract class PhabricatorEditEngine
|
||||||
|
|
||||||
$field->setControlError($message);
|
$field->setControlError($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$page_state->setIsError(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($this->getIsCreate()) {
|
if ($this->getIsCreate()) {
|
||||||
|
@ -1252,6 +1260,17 @@ abstract class PhabricatorEditEngine
|
||||||
$box_header->addActionLink($action_button);
|
$box_header->addActionLink($action_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$request_submit_key = $request->getSubmitKey();
|
||||||
|
$engine_submit_key = $this->getEditEngineSubmitKey();
|
||||||
|
|
||||||
|
if ($request_submit_key === $engine_submit_key) {
|
||||||
|
$page_state->setIsSubmit(true);
|
||||||
|
$page_state->setIsSave(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$head = $this->newEditFormHeadContent($page_state);
|
||||||
|
$tail = $this->newEditFormTailContent($page_state);
|
||||||
|
|
||||||
$box = id(new PHUIObjectBoxView())
|
$box = id(new PHUIObjectBoxView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setHeader($box_header)
|
->setHeader($box_header)
|
||||||
|
@ -1259,14 +1278,11 @@ abstract class PhabricatorEditEngine
|
||||||
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
|
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
|
||||||
->appendChild($form);
|
->appendChild($form);
|
||||||
|
|
||||||
// This is fairly questionable, but in use by Settings.
|
|
||||||
if ($request->getURIData('formSaved')) {
|
|
||||||
$box->setFormSaved(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = array(
|
$content = array(
|
||||||
|
$head,
|
||||||
$box,
|
$box,
|
||||||
$previews,
|
$previews,
|
||||||
|
$tail,
|
||||||
);
|
);
|
||||||
|
|
||||||
$view = new PHUITwoColumnView();
|
$view = new PHUITwoColumnView();
|
||||||
|
@ -1291,14 +1307,34 @@ abstract class PhabricatorEditEngine
|
||||||
return $page;
|
return $page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newEditFormHeadContent(
|
||||||
|
PhabricatorEditEnginePageState $state) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newEditFormTailContent(
|
||||||
|
PhabricatorEditEnginePageState $state) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected function newEditResponse(
|
protected function newEditResponse(
|
||||||
AphrontRequest $request,
|
AphrontRequest $request,
|
||||||
$object,
|
$object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
|
|
||||||
|
$submit_cookie = PhabricatorCookies::COOKIE_SUBMIT;
|
||||||
|
$submit_key = $this->getEditEngineSubmitKey();
|
||||||
|
|
||||||
|
$request->setTemporaryCookie($submit_cookie, $submit_key);
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())
|
return id(new AphrontRedirectResponse())
|
||||||
->setURI($this->getEffectiveObjectEditDoneURI($object));
|
->setURI($this->getEffectiveObjectEditDoneURI($object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getEditEngineSubmitKey() {
|
||||||
|
return 'edit-engine/'.$this->getEngineKey();
|
||||||
|
}
|
||||||
|
|
||||||
private function buildEditForm($object, array $fields) {
|
private function buildEditForm($object, array $fields) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
$controller = $this->getController();
|
$controller = $this->getController();
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorEditEnginePageState
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $isCreate;
|
||||||
|
private $isSubmit;
|
||||||
|
private $isError;
|
||||||
|
private $isSave;
|
||||||
|
|
||||||
|
public function setIsCreate($is_create) {
|
||||||
|
$this->isCreate = $is_create;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsCreate() {
|
||||||
|
return $this->isCreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsSubmit($is_submit) {
|
||||||
|
$this->isSubmit = $is_submit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsSubmit() {
|
||||||
|
return $this->isSubmit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsError($is_error) {
|
||||||
|
$this->isError = $is_error;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsError() {
|
||||||
|
return $this->isError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsSave($is_save) {
|
||||||
|
$this->isSave = $is_save;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsSave() {
|
||||||
|
return $this->isSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,6 @@ final class PHUIObjectBoxView extends AphrontTagView {
|
||||||
private $background;
|
private $background;
|
||||||
private $tabGroups = array();
|
private $tabGroups = array();
|
||||||
private $formErrors = null;
|
private $formErrors = null;
|
||||||
private $formSaved = false;
|
|
||||||
private $infoView;
|
private $infoView;
|
||||||
private $form;
|
private $form;
|
||||||
private $validationException;
|
private $validationException;
|
||||||
|
@ -75,19 +74,6 @@ final class PHUIObjectBoxView extends AphrontTagView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setFormSaved($saved, $text = null) {
|
|
||||||
if (!$text) {
|
|
||||||
$text = pht('Changes saved.');
|
|
||||||
}
|
|
||||||
if ($saved) {
|
|
||||||
$save = id(new PHUIInfoView())
|
|
||||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
|
||||||
->appendChild($text);
|
|
||||||
$this->formSaved = $save;
|
|
||||||
}
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addTabGroup(PHUITabGroupView $view) {
|
public function addTabGroup(PHUITabGroupView $view) {
|
||||||
$this->tabGroups[] = $view;
|
$this->tabGroups[] = $view;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -324,7 +310,6 @@ final class PHUIObjectBoxView extends AphrontTagView {
|
||||||
$header,
|
$header,
|
||||||
$this->infoView,
|
$this->infoView,
|
||||||
$this->formErrors,
|
$this->formErrors,
|
||||||
$this->formSaved,
|
|
||||||
$exception_errors,
|
$exception_errors,
|
||||||
$this->form,
|
$this->form,
|
||||||
$this->tabGroups,
|
$this->tabGroups,
|
||||||
|
|
Loading…
Reference in a new issue