I've got a problem where clicking a table heading in a View to click-sort it causes any value that has already been set up in an exposed filter to be dropped. This means that click-sorting effectively returns the View to its default state with none of the user's filters selected - i.e. users can't sort after they've added a filter. The View is a Block View using AJAX, but I've tried with a Page view and discovered that the problem is specific to AJAX: a Page View with AJAX has the same symptoms, but a Page View without AJAX works perfectly.

The view is set up as follows:

  • I have a content type with a CCK field of type "number", called Height.
  • The View contains all nodes of this content type, and includes the Height field, which is set to be click-sortable (the view is formatted as a table).
  • The View has an exposed filter on the CCK Height field, and the user is allowed to choose from all of the standard different ways of filtering numbers (>, >=, <, <=, ==, between, etc.)
  • The View is a Block View, with AJAX enabled.

I've done some investigation into this, and discovered that the destination of the click-sortable link changes subtly when you attempt to click sort after a filter has been applied. It looks to me like it is being URL-encoded twice, which presumably means that Views can no longer decode it correctly, explaining why the exposed filter settings are lost. Unfortunately, I've never worked with the Views codebase before, so I've no idea where to continue with debugging this. If somebody could point me in the right direction, I'd be happy to try and debug further (bearing in mind that this is 6.x-2.16, which I guess isn't being worked on that much any more!)

When the page containing the block View is first loaded, the click-sortable Height link claims to have the following query string (obtained by cutting-and-pasting the link from the browser):
...?order=field_height_value&sort=desc&height_op=%3E%3D&height%5Bvalue%5D=&height%5Bmin%5D=&height%5Bmax%5D=
The relevant bit to look at is "&height%5Bvalue%5D=&", which, after URL-decoding, is saying that there is no value for "height[value]", as would be expected at this stage before applying a filter.

After applying a filter (e.g. height <= 10), the query string changes to:
...?order=field_height_value&sort=desc&height_op=%3C%3D&height%5Bvalue%5D=10&height%5Bmin%5D=&height%5Bmax%5D=&...
(additional Views stuff that has been appended is not shown). Again, the "&height%5Bvalue%5D=10&" looks sensible, claiming that height[value] should now be 10.

After clicking on the Height link to to reverse the click-sort, the filter is incorrectly removed, and all nodes are shown. The query string has now changed to:
...?order=field_height_value&sort=asc&js=1&height_op=%3C%3D&height%255Bvalue%255D=10&.......&height%5Bvalue%5D=&height%5Bmin%5D=&height%5Bmax%5D=
(where the dots in the middle represent the additional Views stuff that I removed before as well).

You can see now that we have "&height%255Bvalue%255D=10&", which has been double-URL-encoded, and so after URL decoding, gives "height%5Bvalue%5D=10", which I presume Views does not understand. It looks like Views has then thought it hasn't been given any value for height[value] (and other parameters with square brackets in them), and re-appended empty values to the end of the query string, which are correctly only URL-encoded once.

To me this shows exactly what the problem is, but, as I said, I'm not familiar enough with Views to know where to start looking for this. If anybody could point me in the right direction, telling me where the URL-decoding/encoding during AJAX processing occurs, I'd be happy to investigate further myself.

CommentFileSizeAuthor
#1 views_clicksort_ajax.patch534 bytespenguin25
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

penguin25’s picture

FileSize
534 bytes

I think I might have found the cause of this problem. I'd appreciate if somebody who knows the internals of Views could comment.

Firstly, by examining PHP's $_REQUEST variable, I realised that whenever you click a table heading to perform a tablesort with AJAX enabled, any key/value pairs that use the square bracket array notation (e.g. in my case, height[value]), are already incorrectly represented in $_REQUEST as height%5Bvalue%5D. Therefore, all of Views' PHP code is doing the correct thing, and URL-encoding this to height%255Bvalue%255D. This suggests the problem is in the JavaScript that is generating the HTTP request when the table headings are clicked (which also ties in with why I only see the problem when AJAX is enabled).

I traced through the JavaScript execution, and found where the "click" handlers for the table headers are generated, in Drupal.behaviors.ViewsAjaxView() in ajax_view.js. This code reads the query string of the original HTML link (so it can generate an HTTP request with the same parameters), via a call to Drupal.Views.parseQueryString(), which is found in base.js.

At the point where the query string is parsed, it is, of course, already URL encoded, because it's part of the HTML document. The parseQueryString() function looks fairly standard - it splits the key/value pairs at every "&" character, and then splits the key and value at the "=" character, and then builds an array with this data, using the following line of JavaScript:

args[pair[0]] = decodeURIComponent(pair[1].replace(/\+/g, ' '));

This, I think, is where the problem lies. The "args" array now has a key that is still URL-encoded, but a value that is not. If this gets sent as an HTTP request, it explains why the PHP $_REQUEST variable contains a key of height%5Bvalue%5D, instead of the correct height[value].

I've changed the line of code in question to read as follows:

args[decodeURIComponent(pair[0].replace(/\+/g, ' '))] = decodeURIComponent(pair[1].replace(/\+/g, ' '));

This has fixed my problem, and click-sorting with AJAX enabled keeps the current exposed filter settings.

I've attached a patch, against Views 6.x-2.16. As I said, I'd appreciate it if somebody who knows the codebase better than I do could verify this is a correct fix, and won't break anything else. Also, I've made no attempt to check if this problem is present in the 6.x-3.x and 7.x-3.x branches.

penguin25’s picture

Status: Active » Needs review
nterbogt’s picture

I'm not sure I'm the best person to be looking at this as I haven't really played with the views code either, but I've tested your patch and it appears to be working as expected for me.

I actually didn't realise I had this bug until I was looking for a report of another and came across this issue. Testing showed it existed in my site, and has now been fixed, so thanks :)

The issue I was experiencing is that my optional date filter on year was being lost when ajax was enabled. The problem is that the 'Any' option for year (labelled '-Year') actually has a value of ''. This means that the following JS threw out the key value pair on an ajax post.

if (pair[0] != 'q' && pair[1]) {

Just changing it as below solved the problem, but I don't know views well enough to know if this might cause other issues.

if (pair[0] != 'q') {

It would be great if we could get the views ajax guru to have a look at the 'parseQueryString' function for the above two issues. I'll try and roll a patch for both changes a little bit later, but if someone beats me to it that would be great.

ptoly’s picture

Patch works for me. Thanks!

timlie’s picture

I can reproduce the bug, patch #1 works for me.

yehmai’s picture

The patch #1 worked for me perfectly.

youngelpaso’s picture

I can also reproduce this issue and my own code change (which is identical essentially to the patch above) works. It'd be great to see this issue fully tested and hopefully closed. Good luck all!

kevin.dutra’s picture

Status: Needs review » Reviewed & tested by the community

By no means would I consider myself a Views AJAX guru, but I'm having trouble coming up with any condition where you wouldn't want the key of the key=value pairs decoded.

I've tested a bit with a few different AJAX-enabled Views that had various combinations of filters, both mutli and single, and have not encountered any issues thus far (with the patch from #1). Between that and the and the successful tests mentioned above, I'm marking this RTBC.

Thanks for the investigation and patch work penguin!

DamienMcKenna’s picture

DamienMcKenna’s picture

Version: 6.x-2.16 » 6.x-2.x-dev
efpapado’s picture

I had a similar problem on views 7.x-3.x, and I ended up on the exact same solution as nterbogt mentions on #3: Remove the pair[1] from the if-condition.
Please check this issue too: https://www.drupal.org/node/2646332 where I propose a patch for 7.x-3.x

izmeez’s picture

Patch applies without difficulty to the latest views 2.26 and is already RTBC.

Chris Matthews’s picture

Status: Reviewed & tested by the community » Closed (outdated)

The Drupal 6 branch is no longer supported, please check with the D6LTS project if you need further support. For more information as to why this issue was closed, please see issue #3030347: Plan to clean process issue queue