1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 00:32:42 +01:00

Add passthru to AphrontRequest

Summary:
For transaction interfaces, I want to prompt the user when they take an action that has no effect, e.g.:

  Action Has No Effect

  You can not close this task, because someone else has already closed it.

  Do you want to post your comment anyway?

        [Cancel] [Post Comment]

We already do this for Differential, but it's all hard-coded. T912 is an open task for fixing this for Maniphest.

To do this in a general way, I want to embed the entire request in the dialog as hidden inputs, then add a "__continue__" key and resubmit the form. The endpoint will read this key the second time through and apply what effects it can (e.g., just post a comment).

This adds a mechanism for getting all the request data, minus "magic" like __dialog__ and __csrf__. We need to jump through some hoops because of how PHP encodes arrays.

Test Plan: Ran unit tests, built "no effect" dialogs on top of this.

Reviewers: btrahan, vrana

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T912, T2104

Differential Revision: https://secure.phabricator.com/D4158
This commit is contained in:
epriestley 2012-12-11 17:27:02 -08:00
parent c970f7e89c
commit 4fe09a0ac7
2 changed files with 107 additions and 0 deletions

View file

@ -17,6 +17,7 @@ final class AphrontRequest {
const TYPE_FORM = '__form__';
const TYPE_CONDUIT = '__conduit__';
const TYPE_WORKFLOW = '__wflow__';
const TYPE_CONTINUE = '__continue__';
private $host;
private $path;
@ -334,4 +335,58 @@ final class AphrontRequest {
return true;
}
public function isContinueRequest() {
return $this->isFormPost() && $this->getStr('__continue__');
}
/**
* Get application request parameters in a flattened form suitable for
* inclusion in an HTTP request, excluding parameters with special meanings.
* This is primarily useful if you want to ask the user for more input and
* then resubmit their request.
*
* @return dict<string, string> Original request parameters.
*/
public function getPassthroughRequestParameters() {
$data = self::flattenData($this->getRequestData());
// Remove magic parameters like __dialog__ and __ajax__.
foreach ($data as $key => $value) {
if (strncmp($key, '__', 2)) {
unset($data[$key]);
}
}
return $data;
}
/**
* Flatten an array of key-value pairs (possibly including arrays as values)
* into a list of key-value pairs suitable for submitting via HTTP request
* (with arrays flattened).
*
* @param dict<string, wild> Data to flatten.
* @return dict<string, string> Flat data suitable for inclusion in an HTTP
* request.
*/
public static function flattenData(array $data) {
$result = array();
foreach ($data as $key => $value) {
if (is_array($value)) {
foreach (self::flattenData($value) as $fkey => $fvalue) {
$fkey = '['.preg_replace('/(?=\[)|$/', ']', $fkey, $limit = 1);
$result[$key.$fkey] = $fvalue;
}
} else {
$result[$key] = (string)$value;
}
}
ksort($result);
return $result;
}
}

View file

@ -79,4 +79,56 @@ final class AphrontRequestTestCase extends PhabricatorTestCase {
}
}
public function testFlattenRequestData() {
$test_cases = array(
array(
'a' => 'a',
'b' => '1',
'c' => '',
),
array(
'a' => 'a',
'b' => '1',
'c' => '',
),
array(
'x' => array(
0 => 'a',
1 => 'b',
2 => 'c',
),
),
array(
'x[0]' => 'a',
'x[1]' => 'b',
'x[2]' => 'c',
),
array(
'x' => array(
'y' => array(
'z' => array(
40 => 'A',
50 => 'B',
'C' => 60,
),
),
),
),
array(
'x[y][z][40]' => 'A',
'x[y][z][50]' => 'B',
'x[y][z][C]' => '60',
),
);
for ($ii = 0; $ii < count($test_cases); $ii += 2) {
$input = $test_cases[$ii];
$expect = $test_cases[$ii + 1];
$this->assertEqual($expect, AphrontRequest::flattenData($input));
}
}
}