Posted by hass on March 7, 2008 at 11:14pm
24 followers
| Project: | Google Analytics |
| Version: | 7.x-1.x-dev |
| Component: | Code |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | needs work |
Issue Summary
Since ga.js it looks impossible (untested) for other modules to set a custom variable in head. So it becomes impossible to add tracking data like shopping data to the ga tracking. See http://0-code.google.com.millennium.unicatt.it/apis/analytics/docs/gaJSA... for some good examples.
var pageTracker = _gat._getTracker("UA-12345-1");
pageTracker._initData();
pageTracker._trackPageview();
pageTracker._addTrans(
"1234", // order ID - required
"Womens Apparel", // affiliation or store name
"11.99", // total - required
"1.29", // tax
"15.00", // shipping
"San Jose", // city
"California", // state or province
"USA" // country
);
pageTracker._addItem(
"1234", // order ID - required
"DD44", // SKU/code
"T-Shirt", // product name
"Olive Medium", // category or variation
"11.99", // unit price - required
"1" // quantity - required
);
pageTracker._trackTrans();It looks like we need a hook or something else that would allow other modules to set custom variables and urls. We should do some brainstroming what the best way would be.
Comments
#1
Marked #231451 as duplicate.
#2
Nobody is interested on this?
I think it is the 'natural' next step for Analytics module. Now we have tracking and segmentation, but Analytics module has to be capable to track: subscriptions, e-commerce, registrations, newsletter or any other 'calls to action' to use all the power features from Google Analytics.
#3
I thought a little bit more about this and i think there are possible workarounds. There is no real need to add this in the hook way. Someone could create a variable in the page head like
var ecommerceTracker =_addTrans(...);ecommerceTracker =_addTrans(...);
and add something like:
if (ecommerceTracker) { pageTracker = ecommerceTracker; }to the "Custom JavaScript code" text area... not perfect, but this should work, too.
#4
The implementation must allow to add code *before* and *after* _trackPageview() call. See #275359: Allow adding custom javascript code before and after _trackPageview()
#5
#6
This is a first try to implement such a hook for all other modules that likes to hook into GA module. It allows to push in every custom tracking method available in the GA API (http://code.google.com/apis/analytics/docs/gaJSApi.html).
I hope one of the google adsense, ubercart or ecommerce module maintainers could take a look if they are comfortable with this code or if they have a better idea. Feedback is very welcome!!! This patch will stay in queue until a maintainer depend on this code and approved it is working well.
Here follows a code example how this could be integrated in other modules:
<?php
function mymodule_googleanalytics(&$hook) {
switch ($hook) {
case 'preprocess':
$value['methods'][]['_setDomainName'] = array('.example.com');
$value['methods'][]['_clearIgnoredOrganic'] = array();
$value['methods'][]['_setSampleRate'] = array('80');
break;
case 'process':
$value['methods'][]['_addTrans'] = array(
"1234", //order ID - required
"My Partner Store", //affiliation or store name
"84.99", //total - required
"7.66", //tax
"15.99", //shipping
"Boston", //city
"MA", //state or province
"USA" //country
);
$value['methods'][]['_addItem'] = array(
'343212',
'DD4444',
'Lava Lamp',
'Decor',
'34.99',
'1',
);
$value['methods'][]['_addItem'] = array(
'46464',
'CC1111',
'Black Lamp',
'Decor',
'2.99',
'5',
);
$value['methods'][]['_trackTrans'] = array();
break;
}
return $value;
}
?>
#7
Strictly speaking, I don't think this is needed. Modules can use hook_preprocess_page to fiddle with the $javascript array. They can inject their own calls wherever needed. They should do so before template_preprocess_page() does the final drupal_get_js().
#8
I'm not sure how they should insert something before the
'pageTracker._initData();';or after'pageTracker._trackPageview('. $url_custom .');';. If they like to do - they need to alter the $script added in footer with regexes or so and I'm not sure if template_preprocess_page() have the ability to alter this at this late stage. I've got some other troubles at this late stage in D6 that made me to move some parts to the hook_init(), but not all.I will try to test template_preprocess_page() for altering the footer code. Aside this is only available in D6.
#9
I've dumped all $variables in
module_preprocess_page()and the only place I can see the footer code is in$variables['closure']. This is completely ready HTML code. No way to alter an array of javascript code. Therefor this will become a funny regex, but could work.Marking need work to make clear this needs some more investigation and testing.
#10
Ok, here the funny regex... the patch above will not be committed by this way. I hope this regex is correct. Maybe someone else could review this. Then we are able to add this to a readme or a handbook page to give other maintainers the ability to reuse this variant.
<?php
function mymodule_preprocess_page(&$variables) {
// Example code. Make sure your module runs all values through drupal_to_js() to be protected against XSS.
$before = 'pageTracker._setDomainName(".example.com");';
$after = 'pageTracker._addTrans("1234", "My Partner Store", "84.99", "7.66", "15.99", "Boston", "MA", "USA");';
$after .= 'pageTracker._addItem("343212", "DD4444", "Lava Lamp", "Decor", "34.99", "1");';
$after .= 'pageTracker._trackTrans();';
$variables['closure'] = preg_replace('/(.*)(<script type="text\/javascript">var pageTracker = _gat._getTracker\("(UA-\d{4,}-\d+)"\);)(.*)(pageTracker._initData\(\);pageTracker._trackPageview\(\);)(.*)(<\/?script>)(.*)/i', "$2$before$5$after$7", $variables['closure']);
}
?>
The only issue we really have with this solution is - it is not working for D5 and all e-commerce packages are only available for D5 today. I also expect that many people are not able or don't like to upgrade to D6 for a good time...
We could also change the
(UA-\d{4,}-\d+)to(.*)validation here to reduce complexity (untested). We don't really need to validation here.#11
#12
There are some bugs... I will update asap.
#13
Ok, new version that fixes to keep custom URL's, segmentation data and custom javascript code settings as is. Custom javascript code take recedence over module provided google api methods.
<?php
function mymodule_preprocess_page(&$variables) {
// Example code. Make sure your module runs all values through drupal_to_js() to be protected against XSS.
$before = 'pageTracker._setDomainName(".example.com");';
$after = 'pageTracker._addTrans("1234", "My Partner Store", "84.99", "7.66", "15.99", "Boston", "MA", "USA");';
$after .= 'pageTracker._addItem("343212", "DD4444", "Lava Lamp", "Decor", "34.99", "1");';
$after .= 'pageTracker._trackTrans();';
$variables['closure'] = preg_replace('/(.*)(<script type="text\/javascript">var pageTracker = _gat._getTracker\("(UA-\d{4,}-\d+)"\);)(.*)(pageTracker._initData\(\);pageTracker._trackPageview\((.*)?\);)(.*)(<\/?script>)(.*)/i', "$2$before$4$5$7$after$8", $variables['closure']);
}
?>
#14
Book page have been added at http://drupal.org/node/284599.
#15
Automatically closed -- issue fixed for two weeks with no activity.
#16
The original idea seems completely reasonable to me. In fact, I think we should extend the hook idea to include the ability to add multiple trackers.
Modifying this information via a preprocess hook feels quite strange and un-Drupal to me. Where else do we recommend that people do this? Does core ever recommend that people modify it via preg_replace in the theme layer?
#17
Yes. One example - see http://drupal.org/node/228511#comment-881969.
#18
That should also be possible with a theme_form_id function and/or a hook_form_alter which give the data in a structured way (much like the initial hook you proposed here...).
#19
Could you provide an example? I asked this label question very long time and found many many people doing string replacements and such crazy things...
I have thought about a template file in 2.x, but not yet sure how this theming function should looks like best. Otherwise I'm not sure how moshe would do it, but he said we don't need this hook...
#20
Marked #356557: Add a hook for modules wanting to add custom JS as duplicate
#21
@moshe: could you please enlighten us why we don't need it? Others don't like the regex stuff I've created very much... what would be the best solution?
#22
I'm don't have enough familiarity with javascript or pagetracker (in the example). my idea was that modules could call pagetracker methods in their javascript that fiddle with variables. then the javascript that finally sends info to google runs last.
another idea is for this module to structure its javascript injection into a few different drupal_add_js() calls. Then modules can remove/add the parts they want during MODULE_process_page(). for an example of this fiddling, see jquery_update_preprocess_age() at http://cvs.drupal.org/viewvc.py/drupal/contributions/modules/jquery_upda...
#23
Marked #324764: support for multiple analytics keys as duplicate.
#24
#25
Another use case is changing $url_custom from modules. The Apache Solr search module, used on Drupal.org, should set a custom URL to enable site search tracking.
#26
For $url_custom, it would be more simple to do something like:
<?phpdrupal_add_js(array('googleAnalyticsPageURL' => ...), 'setting');
?>
And from JS,
pageTracker._trackPageview(Drupal.settings.googleAnalyticsPageURL).#27
Hook may help #392606: Is it possible to add custom _setVar on a single node? and #320679: Set Goals for new comments, nodes and signups .
#28
@drumm: logic wise this sounds difficult as we process the most code in hook_footer and hook_footer does not have the ability to add a JS setting to head... so core is blocking here :-(
#29
The preg_replace code has no
try{}andcatch (err) {}statement, without them the replace doesn't do anything.#30
Yeah, this needs an update.
#31
Damien (DamZ) proposed a much cleaner way of doing it:
<?php// use hook_preprocess_page or hook_init, etc.
function mymodule_block() {
$node = menu_get_object();
$your_ga_code = "pageTracker._trackEvent('Click', ${node->nid)";
$GLOBALS['conf']['googleanalytics_codesnippet_after'] .= $your_ga_code;
}
?>
The trick is that $GLOBALS['conf'] got flushed for every page request. so you can just use .= to append your GA code.
I've tested the code, and it works!! Thanks Damien!
#32
This is the most dirty way I can think of and not a way we go here.
#33
oh SNAP. :)
#34
The hook idea in http://drupal.org/node/231451#comment-900139 seems like by far the most flexible way to allow other modules to cater for additional tracking needs. After reading through this entire thread, I'm still unsure why that was dismissed...?
#35
Sounds interesting, but complicated. I don't follow all the discussion but am very keen to find a way to add new events to GA.
Any updates? Is something like this likely to make it into an upcoming release of the module?
#36
Here's a patch for the latest google analytics 6.x-3.x-dev that gives a module a hook:
hook_googleanalytics_push_commands
which allows other modules to to add _gaq.push([commandArray]) commands.
It also provides the hook:
hook_googleanalytics_custom_url
Which allows modules to create their own custom_url to pass to _trackPageview
#37
Your overloading variant cannot go in. Please read above concepts first. We cannot fire 20 hooks for performance reasons.
#38
What 20 hooks? It's just two hooks.
What "overloading variant"? The above concepts are two years old and barely relavant to Google's latest code.
#39
Your hooks are tooo many. You made them only to solve your specific issues, but not the issues from others in general. Next time someone else need to hook something else and we have number 3 and so on. We need one hook that does all.
Only the age of a topic doesn't make the #6 concept wrong. Otherwise we could go with a "theme" like function.
#40
Ok, I see.
With the new version of ga.js most of the commands can be entered using the _gaq.push(commandArray) function so rather than having to handle each method seperately and validate it etc as in #6, let the module implementing the hook add whatever commandArrays it wants.
So it would be something like:
function mymodule_googleanalytics(&$commands) {
$commands[] = "['_setCustomVar', 1, 'make', 'ford', 1 ]";
}
#41
This may work, but it must be a real API to allow other modules to detect the ga tracker type like ga.js async/legacy/ga.js and whatever may come in future. I'm fine to change API only with major version changes, but this will not solve the problem that Ubercart have now a problem to detect if GA is 2.x or 3.x.
#42
So you need to continue to support the way the current googleanalytics module works using the old way "_gat._getTracker" etc, aswell as support the new async way? Is that correct?
#43
In general yes, but for now it would be great to make the api save for future and only implement the api to work with 3.x async. We can use 2.x for testing if it really makes Ubercart happy.
#44
Do you want to do something like the views module that requires a module to declare what version of the api it works with?
Then get a list of all hooks that implement the api version and call only those functions relavant to the api.
#45
Yes :-)
#46
Sounds like a plan...
...Trouble is I need to find time / persuade my bosses to allot time to do it.
#47
subscribe
#48
subscribe
This is how I see it:
P1: allowing custom push commands.
P2: allowing $url_custom to be set.
P3: allowing custom segmentation (might not be required after P1 is done).
P4: api hook & backport to 2.x.
I try to avoid putting code into text fields as much as possible, so $codesnippet_before & $codesnippet_after isn't my style. I could do a
hook_init() {global $conf;}hack to set these variables at runtime via code (kinda like strongarm), but this isn't ideal for module development. For the hook I can see use cases for before and after, so I think there should be 2 hooks for push commands. In the mean time looks like I'll use the strongarm hack to get stuff done for custom modules we have.Might want to look into this module
http://drupal.org/project/ga_customvars
#49
P4: 2.x will no longer supported very soon.
P3: #609892: Multiple custom variables
P2: #807320: Rollback: Track outgoing links as targets / Disable event tracking optionally for outgoing links. not active. Seems nobody really interrested in.
P1: 95% or more are static setting only and can be configured today. P3 may solve P1 for you too. I currently have no other pushes in mind, but i'm sure there might be some. The dynamic stuff may be very difficult to press into code in a generic way accept this hook implementation.
The last beein said I wish the hook would exist asap, but nobody seems to be really interrested, too.
#50
subscribe
#51
Seems like a hook like the one in #40 would be what I want in order to implement having a third tracker on my site based on what the $custom_theme variable is set to on a particular page. (We need to do that because of using one domain to display content from various content partners that need their own Analytics stats.)
I might look into writing a hook along those lines, but I wouldn't support the old GA APIs, since that would require a totally different way of approaching the problem.
For now, I think I'm just going to code the .gaq_push('custom variables/command') directly in my themes, like I had done in the past.
#52
@hass: The snippet you wrote at #13 using "mymodule_preprocess_page" is outdated. Would you please write a new snippet to support the new GA APIs? It would be very useful for people to add customized GA code in their modules since the hook is not ready soon. Thanks!
#53
I've seen many hacked google_analytics due to this missing hook...
We might push this a little soon.
#54
I agree with greggles et. al. that we need a hook. preprocess page and other workarounds are terrible - yes, you can do it, but it's kludgy and most importantly not at all readable or maintainable from a code perspective.
If your module implements hook_googleanalytics, it's pretty clear to anyone reading the code what's going on, and every module that implements hook_googleanalytics will have similar code.
If your module writes a custom preprocess function full of regex, then every module will do things differently and it will reduce maintainability/increase complexity.
#55
I have never said to go with hook_preprocess. No idea why people are not able to look into #6.
#56
I have patched with the patch in #36, it seems to work well. Will know more when we have a few days reports in.
#57
Will never get committed because of design flaws.
#58
Marked #1231308: Need ability to allow anchor (#) tag tracking as duplicate.
#59
Here's a modest proposal TM for getting stuff like this done.
Rules integration for Google Analytics goals tracking.
http://drupal.org/node/1243544
Added benefit is more modules will end up implementing rules, if they want to integrate with Google Analytics goals.
Added bonus, any module which already implements rules in to their workflow will be able to be easily tracked.
#60
subscribe
#61
Going to post this here, in case a hook is never written to alter $url_custom.
The problem:
We are using drupal 7 with apachesolr search pages. These search pages can have any URL defined, and you can have many of these pages on your site (this is vastly different from drupal 6.x).
At the moment we have the defined search pages (where 'monkey' is the search term):
/search/results/monkey
/publications/monkey
This line was proving to be most troublesome, as the GA module assumes all search pages start with the word 'search'
if (module_exists('search') && variable_get('googleanalytics_site_search', FALSE) && arg(0) == 'search' && $keys = googleanalytics_search_get_keys()) {This obviously was not the case with our site (and I imagine a growing number of sites going forward for drupal 7 apachesolr).
Instead of going down the road of forking this module and adding in the necessary hooks to alter $url_custom, I set about implementing hook_js_alter()
Here is my code:
<?php
/**
* Massive hack in lieu of the GA module not wanting to support alter hooks
*/
function example_module_js_alter(&$javascript) {
// modify the GA code if on a search results page
if (module_exists('search') && variable_get('googleanalytics_site_search', FALSE) && arg(0) == 'publications') {
// We allow different scopes. Default to 'header' but allow user to override if they really need to.
$scope = variable_get('googleanalytics_js_scope', 'header');
foreach ($javascript as $index => $js) {
if ($js['type'] == 'inline' && $js['scope'] == $scope && strpos($js['data'], '_gaq.push([') > 0) {
// publications search tracking support
$url_custom = '';
if ($keys = example_module_publications_search_get_keys()) {
$url_custom = '(window.googleanalytics_search_results) ? ' . drupal_json_encode(url('search/' . arg(0), array('query' => array('search' => $keys)))) . ' : ' . drupal_json_encode(url('search/' . arg(0), array('query' => array('search' => 'no-results:' . $keys, 'cat' => 'no-results'))));
}
if (!empty($url_custom)) {
$javascript[$index]['data'] = str_replace('_gaq.push(["_trackPageview"]);', '_gaq.push(["_trackPageview",' . $url_custom . ']);', $js['data']);
}
}
}
}
}
/**
* Helper function for grabbing search keys. Function is missing in D7.
*/
function example_module_publications_search_get_keys() {
static $return;
if (!isset($return)) {
// Extract keys as remainder of path
// Note: support old GET format of searches for existing links.
$path = explode('/', $_GET['q'], 2);
$keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys'];
$return = count($path) == 2 ? $path[1] : $keys;
}
return $return;
}
?>
I hope this helps someone else
Sean
#62
#1243544: Rules integration for Google Analytics goals tracking. marked as duplicate.
#63
I just read through this whole thread.
What you proposed in #6 does not seem to do anything about the $custom_url variable problems people have?
This is something we use in the ga-module. First module to return a custom_url "wins". We need to track categories visited and we use solr for search/categories. We also concluded that any module which returns a custom url for the current page, knows what it is doing. So first come, first served.
This is a simplified example on how we have done it:
foreach (module_implements('ga_custom_url') as $module) {
$func = $module.'_ga_custom_url';
if ($url_custom = $func()) {
break;
}
}
function search_statistics_ga_custom_url() {
if (!apachesolr_has_searched()) {
return NULL;
}
$query = apachesolr_current_query();
$phrase = trim($query->get_query_basic());
if(empty($phrase)) {
return NULL;
}
return drupal_to_js(url('', array('query' => 'search='. $phrase)));
}
#64
This was just a design idea. It is nothing ready... I believe we may also need weights...
#65
I want to introduce de GA Push sandbox module, that I think offers the discussed functionality not using hooks but direct function call. Well, offers this functionality for Drupal 7.x, but I although this issue is discussed for the 6.x branch I guess all people interested in this issue wants this in the 7.x branch too.
I don't see the point of using hooks to add GA push items to pages. Hooks are intended to 'observe' module actions and react accordingly. But in this case modules just need to send some GA push items to Google Analytics servers. So I think a good approach is to simply offer a function that any module can call that sends info to GA servers.
GA Push does this and something more. It offers a function to send info to GA (event and ecommerce info at the moment, page view tracking is not included as GA Drupal module already does it). The info is sent to GA using a 'backend': PHP or JS are the defined backends. If PHP method is selected the Drupal server makes a request to GA servers to send tracking information; JS method puts the info in the Drupal.settings array and the request is made by the client browser when page is loaded in the client side.
Both methods uses the Google Analytics Drupal module configuration, and in case of JS method it uses the customizations made to GA JS object (I'm thinking in the domain stuff).
Also allows to define via GUI JS events that will be tracked (elements clicked, elements hovered an so on).
GA Push has a submodule for tracking form validations errors (some people from marketing love this).
So, GA Push is an extension for Google Analytics module. We'd planned to release it as a full separate project, but we've been told to try integrate this module in GA. Personally I don't think GA Push should be part of the main GA module, as its functionality is only needed for advanced users and it's low coupled with GA (as it just uses GA config), but I want to hear more opinons. Also, GA Push should then wait until is integrated and stabilized inside GA before it can be used, and we need to use it in short.
GA Push is currently fully functional but it may be tested more extensively. Also, it may present undetected flaws, but this is why we share it, to improve it and allow others to use it.
Module can be found here:
http://drupal.org/sandbox/GeduR/1652964
#66
The last submitted patch, push_commands.patch, failed testing.
#67
Did I trigger a patch test? But I didn't attach any patch.... trying to set it to needs review again.
#68
Only patches for ga can be reviewed. My last idea was more a theme like function or js alter to change ga, but it need to be a usable array structure that can be "rendered". Also see #1300240: Add tracking for form error messages, please.
#69
Currently using similar code in #61 in D6 via AdvAgg's hooks. I wanted to add in the value of a CCK field to the end of a URL for tracking purposes; so
/node/1is/node/1?my_value=BBQjust for GA. Also using httprl_glue_url() in here.<?php
/**
* Implementation of hook_init
*/
function my_init() {
// Disable the advagg cache on page nodes.
$arg = arg();
if ( isset($arg[0]) && $arg[0] == 'node'
&& isset($arg[1]) && is_numeric($arg[1])
&& empty($arg[2])
) {
// Do stuff with the node id: $arg[1]
$node = node_load($arg[1]);
if (!empty($node) && $node->type == 'page') {
$GLOBALS['conf']['advagg_use_full_cache'] = FALSE;
}
}
}
/**
* Implement hook_advagg_js_header_footer_alter
*/
function my_advagg_js_header_footer_alter(&$master_set, $preprocess_js, $public_downloads) {
// Return if not a node page.
$arg = arg();
if ( !isset($arg[0]) || $arg[0] != 'node'
|| !isset($arg[1]) || !is_numeric($arg[1])
|| !empty($arg[2])
) {
return;
}
$node = node_load($arg[1]);
// Return if node doesn't contain the field I'm looking for.
if ( empty($node)
|| $node->type != 'page'
|| empty($node->field_my_cck_field[0]['value'])
) {
return;
}
foreach ($master_set as $scope => &$data) {
// Skip if no inline data.
if (empty($data['inline'])) {
continue;
}
foreach ($data['inline'] as $key => &$value) {
// Skip if no inline code.
if (empty($value['code'])) {
continue;
}
// Skip if _trackPageview is not here.
if (strpos($value['code'], '_gaq.push(["_trackPageview"]);') === FALSE) {
continue;
}
// Get current URL and parse it.
$parsed_url = parse_url(request_uri());
// Pull out any query strings.
if (!empty($parsed_url['query'])) {
$query_vars = array();
parse_str($parsed_url['query'], $query_vars);
}
// Add in our own query strhing
$query_vars['aid'] = $node->field_my_cck_field[0]['value'];
// Put the URL back together.
$parsed_url['query'] = http_build_query($query_vars, '', '&');
$new_url = httprl_glue_url($parsed_url);
// String replace the _trackPageview code.
$value['code'] = str_replace('_gaq.push(["_trackPageview"]);', '_gaq.push(["_trackPageview", "' . $new_url . '"]);', $value['code']);
}
}
}
?>
#70
Just for understanding, what's wrong with http://api.drupal.org/api/drupal/includes%21common.inc/function/drupal_g... and url() and http://api.drupal.org/api/drupal/includes%21common.inc/function/drupal_p... ?
#71
@hass
Code in #69 is for D6.
#72
http://drupal.org/node/930760
#73
Here is a patch to the effect of what was in #63. It's a simple temporary solution that enables us to alter the url that gets passed to _trackPageview until someone gets around to implementing something better. We are using this with Drupal Commerce in order to append a product SKU to the checkout complete url so that we can do url based tracking with funnel pipelines for purchases of specific products (we were using ga_push to send Events and track those, but GA Event based goals doesn't yet support goal funnels).
/**
* Implements hook_commerce_checkout_router().
*/
function cw_commerce_commerce_checkout_router($order, $checkout_page) {
// If the user is on the commerce checkout complete page, set a static
// variable so that we can react on this in hook_ga_custom_url().
if ($checkout_page['page_id'] == 'complete') {
$cw_commerce_commerce_checkout_router_complete = &drupal_static('cw_commerce_commerce_checkout_router_complete');
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$line_items = $order_wrapper->commerce_line_items->value();
if ($line_items) {
$product = commerce_product_load($line_items[0]->commerce_product[LANGUAGE_NONE][0]['product_id']);
// Save the SKU into a static var that we can use later.
$cw_commerce_commerce_checkout_router_complete = $product->sku;
}
}
}
/**
* Implement hook_ga_custom_url() to override the tracked page url for
* commerce checkout completions.
*
* This is a sort of hack based on http://drupal.org/node/231451#comment-6080926.
*/
function cw_commerce_ga_custom_url() {
$sku = &drupal_static('cw_commerce_commerce_checkout_router_complete');
if (empty($sku)) {
return NULL;
}
$current_path = current_path();
return drupal_json_encode(url($current_path, array('query' => array('sku' => $sku))));
}