Passing search filters to multiple reports on a page with a sidestory of page name lessons learned (i.e. what not to name your Customer Portal page)

I recently learned a hard lesson about page names in Customer Portal. In my case, I was building a report search page that contained a bunch of filters and a Grid widget that showed the results of my report search. Not thinking twice about it, I named my page "search.php" so that its URL would be https://subdomain.custhelp.com/app/search.

This in itself didn't cause an immediate problem and I completed development and passed all of my unit and system tests. What I didn't know was that this page was going to be displayed inside of an iFrame on a different site. I typically don't recommend putting CP inside of an iFrame, but in this case I didn't have a say in the matter. This is when the problems started to occur. The customer reported that the search filters would work the first time they used them, but every subsequent search failed. Not only would the content not display, but their search filter selections would revert back to the selections from their first search.

I was baffled and spent a few hours very deep in CP javascript code trying to figure what was going on. I eventually gave up and decided that the HTML5 history and dynamic content update used by the search components was in some way incompatible with an iFrame. My initial solution to this problem (which is the real topic of this article) didn't work, but helped me identify the actual issue.

By default, a CP search page will do an AJAX based search and update the results dynamically. There is a trick that can make the page instead do a full page refresh. With this method the filters are passed to the page through URL parameters; My theory was that this would solve the iFrame issue.

This trick works by using the "report_page_url" widget attribute on the search filter widgets. The purpose of this attribute is to allow for search filters to be on one page, but the results shown on another. An example of this is the /app/home page; it has an answer keyword search field but upon submit the user is directed to the /app/answers/list page for the results. By using this parameter, it is possible to trick the CP framework into thinking that the "report_page_url" is referring to a different page than the current page and thus forcing a GET request instead of an AJAX content search.

But why would you ever want to do this? In the past, I've employed this trick for pages that have one set of filters that should be applied simultaneously to multiple Reports (via Grid or Multiline widgets). Typically a search filter is hard coded to a certain report via the "report_id" widget attribute. If the search was done through AJAX, only that single report's content would be updated. Filters from the URL behave differently; they apply to all Report widgets on the page. By tricking the page into doing a GET refresh, the new filter values are passed through the URL and applied to all Reports on the page.

This trick is accomplished by manipulating the "report_page_url" attribute value between pages views. All that's needed to fool the framework is to pass an extra bogus URL parameter appended to the actual page URL. The bogus parameter's value is toggled between page loads so that the current page URL is different than the URL supplied to the widget attributes. The code to accomplish this on a page with URL "/app/list" looks like this:


<?php 
$url = sprintf("/app/list/toggle/%d", !\RightNow\Utils\Url::getParameter('toggle'));
?>

<rn:widget path="search/FilterDropdown"
   filter_name="EventType"
   search_on_select="true"
   report_id="176" 
   report_page_url="#rn:php:$url#"
   per_page="30" />

In this example code, my page path is "/app/list" and I vary the URL by passing a bogus parameter of "toggle". When the page loads, I retrieve the existing toggle parameter then negate it's value using the "!" operator. PHP treats this as a boolean value so between each page load the value flops between 1 and 0. I construct the new URL with the negated "toggle" parameter then pass this value to the "report_page_url" attribute for each filter-type widget on your page. The net result of this is that RightNow thinks that the report_page_url value is different than the current page, so that when a search occurs it happens through a page load and not through AJAX. When the new search page loads the filters are passed through the URL and applied to each report on the page and the "report_page_url" is recalculated and bogus "toggle" value is negated.

Now lets get back to my original point; search was not working in an iFrame. I tried the above technique but it did not work. As it turns out search pages in iFrames already automatically use a page refresh instead of an AJAX update. And it was here that I accidentally found my issue. There are MAJOR problems if you name your page after one of the URL parameters used by CP. "Search" is one of those URL parameters, although it only comes into play if your search is done through a page refresh. By naming my page "search" and putting it in the iFrame, I ran into this condition! My initial development worked perfectly because the page was using AJAX searches and not in an iFrame.

Why does this occur? In the deep recesses of the CP framework when a search occurs the framework does the job of collecting all of the search filters on the page and constructing them into URL parameters. When it does this, it also adds some CP core parameters such as Search Type (st), sort params (sort), Keyword (kw), Page number (page) and a special one named "search" that only applies for page load type searches. They are added to the URL by a function that first looks for an existing URL parameter to update. If a match is found, the parameter value is updated instead of appended to the URL. This is why my page name of "search" failed. That function thought my page name was actually a search parameter and did a string replacement to the URL in a way that messed up the remaining filters.

To illustrate, my resulting URL should have looked like this:

/app/search/st/1/page/1/EventType/~any~/Language/~any~/search/1

But instead it was constructed like this:

/app/search/1/1/page/1/EventType/~any~/Language/~any~

My solution was simple. Rename my page to "list". There was no particular reason I had named the page "search" except for that it represented the functional purpose of the page. Once I did this, everything worked as expected.

As a lesson learned, never name a page after one of the CP reserved url parameters, or after a filter name in your report. While this may sometimes work with the right conditions, it will fail if you ever need to use "report_page_url" attribute or if the page is displayed in an iFrame.

Comments

Hi ,

I am following mentioned steps , The steps i am doing are as follows :

1. Created PHP page 'report_display _1. Which is used to show filter to user and code is <?php
$url = sprintf("/app/list/toggle/%d", !\RightNow\Utils\Url::getParameter('toggle'));
?>

2. Now when i select any value it gives error 'There has been an error with your request.'

Please do needful .

Zircon - This is a contributing Drupal Theme
Design by WeebPal.