drupal_get_destination, pager and tablesort array handling

matt westgate - January 23, 2004 - 16:07
Project:Drupal
Version:4.7.x-dev
Component:base system
Category:support request
Priority:normal
Assigned:Unassigned
Status:closed
Description

Tablesort tries its best to rebuild the URI from which it was called. The problem is when $_REQUEST holds arrays such as $edit. Tablesort will process this as:

example.com?edit=Array

This can be problematic since many module conditions rely on whether or not the $edit array is set. My patch disallows tablesort to append "array $_REQUEST variables" to it's new query string, since it serves no purpose.

AttachmentSize
tablesort_and_REQUEST_arrays.patch652 bytes

#1

Dries - January 31, 2004 - 11:00

I'd like to hear Moshe's take on this as I'm not sure this is the proper fix. I'd think that the tablesort code knows exactly what fields it should add to the URL.

#2

moshe weitzman - January 31, 2004 - 13:24

I don't think this is the proper fix either. For example,

- browse to the Drupal.org issue search page.
- press Search button
- On the results page, look at the links in the table header and the pager. The query state gets passed along by tablesort. This patch would break this behavior.

Changing Status to 'Active', since this patch shouldn't be applied without modification.

#3

matt westgate - January 31, 2004 - 17:01

I'm pretty sure the project system is hacking the request object just like I am before sending it to tablesort:

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST' && is_array($_POST['edit'])) {
  foreach (
$_POST['edit'] as $key => $value) {
    if (!empty(
$value) && in_array($key, $fields)) {
     
$query->$key = $value;
     
$_POST[$key] = is_array($value) ? implode(',', $value) : $value;
    }
  }
  unset(
$_POST['edit'], $_POST['op']);
}
?>

Basically, we're both moving the edit array into separate name / value pairs and then destroying it before handing it off to tablesort. I don't understand what the benefit is of passing an array into a GET query string since the key/value pairs aren't retained. We could set an option in tablesort to ask it to decompose the array into it's key/value pairs for us and pass them back in the query string. For example an edit array such as:

<?php
$edit
['a'] => 1
$edit
['b'] => 2

would have a query string of
:

&
edit_a=1&edit_b=2

instead of the currently meaningless
:

&
edit=Array
?>

#4

Gábor Hojtsy - January 31, 2004 - 17:12

You mean:

<?php
$edit
['a'] => 1
$edit
['b'] => 2

would have a query string of
:

&
edit[a]=1&edit[b]=2

instead of the currently meaningless
:

&
edit=Array
?>

This reuses the wonderful array handling provided by PHP (as long as nested arrays are handled properly).

#5

Kjartan - February 1, 2004 - 15:02

Indeed project module hacks the request data. I don't really like this as a general fix as it is a hack, but for now it is the best way. I dont think the original patch will break this as it just ignors arrays(). Project module loops through the array and converts them to normal array string elements; $_POST['edit']['status'] = value becomes $_POST['status'] = value, then I think I unset the edit array to clean up the urls.

#6

moshe weitzman - February 5, 2004 - 13:42

After feedback from Matias and Kjartan, I now consider this patch to be desireable

#7

killes@www.drop.org - August 12, 2004 - 15:10

Patch doesn't apply anymore.

#8

matt westgate - October 8, 2004 - 04:14
Version:» 4.5.0-rc

This patch resolves the hacks project module and the ecommerce package use to rebuild the $_REQUEST arrays when implementing paged resultsets and/or tablesorting. I know some other developers have ran into this bug as well usually after form submissions.

Tablesort will now fully handle multi-dimensional $_REQUEST arrays and convert them to a valid querystring and preserve user-submitted data.

AttachmentSize
tablesort_and_REQUEST_arrays_0.patch2.55 KB

#9

moshe weitzman - October 8, 2004 - 04:34

very nicely done ... bonus points for converting pager to use the new array2uri() function ... tablesort's poor handling of arrays frustrated me in a recent client project.

this will simplify almost all cases where a form post leads to a sortable "results" table, such as in project.module.

#10

matt westgate - October 15, 2004 - 14:01
Version:4.5.0-rc»

Moved the new array2uri function to live with its array2* counterparts in common.inc and cleaned up the function documentation a wee bit.

AttachmentSize
tablesort_and_REQUEST_arrays_1.patch2.99 KB

#11

moshe weitzman - December 21, 2004 - 16:43

this is a nice code consolidation IMO

#12

Dries - December 22, 2004 - 10:50

This patch adds more code than it removes.

That aside, it would be nice if the function's PHPdoc comments were extended. Looking at the patch, it is easy to see how/when this function is being used, but reading the PHPdoc comments it is not. I'd add a simple example and some context information. Also, it is unclear what $current_key is used for without inspecting the function's code. For readability, maybe rename $array to $attributes? If that makes sense, maybe rename 'array2uri' to 'attributes2uri'? Which makes me wonder: how does this stack with the existing drupal_attributes() function?

Do we really need the recursive array handling?

#13

killes@www.drop.org - March 13, 2005 - 20:25

Doesn't apply anymore.

#14

matt westgate - July 12, 2005 - 22:49
Status:active» patch (code needs review)

Updating this patch since I've been bitten by this issue again. The new array2uri() function really shines for tablesorting and/or paging search results after a $_POST request. The only coding change involved for developers is to use $_REQUEST['edit'] and $_REQUEST['op'] instead of $_POST['edit'] and $_POST['op'] within their function callback where this functionality is desired. As an added benefit these pages could also be bookmarked.

Dries, I've updated the documentation to hopefully makes things a little easier to understand. I don't think renaming the $array variable to $attributes or calling the function attributes2uri instead of array2uri would make the function and its purpose more transparent. It's not used for HTML attributes like the drupal_attributes function, but rather for form submitted data (e.g., the $edit array) that needs to be tablesorted/paged.

Project module and the ecommerce package still currently hack the $_POST vars to circumvent this problem. Not sure if other modules are also doing this.

AttachmentSize
tablesort_and_REQUEST_arrays_2.patch3.19 KB

#15

Steven - July 15, 2005 - 16:25

If we're going to have an array2uri() function, we might as well add urlencode() calls in there... this would centralize a lot of existing urlencode() calls, I think.

#16

Dries - July 18, 2005 - 13:51

Not going to commit this yet.

1. Let's wait for Mathias' feedback on Steven's comment.

2. I suggest using query or query_string, not querystring.

3. It is not clear why this can't be part of drupal_attributes($array)? If there is an important difference, it would be nice to make that clear in the PHPdoc comments.

#17

matt westgate - July 22, 2005 - 03:18

Agreed with Steven that this is a logical place to urlencode values. The patch has been updated accordingly.

There was some IRC discussion as to whether this function should be called drupal_uri, drupal2uri or just plain ol' array2uri. I think since this code doesn't make any Drupal specific function calls, array2uri is the best name.

Which brings up a whole new issue for a whole new thread: Perhaps drupal_attributes should be renamed array2attributes?

AttachmentSize
tablesort_and_REQUEST_arrays_3.patch3.23 KB

#18

Steven - July 25, 2005 - 08:50

Dries: drupal_attributes is used for outputting attributes of an HTML tag. This patch is about putting data into the query (GET) part of an URL. They are completely different.

mathias: couldn't we use this function in drupal_get_destination() as well ?

#19

Steven - July 25, 2005 - 08:56

By the way, what exactly is the point of this check:

+        if (!strstr($query_string, $key_as_uri)) {
+          $query_string .= '&'. $key_as_uri. '='. urlencode($value);
+        }

It seems to be there to avoid duplicates, but there is no similar check below. If it is required, it needs a comment to explain it.

#20

matt westgate - July 26, 2005 - 17:02

drupal_get_destination() now uses array2uri(). I discovered that the use of strstr(), was to compensate for the function calling itself at times when it didn't need to. This bug is fixed and strstr() is removed. Good catch!

AttachmentSize
tablesort_and_REQUEST_arrays_4.patch3.39 KB

#21

matt westgate - July 26, 2005 - 18:57

Allow a string, NULL or array to be passed into array2uri(), making life easier on developers per Steven's feedback on IRC.

AttachmentSize
tablesort_and_REQUEST_arrays_5.patch3.58 KB

#22

moshe weitzman - July 28, 2005 - 18:15

thanks for updating this patch, matt. looks good to me. i did not test it yet though.

#23

Steven - July 29, 2005 - 18:40

Err, passing strings to array2uri() is pretty non-sensical and not at all what I meant. I'll see if I can make a better patch later :P.

#24

moshe weitzman - August 8, 2005 - 02:58
Status:patch (code needs review)» patch (code needs work)

#25

matt westgate - August 13, 2005 - 18:37
Status:patch (code needs work)» patch (code needs review)

* Further code optimizations.
* Made drupal_get_destination() make better use of array2uri().

AttachmentSize
tablesort_and_REQUEST_arrays_6.patch4 KB

#26

rbrooks00 - August 13, 2005 - 18:53

+1 for this patch

Having the built in ability to sort tabular data is incredibly useful.

#27

matt westgate - September 5, 2005 - 18:33
Status:patch (code needs review)» patch (reviewed & tested by the community)

Syncing with HEAD and setting to commit-worthy as I've been using it in production mode for awhile now.

AttachmentSize
tablesort_and_REQUEST_arrays_7.patch4.02 KB

#28

Bèr Kessels - September 5, 2005 - 19:35

just curious: How does this patch break (the -) or interfere with the AJAX table sorting patch?

#29

matt westgate - September 5, 2005 - 20:55

The two patches don't conflict as far as I can see. This patch introduces a function to convert arrays (such as the $edit array) to expanded querystrings.

A byproduct of this function is you can now have tablesortable and paged resultsets after submitting a POST form. Project module and the ecommerce package are currently using hacks to circumvent this.

#30

andremolnar - November 10, 2005 - 16:51

+1 Tested and functioning exactly as described.
+1 Because this is a life-saver - and couldn't have come at a better time. I just recently started wrestling wth ?edit=Array and couldn't believe that there wasn't better handling for that inside of the drupal framework.

#31

moshe weitzman - November 13, 2005 - 17:18

CVS reviewers - is there anything holding up this patch? please say so so we can fix it.

#32

m3avrck - November 21, 2005 - 15:30

Yes patch applies cleanly and I like how it works. Instead of the useless array() now I have some variables I can work with :-D Please commit this, my module is broken without it and it seems this problem has existed for way too long! I can see certainly how project.module could benefit right away...

#33

Steven - November 29, 2005 - 01:13
Status:patch (reviewed & tested by the community)» patch (code needs work)

This patch causes drupal_get_destination() to emit a leading ampersand, which ends up becoming '?&destination=' when used in a clean URL. Typically the leading separator is added by url() or something similar, so it should IMO not be part of array2uri()'s final result.

This would allow simplifying code too:

  $query_string = array2uri($attributes);
  $url = url($q, 'page='. implode($page_new, ',') . $query_string);

to
$attributes['page'] = implode($page_new, ',');
  $url = url($q, array2uri($attributes));

I also don't see the point of allowing non-arrays to be passed to array2uri(): it doesn't seem to be used anywhere in this patch anyhow.

#34

eberts - March 27, 2006 - 00:49
Title:Tablesort and $_REQUEST array handling» drupal_get_destination, pager and tablesort array handling
Assigned to:matt westgate» eberts
Status:patch (code needs work)» patch (code needs review)

Updating to HEAD, This is a combination of http://drupal.org/node/54305 and this issue.

Maybe there are other places where drupal_query_string_encode() could be used to simplify code.

AttachmentSize
drupal_63.patch4.5 KB

#35

eberts - March 27, 2006 - 00:56

Note: This also makes the pager work with tablesort without losing the sort/order.

#36

moshe weitzman - April 10, 2006 - 20:07
Priority:normal» critical

we need to get this in. recent changes to drupal_get_destination() make it fail when there are arrays in $_GET like edit[og_groups][]=12.

other people are experiencing this problem too: http://drupal.org/node/54305

i will review the patch ASAP.

#37

chx - April 11, 2006 - 19:05
Status:patch (code needs review)» patch (code needs work)

Method of testing: I created a PHP block which has a very minimalistic form, with #redirect FALSE. Before and after patch I still get http://localhost/head/?q=admin&edit=Array&op=v . I am looking into it.

#38

chx - April 11, 2006 - 19:07
Assigned to:eberts» chx

I take over.

#39

chx - April 11, 2006 - 20:00
Status:patch (code needs work)» patch (code needs review)

This is somewhat closer. theme_pager_link needed patching. I am by far not 100% sure of this patch.

AttachmentSize
drupal_query_string_encode.patch4.84 KB

#40

chx - April 12, 2006 - 00:31
Assigned to:chx» Anonymous

This is a lot cleaner of the same.

AttachmentSize
drupal_query_string_encode_0.patch4.97 KB

#41

killes@www.drop.org - April 12, 2006 - 11:12

Moshe? Matt?

#42

chx - April 12, 2006 - 14:45

moshe discovered that some debug code got into the patch.

AttachmentSize
drupal_query_string_encode_1.patch4.57 KB

#43

moshe weitzman - April 12, 2006 - 17:15
Status:patch (code needs review)» patch (reviewed & tested by the community)

I made a tiny change to chx' patch in drupal_get_destination() because the path needs urlencoding too. i tested table sort, pager, destinations using arrays on querystring. seems to work.

AttachmentSize
dest.patch5.4 KB

#44

Steven - April 12, 2006 - 19:08
Status:patch (reviewed & tested by the community)» patch (code needs work)
  • Right now, $exclude only applies to top level items, and it is set to array() for nested calls. But, if we move the exclude check to after where $parent is added to $key, we can exclude any item e.g; by excluding edit[foo].
  • I don't see any reason not to urlencode. The lack of urlencode in theme('pager_link') is IMO a bad omission. $parameters is mostly passed in from tablesort_pager() which comes from $_GET or $_POST. This means, non-urlencoded values.

Other than that, I like the patch htough.

#45

Steven - April 12, 2006 - 19:22
Status:patch (code needs work)» patch (code needs review)

Patch for the above two things.

AttachmentSize
dest_0.patch5.23 KB

#46

chx - April 12, 2006 - 19:35
Status:patch (code needs review)» patch (code needs work)

I again got a link like http://localhost/head/?q=admin&edit%5Bt%5D=t&edit%5Bform_id%5D=t&op=v&pa... if clicked the next page has links like http://localhost/head/?q=admin&edit%5Bt%5D=t&edit%5Bform_id%5D=t&op=v&pa... :(

#47

Steven - April 12, 2006 - 20:34
Status:patch (code needs work)» patch (code needs review)

Urlencoding issues asside, ebert's patch was still bogus.

Before all these patches, tablesort_pager() would be used to properly fill in theme('pager')'s $parameters argument with the GET/POST data. Whoever called theme('pager') was expected to use tablesort_pager() to pass these arguments along and keep the page state (such as tablesorting) when moving around.

After his patch, pager_query() remembers the query URL on its own and retrieves it in theme('pager'), meaning all query arguments are already there. This means that anyone who uses tablesort_pager() will be passing in duplicate data.

Having pager remember its own URL is no problem. There is no reason why the pager should know about tablesort or receive special treatment. Tablesort and pager both care only about their own variables, there is no need for an artificial passthrough. The way it is in the latest patch, we have a redundant API sitting around.

I'm all for removing tablesort_pager(), but then a lot of contrib modules need to be updated. But this is a pretty mindless upgrade, as all tablesort_pager() calls in contrib are the 5th argument to theme('pager', ...) (that shows you how weird it is to have an API function for this, if it's only ever used in one way).

So the attached patch addresses this. Both pager and tablesort now construct their own query string, doing so from $_REQUEST without the $_COOKIE data (anything that needs to be kept, is kept). If you need it, you can still pass in $parameters to theme('pager'), but this should be very rare. Anyone who uses only tablesort_pager() can now drop that argument instead.

AttachmentSize
drupal_get_query_string.patch10.34 KB

#48

Steven - April 12, 2006 - 20:42

Sorry, that was not the final one.

AttachmentSize
drupal_get_query_string_0.patch10.93 KB

#49

chx - April 12, 2006 - 21:14
Status:patch (code needs review)» patch (reviewed & tested by the community)

Now I do not get ugly URLs...

#50

moshe weitzman - April 13, 2006 - 02:37

I gave this a thorough exercising and all worked well. I did notice on peculiarity when passing array notation in the querystring. Here is how I reproduce it

- enable the switch user block of devel.module (and assure you have its permission). this is just useful because it appends a drupal_get_destination in member link.
- now browse to an url like node?edit[og_groups][]=66. i know that url is meaningless - it is just illustrative.
- notice the href in the switch user links now have a 0 key in an element: edit[og_groups][0]=66.

This peculiarity doesn't break anything that I could find so I say RTBC.

#51

killes@www.drop.org - April 13, 2006 - 08:25
Status:patch (reviewed & tested by the community)» fixed

applied

#52

eberts - April 13, 2006 - 09:43
Status:fixed» patch (code needs review)

Some clea
t.

AttachmentSize
drupal_64.patch2.08 KB

#53

killes@www.drop.org - April 13, 2006 - 10:01

Hm, what is this new patch about?

#54

Steven - April 13, 2006 - 12:57
Status:patch (code needs review)» fixed

eberts: Please don't post random patches without explanation.

The movement of the ampersand in my earlier patch ensures better consistency between the pager and tablesort code and makes more sense. The ampersand only applies when you combine the query string with existing arguments. It is not an intrinsic part of it.

I'll apply the urlencode change.

#55

eberts - April 13, 2006 - 18:09

I stupid managed to delete half of the text while submitting, sorry about it.

But still I don't see the need of the additional apersand if there is no query string appended anyway. It's just not tasty.

#56

ultraBoy - April 20, 2006 - 12:27
Status:fixed» active

With today's cvs I get pager links such as "
http://localhost/alongtail05/?q=advice/content-movie&page=12&edit[quicksearch_title]=Lost+Highway+-+1997&edit[form_id]=content-movie_autocomplete&op=go&nid=100&vote=2"

I'm sorry, you may kick me if it's wrong issue. :)

#57

ultraBoy - April 20, 2006 - 13:06

It happens only when the form is submitted to a page with theme_pager ( $output .= theme('pager', NULL, $ipp, 0); )

Form looks like this:

<?php
                  $form
['#action'] = url('advice/" . $type . "');
                 
$form['#redirect'] = FALSE;
                 
$form['quicksearch_title'] = array(
                   
'#type' => 'textfield',
                   
'#title' => t(".$type."),
                   
'#size' => 20,
                   
'#maxlength' => 100,
                   
'#value' => NULL,
                   
'#autocomplete_path' => 'advice/autocomplete/".$type."',
                    );
                 
$form['submit'] = array('#type' => 'submit', '#value' => t('go'));
                  return
drupal_get_form('".$type."_autocomplete', $form, 'advice/content-movie/all');
?>
(it looks a bit crazy just cause this form is 'eval'ed)

#58

Steven - April 22, 2006 - 10:53
Status:active» by design

This is by design. The proper way of dealing with this is to have a drupal_goto() to a clean URL after submitting the form (like search does sort of).

#59

ultraBoy - April 22, 2006 - 15:51
Category:bug report» support request

But, if I use drupal_goto(), I loose all $_POST variables, isn't it?

#60

ultraBoy - April 22, 2006 - 16:08
Priority:critical» normal
Status:by design» active

#61

magico - December 28, 2006 - 19:30
Version:» 4.7.x-dev
Status:active» closed
 
 

Drupal is a registered trademark of Dries Buytaert.