diff --git a/piwik.admin.inc b/piwik.admin.inc
index ceb592a..b04bf3f 100644
--- a/piwik.admin.inc
+++ b/piwik.admin.inc
@@ -6,13 +6,12 @@
  */
 
 /**
- * Implementation of hook_admin_settings() for configuring the module
+ * Implements hook_admin_settings() for configuring the module.
  */
-function piwik_admin_settings_form(&$form_state) {
+function piwik_admin_settings_form($form_state) {
   $form['account'] = array(
     '#type' => 'fieldset',
     '#title' => t('General settings'),
-    '#collapsible' => FALSE,
   );
 
   $form['account']['piwik_site_id'] = array(
@@ -42,11 +41,21 @@ function piwik_admin_settings_form(&$form_state) {
     '#description' => t('The URL to your Piwik base directory with SSL certificate installed. Required if you track a SSL enabled website. Example: "https://www.example.com/piwik/".'),
   );
 
-  $form['domain_tracking'] = array(
+  // Visibility settings.
+  $form['tracking_title'] = array(
+    '#type' => 'item',
+    '#title' => t('Tracking scope'),
+  );
+  $form['tracking'] = array(
+    '#type' => 'vertical_tabs',
+    '#attached' => array(
+      'js' => array(drupal_get_path('module', 'piwik') . '/piwik.admin.js'),
+    ),
+  );
+
+  $form['tracking']['domain_tracking'] = array(
     '#type' => 'fieldset',
     '#title' => t('Domains'),
-  	'#collapsible' => TRUE,
-  	'#collapsed' => TRUE,
   );
 
   global $cookie_domain;
@@ -61,7 +70,7 @@ function piwik_admin_settings_form(&$form_state) {
     }
   }
 
-  $form['domain_tracking']['piwik_domain_mode'] = array(
+  $form['tracking']['domain_tracking']['piwik_domain_mode'] = array(
     '#type' => 'radios',
     '#title' => t('What are you tracking?'),
     '#options' => array(
@@ -72,55 +81,60 @@ function piwik_admin_settings_form(&$form_state) {
   );
 
   // Page specific visibility configurations.
-  $form['page_vis_settings'] = array(
+  $php_access = user_access('use PHP for tracking visibility');
+  $visibility = variable_get('piwik_visibility_pages', 0);
+  $pages = variable_get('piwik_pages', '');
+
+  $form['tracking']['page_vis_settings'] = array(
     '#type' => 'fieldset',
-    '#title' => t('Page specific tracking settings'),
+    '#title' => t('Pages'),
     '#collapsible' => TRUE,
     '#collapsed' => TRUE,
   );
 
-  $access = user_access('use PHP for tracking visibility');
-  $visibility = variable_get('piwik_visibility_pages', 0);
-  $pages = variable_get('piwik_pages', '');
-
-  if ($visibility == 2 && !$access) {
-    $form['page_vis_settings'] = array();
-    $form['page_vis_settings']['visibility'] = array('#type' => 'value', '#value' => 2);
-    $form['page_vis_settings']['pages'] = array('#type' => 'value', '#value' => $pages);
+  if ($visibility == 2 && !$php_access) {
+    $form['tracking']['page_vis_settings'] = array();
+    $form['tracking']['page_vis_settings']['visibility'] = array('#type' => 'value', '#value' => 2);
+    $form['tracking']['page_vis_settings']['pages'] = array('#type' => 'value', '#value' => $pages);
   }
   else {
-    $options = array(t('Add to every page except the listed pages.'), t('Add to the listed pages only.'));
-    $description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
+    $options = array(
+      t('Every page except the listed pages'),
+      t('The listed pages only')
+    );
+    $description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
 
-    if ($access) {
-      $options[] = t('Add if the following PHP code returns <code>TRUE</code> (PHP-mode, experts only).');
-      $description .= ' '. t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can break your Drupal site.', array('%php' => '<?php ?>'));
+    if (module_exists('php') && $php_access) {
+      $options[] = t('Pages on which this PHP code returns <code>TRUE</code> (experts only)');
+      $title = t('Pages or PHP code');
+      $description .= ' ' . t('If the PHP option is chosen, enter PHP code between %php. Note that executing incorrect PHP code can break your Drupal site.', array('%php' => '<?php ?>'));
     }
-    $form['page_vis_settings']['piwik_visibility_pages'] = array(
+    else {
+      $title = t('Pages');
+    }
+    $form['tracking']['page_vis_settings']['piwik_visibility_pages'] = array(
       '#type' => 'radios',
       '#title' => t('Add tracking to specific pages'),
       '#options' => $options,
       '#default_value' => $visibility,
     );
-    $form['page_vis_settings']['piwik_pages'] = array(
+    $form['tracking']['page_vis_settings']['piwik_pages'] = array(
       '#type' => 'textarea',
-      '#title' => t('Pages'),
+      '#title' => $title,
+      '#title_display' => 'invisible',
       '#default_value' => $pages,
       '#description' => $description,
-      '#wysiwyg' => FALSE,
       '#rows' => 10,
     );
   }
 
   // Render the role overview.
-  $form['role_vis_settings'] = array(
+  $form['tracking']['role_vis_settings'] = array(
     '#type' => 'fieldset',
-    '#title' => t('Role specific tracking settings'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
+    '#title' => t('Roles'),
   );
 
-  $form['role_vis_settings']['piwik_visibility_roles'] = array(
+  $form['tracking']['role_vis_settings']['piwik_visibility_roles'] = array(
     '#type' => 'radios',
     '#title' => t('Add tracking for specific roles'),
     '#options' => array(
@@ -130,12 +144,8 @@ function piwik_admin_settings_form(&$form_state) {
     '#default_value' => variable_get('piwik_visibility_roles', 0),
   );
 
-  $roles = user_roles();
-  $role_options = array();
-  foreach ($roles as $rid => $name) {
-    $role_options[$rid] = $name;
-  }
-  $form['role_vis_settings']['piwik_roles'] = array(
+  $role_options = array_map('check_plain', user_roles());
+  $form['tracking']['role_vis_settings']['piwik_roles'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Roles'),
     '#default_value' => variable_get('piwik_roles', array()),
@@ -144,51 +154,62 @@ function piwik_admin_settings_form(&$form_state) {
   );
 
   // Standard tracking configurations.
-  $form['user_vis_settings'] = array(
+  $form['tracking']['user_vis_settings'] = array(
     '#type' => 'fieldset',
-    '#title' => t('User specific tracking settings'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
+    '#title' => t('Users'),
   );
-  $form['user_vis_settings']['piwik_custom'] = array(
+  $t_permission = array('%permission' => t('opt-in or out of tracking'));
+  $form['tracking']['user_vis_settings']['piwik_custom'] = array(
     '#type' => 'radios',
-    '#title' => t('Custom tracking settings'),
+    '#title' => t('Allow users to customize tracking on their account page'),
     '#options' => array(
-      t('Users cannot control whether they are tracked or not.'),
-      t('Track users by default, but let individual users to opt out.'),
-      t('Do not track users by default, but let individual users to opt in.')
+      t('No customization allowed'),
+      t('Tracking on by default, users with %permission permission can opt out', $t_permission),
+      t('Tracking off by default, users with %permission permission can opt in', $t_permission)
     ),
-    '#description' => t('Allow individual users to customize the visibility of tracking in their account settings. Only users with %permission permission are allowed to set their own preference.', array('%permission' => t('opt-in or out of tracking'))),
     '#default_value' => variable_get('piwik_custom', 0),
   );
 
   // Link specific configurations.
-  $form['linktracking'] = array(
+  $form['tracking']['linktracking'] = array(
     '#type' => 'fieldset',
-    '#title' => t('Link tracking settings'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
+    '#title' => t('Links and downloads'),
   );
-  $form['linktracking']['piwik_track'] = array(
+  $form['tracking']['linktracking']['piwik_track'] = array(
     '#type' => 'checkbox',
-    '#title' => t('Track download and outbound links'),
+    '#title' => t('Track clicks on outbound links and downloads (clicks on file links) for the following extensions'),
     '#default_value' => variable_get('piwik_track', 1),
-    '#description' => t('Enables tracking of clicks on download and outbound links.')
   );
-  $form['linktracking']['piwik_trackfiles_extensions'] = array(
+  $form['tracking']['linktracking']['piwik_trackfiles_extensions'] = array(
+    '#title' => t('List of download file extensions'),
+    '#title_display' => 'invisible',
     '#type' => 'textfield',
-    '#title' => t('File extensions to track'),
     '#default_value' => variable_get('piwik_trackfiles_extensions', PK_TRACKFILES_EXTENSIONS),
-    '#description' => t('A pipe separated list of file extensions that should be tracked when clicked with regular expression support. Example: !extensions', array('!extensions' => PK_TRACKFILES_EXTENSIONS)),
+    '#description' => t('A file extension list separated by the | character that will be tracked when clicked. Regular expressions are supported. For example: !extensions', array('!extensions' => PK_TRACKFILES_EXTENSIONS)),
     '#maxlength' => 255,
   );
 
+  $form['tracking']['search'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Search'),
+  );
+
+  $site_search_dependencies = '<div class="admin-dependencies">';
+  $site_search_dependencies .= t('Depends on: !dependencies', array('!dependencies' => (module_exists('search') ? t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => 'Search')) : t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => 'Search')))));
+  $site_search_dependencies .= '</div>';
+
+  $form['tracking']['search']['piwik_site_search'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Track internal search'),
+    '#description' => t('If checked, internal search keywords are tracked. You must manually install and configure the SiteSearch plugin for your sites. For the setting <em>Search URL</em> use a value of <strong>/search</strong> and for <em>Search Parameter</em> use a value of <strong>query</strong>. For more information see <a href="@url">SiteSearch plugin for piwik</a>.', array('@url' => 'http://github.com/BeezyT/piwik-sitesearch/wiki')) . $site_search_dependencies,
+    '#default_value' => variable_get('piwik_site_search', FALSE),
+    '#disabled' => (module_exists('search') ? FALSE : TRUE),
+  );
+
   // Privacy specific configurations.
   $form['tracking']['privacy'] = array(
     '#type' => 'fieldset',
     '#title' => t('Privacy'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
   );
   $form['tracking']['privacy']['piwik_privacy_donottrack'] = array(
     '#type' => 'checkbox',
@@ -203,7 +224,7 @@ function piwik_admin_settings_form(&$form_state) {
     '#title' => t('Page titles hierarchy'),
     '#description' => t('This functionality enables a dynamically expandable tree view of your site page titles in your Piwik statistics. See in Piwik statistics under <em>Actions</em> > <em>Page titles</em>.'),
     '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
+    '#collapsed' => FALSE,
   );
   $form['page_title_hierarchy']['piwik_page_title_hierarchy'] = array(
     '#type' => 'checkbox',
@@ -218,140 +239,121 @@ function piwik_admin_settings_form(&$form_state) {
     '#default_value' => variable_get('piwik_page_title_hierarchy_exclude_home', TRUE),
   );
 
-  // Advanced feature configurations.
-  $form['advanced'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Advanced settings'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-
-  $form['advanced']['piwik_cache'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Cache tracking code file locally'),
-    '#description' => t("If checked, the tracking code file is retrieved from Piwik and cached locally. It is updated daily from your Piwik servers to ensure updates to tracking code are reflected in the local copy."),
-    '#default_value' => variable_get('piwik_cache', 0),
-  );
-  if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE) {
-    $form['advanced']['piwik_cache']['#disabled'] = TRUE;
-    $form['advanced']['piwik_cache']['#description'] .= ' '. t('<a href="@url">Public file transfers</a> must be enabled to allow local caching.', array('@url' => url('admin/settings/file-system', array('query' => drupal_get_destination()))));
-  }
-
-  // Allow for tracking of the originating node when viewing translation sets.
-  if (module_exists('translation')) {
-    $form['advanced']['piwik_translation_set'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Track translation sets as one unit'),
-      '#description' => t('When a node is part of a translation set, record statistics for the originating node instead. This allows for a translation set to be treated as a single unit.'),
-      '#default_value' => variable_get('piwik_translation_set', 0),
-    );
-  }
-
-  $site_search_dependencies = '<div class="admin-dependencies">';
-  $site_search_dependencies .= t('Depends on: !dependencies', array('!dependencies' => (module_exists('search') ? t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => 'Search')) : t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => 'Search')))));
-  $site_search_dependencies .= '</div>';
-
-  $form['advanced']['piwik_site_search'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Track internal search'),
-    '#description' => t('If checked, internal search keywords are tracked. You must manually install and configure the SiteSearch plugin for your sites. For the setting <em>Search URL</em> use a value of <strong>/search</strong> and for <em>Search Parameter</em> use a value of <strong>query</strong>. For more information see <a href="@url">SiteSearch plugin for piwik</a>.', array('@url' => 'http://github.com/BeezyT/piwik-sitesearch/wiki')) . $site_search_dependencies,
-    '#default_value' => variable_get('piwik_site_search', FALSE),
-    '#disabled' => (module_exists('search') ? FALSE : TRUE),
-  );
-
-  $form['advanced']['codesnippet'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Custom JavaScript code'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#description' => t('You can add custom Piwik <a href="@snippets">code snippets</a> here. These will be added to every page that Piwik appears on. <strong>Do not include the &lt;script&gt; tags</strong>, and always end your code with a semicolon (;).', array('@snippets' => 'http://piwik.org/docs/javascript-tracking/'))
-  );
-  
-	$form['advanced']['codesnippet']['piwik_codesnippet_before'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Code snippet (before)'),
-    '#default_value' => variable_get('piwik_codesnippet_before', ''),
-    '#rows' => 5,
-    '#wysiwyg' => FALSE,
-    '#description' => t('Code in this textarea will be added <strong>before</strong> _paq.push(["trackPageView"]).')
-  );
-  $form['advanced']['codesnippet']['piwik_codesnippet_after'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Code snippet (after)'),
-    '#default_value' => variable_get('piwik_codesnippet_after', ''),
-    '#rows' => 5,
-    '#wysiwyg' => FALSE,
-    '#description' => t('Code in this textarea will be added <strong>after</strong> _paq.push(["trackPageView"]). This is useful if you\'d like to track a site in two accounts.')
-  );
-
-  
   $form['piwik_custom_var'] = array(
+    '#collapsible' => TRUE,
   	'#collapsed' => TRUE,
-  	'#collapsible' => TRUE,
-    '#description' => t('You can add Piwik Analytics <a href="!custom_var_documentation">Custom Variables</a> here. These will be added to every page that Piwik tracking code appears on. Piwik will only accept custom variables if the <em>name</em> and <em>value</em> combined are less than 64 bytes after URL encoding. Keep the names as short as possible and expect long values to get trimmed. You may use tokens in custom variable values. Global and user tokens are always available; on node pages, node tokens are also available. Piwik uses the asynchronous method, noted near the bottom of the <a href="!custom_var_documentation">Custom Variables Page</a>', array('!custom_var_documentation' => 'http://http://piwik.org/docs/custom-variables/')),
+    '#description' => t('You can add Piwiks <a href="!custom_var_documentation">Custom Variables</a> here. These will be added to every page that Piwik tracking code appears on. Custom variables names and values are limited to 100 characters in length each. Keep the names and values as short as possible and expect long values to get trimmed. You may use tokens in custom variable values. Global and user tokens are always available; on node pages, node tokens are also available.', array('!custom_var_documentation' => 'http://piwik.org/docs/custom-variables/')),
     '#theme' => 'piwik_admin_custom_var_table',
     '#title' => t('Custom variables'),
     '#tree' => TRUE,
     '#type' => 'fieldset',
   );
 
-
-  $token_enabled = module_exists('token');
   $piwik_custom_vars = variable_get('piwik_custom_var', array());
 
-  // Piwik Analytics supports up to 5 custom variables.
+  // Piwik supports up to 5 custom variables.
   for ($i = 1; $i < 6; $i++) {
-    // TODO: '#default_value' is currently broken, see http://drupal.org/node/410926.
     $form['piwik_custom_var']['slots'][$i]['slot'] = array(
-      //'#attributes' => array('readonly' => 'readonly'),
-      //'#default_value' => $i,
+      '#default_value' => $i,
       '#description' => t('Slot number'),
       '#disabled' => TRUE,
       '#size' => 1,
+      '#title' => t('Custom variable slot #@slot', array('@slot' => $i)),
+      '#title_display' => 'invisible',
       '#type' => 'textfield',
-      '#value' => $i,
     );
     $form['piwik_custom_var']['slots'][$i]['name'] = array(
       '#default_value' => !empty($piwik_custom_vars['slots'][$i]['name']) ? $piwik_custom_vars['slots'][$i]['name'] : '',
       '#description' => t('The custom variable name.'),
-      '#size' => 20,
+      '#maxlength' => 100,
+    	'#size' => 20,
+      '#title' => t('Custom variable name #@slot', array('@slot' => $i)),
+      '#title_display' => 'invisible',
       '#type' => 'textfield',
     );
     $form['piwik_custom_var']['slots'][$i]['value'] = array(
       '#default_value' => !empty($piwik_custom_vars['slots'][$i]['value']) ? $piwik_custom_vars['slots'][$i]['value'] : '',
-      '#description' => ($token_enabled ? t('The custom variable value. You may use tokens in this field.') : t('The custom variable value.')),
-      '#size' => 20,
+      '#description' => t('The custom variable value. You may use tokens in this field.'),
+      '#maxlength' => 255,
+      '#title' => t('Custom variable value #@slot', array('@slot' => $i)),
+      '#title_display' => 'invisible',
       '#type' => 'textfield',
+      '#element_validate' => array('piwik_token_element_validate'),
+      '#token_types' => array('node'),
     );
-    if ($token_enabled) {
-      $form['piwik_custom_var']['slots'][$i]['value']['#element_validate'][] = 'piwik_token_element_validate';
+    if (module_exists('token')) {
       $form['piwik_custom_var']['slots'][$i]['value']['#element_validate'][] = 'token_element_validate';
-      $form['piwik_custom_var']['slots'][$i]['value']['#token_types'][] = 'node';
-      $form['piwik_custom_var']['slots'][$i]['value']['#token_types'][] = 'user';
     }
     $form['piwik_custom_var']['slots'][$i]['scope'] = array(
-      '#default_value' => !empty($piwik_custom_vars['slots'][$i]['scope']) ? $piwik_custom_vars['slots'][$i]['scope'] : 3,
+      '#default_value' => !empty($piwik_custom_vars['slots'][$i]['scope']) ? $piwik_custom_vars['slots'][$i]['scope'] : 'visit',
       '#description' => t('The scope for the custom variable.'),
+      '#title' => t('Custom variable slot #@slot', array('@slot' => $i)),
+      '#title_display' => 'invisible',
       '#type' => 'select',
       '#options' => array(
-        1 => t('Visitor'),
-        2 => t('Session'),
-        3 => t('Page'),
+        'visit' => t('Visit'),
+        'page' => t('Page'),
       ),
     );
   }
 
   $form['piwik_custom_var']['piwik_custom_var_description'] = array(
     '#type' => 'item',
-    '#description' => t('You can supplement Pwik\'s basic IP address tracking of visitors by segmenting users based on custom variables.') . (!$token_enabled ? ' '. t('<a href="@module_list">Enable the token module</a> to be able to use tokens for more granular tracking.', array('@module_list' => url('admin/build/modules'))) : '') 
+    '#description' => t('You can supplement Piwiks\' basic IP address tracking of visitors by segmenting users based on custom variables. Make sure you will not associate (or permit any third party to associate) any data gathered from your websites (or such third parties\' websites) with any personally identifying information from any source as part of your use (or such third parties\' use) of the Piwik\' service.'),
   );
   $form['piwik_custom_var']['piwik_custom_var_token_tree'] = array(
     '#theme' => 'token_tree',
-    '#token_types' => array('node', 'user'),
+    '#token_types' => array('node'),
+  );
+
+  // Advanced feature configurations.
+  $form['advanced'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['advanced']['piwik_cache'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Locally cache tracking code file'),
+    '#description' => t('If checked, the tracking code file is retrieved from your Piwik site and cached locally. It is updated daily to ensure updates to tracking code are reflected in the local copy.'),
+    '#default_value' => variable_get('piwik_cache', 0),
   );
 
+  // Allow for tracking of the originating node when viewing translation sets.
+  if (module_exists('translation')) {
+    $form['advanced']['piwik_translation_set'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Track translation sets as one unit'),
+      '#description' => t('When a node is part of a translation set, record statistics for the originating node instead. This allows for a translation set to be treated as a single unit.'),
+      '#default_value' => variable_get('piwik_translation_set', 0),
+    );
+  }
+
+  // Code snippet settings.
+  $form['advanced']['codesnippet'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Custom JavaScript code'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#description' => t('You can add custom Piwik <a href="@snippets">code snippets</a> here. These will be added to every page that Piwik appears on. <strong>Do not include the &lt;script&gt; tags</strong>, and always end your code with a semicolon (;).', array('@snippets' => 'http://piwik.org/docs/javascript-tracking/'))
+  );
+  $form['advanced']['codesnippet']['piwik_codesnippet_before'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Code snippet (before)'),
+    '#default_value' => variable_get('piwik_codesnippet_before', ''),
+    '#rows' => 5,
+    '#description' => t('Code in this textarea will be added <strong>before</strong> _paq.push(["trackPageView"]).')
+  );
+  $form['advanced']['codesnippet']['piwik_codesnippet_after'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Code snippet (after)'),
+    '#default_value' => variable_get('piwik_codesnippet_after', ''),
+    '#rows' => 5,
+    '#description' => t('Code in this textarea will be added <strong>after</strong> _paq.push(["trackPageView"]). This is useful if you\'d like to track a site in two accounts.')
+  );
 
-  /* hook_footer() is not able to add code to head. Upgrade to D7 required.
   $form['advanced']['piwik_js_scope'] = array(
     '#type' => 'select',
     '#title' => t('JavaScript scope'),
@@ -361,24 +363,40 @@ function piwik_admin_settings_form(&$form_state) {
       'header' => t('Header'),
     ),
     '#default_value' => variable_get('piwik_js_scope', 'footer'),
-  ); */
+  );
 
   return system_settings_form($form);
 }
 
 function piwik_admin_settings_form_validate($form, &$form_state) {
+  // Custom variables validation.
+  foreach ($form_state['values']['piwik_custom_var']['slots'] as $custom_var) {
+    // Validate empty names/values.
+    if (empty($custom_var['name']) && !empty($custom_var['value'])) {
+      form_set_error("piwik_custom_var][slots][" . $custom_var['slot'] . "][name", t('The custom variable @slot-number requires a <em>Value</em> if a <em>Name</em> has been provided.', array('@slot-number' =>  $custom_var['slot'])));
+    }
+    elseif (!empty($custom_var['name']) && empty($custom_var['value'])) {
+      form_set_error("piwik_custom_var][slots][" . $custom_var['slot'] . "][value", t('The custom variable @slot-number requires a <em>Name</em> if a <em>Value</em> has been provided.', array('@slot-number' =>  $custom_var['slot'])));
+    }
+  }
+
+  // Trim some text area values.
+  $form_state['values']['piwik_pages'] = trim($form_state['values']['piwik_pages']);
+  $form_state['values']['piwik_codesnippet_before'] = trim($form_state['values']['piwik_codesnippet_before']);
+  $form_state['values']['piwik_codesnippet_after'] = trim($form_state['values']['piwik_codesnippet_after']);
+
   if (!preg_match('/^\d{1,}$/', $form_state['values']['piwik_site_id'])) {
     form_set_error('piwik_site_id', t('A valid Piwik site ID is an integer only.'));
   }
 
-  $url = $form_state['values']['piwik_url_http'] .'piwik.php';
+  $url = $form_state['values']['piwik_url_http'] . 'piwik.php';
   $result = drupal_http_request($url);
   if ($result->code != 200) {
     form_set_error('piwik_url_http', t('The validation of "@url" failed with error "@error" (HTTP code @code).', array('@url' => check_url($url), '@error' => $result->error, '@code' => $result->code)));
   }
 
   if (!empty($form_state['values']['piwik_url_https'])) {
-    $url = $form_state['values']['piwik_url_https'] .'piwik.php';
+    $url = $form_state['values']['piwik_url_https'] . 'piwik.php';
     $result = drupal_http_request($url);
     if ($result->code != 200) {
       form_set_error('piwik_url_https', t('The validation of "@url" failed with error "@error" (HTTP code @code).', array('@url' => check_url($url), '@error' => $result->error, '@code' => $result->code)));
@@ -397,23 +415,19 @@ function piwik_admin_settings_form_validate($form, &$form_state) {
   if (preg_match('/(.*)<\/?script(.*)>(.*)/i', $form_state['values']['piwik_codesnippet_after'])) {
     form_set_error('piwik_codesnippet_after', t('Do not include the &lt;script&gt; tags in the javascript code snippets.'));
   }
-
-  // Trim some text area values.
-  $form_state['values']['piwik_pages'] = trim($form_state['values']['piwik_pages']);
-  $form_state['values']['piwik_codesnippet_before'] = trim($form_state['values']['piwik_codesnippet_before']);
-  $form_state['values']['piwik_codesnippet_after'] = trim($form_state['values']['piwik_codesnippet_after']);
 }
 
 /**
  * Layout for the custom variables table in the admin settings form.
  */
-function theme_piwik_admin_custom_var_table($form) {
+function theme_piwik_admin_custom_var_table($variables) {
+  $form = $variables['form'];
 
   $header = array(
-    array('data' => t('slot')),
-    array('data' => t('name')),
-    array('data' => t('value')),
-    array('data' => t('scope')),
+    array('data' => t('Slot')),
+    array('data' => t('Name')),
+    array('data' => t('Value')),
+    array('data' => t('Scope')),
   );
 
   $rows = array();
@@ -428,8 +442,7 @@ function theme_piwik_admin_custom_var_table($form) {
     );
   }
 
-  $output = theme('table', $header, $rows);
-  $output .= drupal_render($form['piwik_custom_var']);
+  $output = theme('table', array('header' => $header, 'rows' => $rows));
   $output .= drupal_render($form['piwik_custom_var_description']);
   $output .= drupal_render($form['piwik_custom_var_token_tree']);
 
@@ -459,11 +472,9 @@ function piwik_token_element_validate(&$element, &$form_state) {
   }
 
   $tokens = token_scan($value);
-  $title = empty($element['#title']) ? $element['#parents'][0] : $element['#title'];
-
   $invalid_tokens = _piwik_get_forbidden_tokens($tokens);
   if ($invalid_tokens) {
-    form_error($element, t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => $title, '@invalid-tokens' => implode(', ', $invalid_tokens))));
+    form_error($element, t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => $element['#title'], '@invalid-tokens' => implode(', ', $invalid_tokens))));
   }
 
   return $element;
@@ -473,17 +484,12 @@ function _piwik_get_forbidden_tokens($value) {
   $invalid_tokens = array();
   $value_tokens = is_string($value) ? token_scan($value) : $value;
 
-  // For porting/compatibility reasons with _piwik_contains_forbidden_token()
-  // the leading and trailing token separator need to be added to every value.
-  $value_tokens = token_prepare_tokens($value_tokens);
-
-/*  foreach ($value_tokens as $token) {
-    // If token is forbidden, add it to invalid tokens array.
-    if (_piwik_contains_forbidden_token($token)) {
-      $invalid_tokens[] = $token;
+  foreach ($value_tokens as $type => $tokens) {
+    if (array_filter($tokens, '_piwik_contains_forbidden_token')) {
+      $invalid_tokens = array_merge($invalid_tokens, array_values($tokens));
     }
   }
-*/
+
   array_unique($invalid_tokens);
   return $invalid_tokens;
 }
@@ -496,7 +502,6 @@ function _piwik_get_forbidden_tokens($value) {
  * @return boolean
  *   TRUE if blacklisted token has been found, otherwise FALSE.
  */
-/*
 function _piwik_contains_forbidden_token($token_string) {
   // List of strings in tokens with personal identifying information not allowed
   // for privacy reasons. See section 8.1 of the Google Analytics terms of use
@@ -510,24 +515,25 @@ function _piwik_contains_forbidden_token($token_string) {
   //
   // TODO: If someone have better ideas, share them, please!
   $token_blacklist = array(
- 
- /** All tokens are allowed
-   /**
-   	'author-uid]',
-    'author-name',
-    'author-mail',
-    'author-homepage]',
-    '[user-name]',
-    '[user-id]',
-    '[user-mail]',
-    // [user] tokens
-    '[user]',
-    '[user-raw]',
-    '[uid]',
-    '[mail]',
-    '[account-url]',
-    '[account-edit]', 
-  ); 
+    ':author]',
+    ':author:edit-url]',
+    ':author:url]',
+    ':author:path]',
+    ':fid]',
+    ':mail]',
+    ':name]',
+    ':uid]',
+    ':one-time-login-url]',
+    ':owner]',
+    ':owner:cancel-url]',
+    ':owner:edit-url]',
+    ':owner:url]',
+    ':owner:path]',
+    'user:cancel-url]',
+    'user:edit-url]',
+    'user:url]',
+    'user:path]',
+  );
 
   return preg_match('/' . implode('|', array_map('preg_quote', $token_blacklist)) . '/i', $token_string);
-} */ 
+}
diff --git a/piwik.js b/piwik.js
deleted file mode 100644
index 48f27b7..0000000
--- a/piwik.js
+++ /dev/null
@@ -1,49 +0,0 @@
-
-$(document).ready(function() {
-
-  // Attach onclick event to document only and catch clicks on all elements.
-  $(document.body).click(function(event) {
-    // Catch only the first parent link of a clicked element.
-    $(event.target).parents("a:first,area:first").andSelf().filter("a,area").each(function() {
-
-      var pa = Drupal.settings.piwik;
-      // Expression to check for absolute internal links.
-      var isInternal = new RegExp("^(https?):\/\/" + window.location.host, "i");
-      // Expression to check for special links like gotwo.module /go/* links.
-      var isInternalSpecial = new RegExp("(\/go\/.*)$", "i");
-      // Expression to check for download links.
-      var isDownload = new RegExp("\\.(" + pa.trackDownloadExtensions + ")$", "i");
-
-      // Is the clicked URL internal?
-      if (isInternal.test(this.href)) {
-        // Is download tracking activated and the file extension configured for download tracking?
-        if (pa.trackDownload && isDownload.test(this.href)) {
-          // Download link clicked.
-          var extension = isDownload.exec(this.href);
-          _paq.push(["_trackEvent", "Downloads", extension[1].toUpperCase(), this.href.replace(isInternal, '')]);
-        }
-        else if (isInternalSpecial.test(this.href)) {
-          // Keep the internal URL for Google Analytics website overlay intact.
-          _paq.push(["_trackPageview", this.href.replace(isInternal, '')]);
-        }
-      }
-      else {
-        if (pa.trackMailto && $(this).is("a[href^=mailto:],area[href^=mailto:]")) {
-          // Mailto link clicked.
-          _paq.push(["_trackEvent", "Mails", "Click", this.href.substring(7)]);
-        }
-        else if (pa.trackOutgoing && this.href) {
-          if (pa.trackOutboundAsPageview) {
-            // Track all external links as page views after URL cleanup.
-            // Currently required, if click should be tracked as goal.
-            _paq.push(["_trackPageview", '/outbound/' + this.href.replace(/^(https?|ftp|news|nntp|telnet|irc|ssh|sftp|webcal):\/\//i, '').split('/').join('--')]);
-          }
-          else {
-            // External link clicked.
-            _paq.push(["_trackEvent", "Outbound links", "Click", this.href]);
-          }
-        }
-      }
-    });
-  });
-});
diff --git a/piwik.module b/piwik.module
index bbd6f2d..ed39b37 100644
--- a/piwik.module
+++ b/piwik.module
@@ -6,57 +6,92 @@
  * Adds the required Javascript to the bottom of all your Drupal pages
  * to allow tracking by the Piwik statistics package.
  *
- * @author: Alexander Hass <www.hass.de>
+ * @author: Alexander Hass <http://drupal.org/user/85918>
  */
 
 define('PK_TRACKFILES_EXTENSIONS', '7z|aac|arc|arj|asf|asx|avi|bin|csv|doc|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mp(2|3|4|e?g)|mov(ie)?|msi|msp|pdf|phps|png|ppt|qtm?|ra(m|r)?|sea|sit|tar|tgz|torrent|txt|wav|wma|wmv|wpd|xls|xml|z|zip');
 
 /**
- * Implementation of hook_help().
+ * Implements hook_help().
  */
 function piwik_help($path, $arg) {
   switch ($path) {
-    case 'admin/settings/piwik':
+    case 'admin/config/system/piwik':
       return t('<a href="@pk_url">Piwik - Web analytics</a> is an open source (GPL license) web analytics software. It gives interesting reports on your website visitors, your popular pages, the search engines keywords they used, the language they speak... and so much more. Piwik aims to be an open source alternative to Google Analytics.', array('@pk_url' => 'http://www.piwik.org/'));
   }
 }
 
 /**
- * Implementation of hook_perm().
+ * Implements hook_theme().
  */
-function piwik_perm() {
-  return array('administer piwik', 'opt-in or out of tracking', 'use PHP for tracking visibility');
+function piwik_theme() {
+  return array(
+    'piwik_admin_custom_var_table' => array(
+      'render element' => 'form',
+    ),
+  );
+}
+
+/**
+ * Implements hook_permission().
+ */
+function piwik_permission() {
+  return array(
+    'administer piwik' => array(
+      'title' => t('Administer Piwik'),
+      'description' => t('Perform maintenance tasks for Piwik.'),
+    ),
+    'opt-in or out of tracking' => array(
+      'title' => t('Opt-in or out of tracking'),
+      'description' => t('Allow users to decide if tracking code will be added to pages or not.'),
+    ),
+    'use PHP for tracking visibility' => array(
+      'title' => t('Use PHP for tracking visibility'),
+      'description' => t('Enter PHP code in the field for tracking visibility settings. %warning', array('%warning' => t('Warning: Give to trusted roles only; this permission has security implications.'))),
+    ),
+  );
 }
 
 /**
- * Implementation of hook_menu().
+ * Implements hook_menu().
  */
 function piwik_menu() {
-  $items['admin/settings/piwik'] = array(
+  $items['admin/config/system/piwik'] = array(
     'title' => 'Piwik',
     'description' => 'Configure the settings used to generate your Piwik tracking code.',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('piwik_admin_settings_form'),
     'access arguments' => array('administer piwik'),
-    'file' => 'piwik.admin.inc',
     'type' => MENU_NORMAL_ITEM,
+    'file' => 'piwik.admin.inc',
   );
 
   return $items;
 }
 
 /**
- * Implementation of hook_footer() to insert Javascript at the end of the page.
+ * Implements hook_page_alter() to insert JavaScript to the appropriate scope/region of the page.
  */
-function piwik_footer($main = 0) {
+function piwik_page_alter(&$page) {
   global $user;
 
   $id = variable_get('piwik_site_id', '');
 
-  if (!empty($id) && _piwik_visibility_pages() && _piwik_visibility_user($user)) {
+  // Get page status code for visibility filtering.
+  $status = drupal_get_http_header('Status');
+  $trackable_status_codes = array(
+    '403 Forbidden',
+    '404 Not Found',
+  );
+
+  // 1. Check if the piwik account number has a value.
+  // 2. Track page views based on visibility value.
+  // 3. Check if we should track the currently active user's role.
+  if (!empty($id) && (_piwik_visibility_pages() || in_array($status, $trackable_status_codes)) && _piwik_visibility_user($user)) {
 
     $url_http = variable_get('piwik_url_http', '');
     $url_https = variable_get('piwik_url_https', '');
+    $scope = variable_get('piwik_js_scope', 'footer');
 
     $set_custom_url = '';
     $set_document_title = '';
@@ -78,16 +113,13 @@ function piwik_footer($main = 0) {
         // Encode title, at least to keep "/" intact.
         $titles = array_map('urlencode', $titles);
 
-        $set_document_title = drupal_to_js(implode('/', $titles));
+        $set_document_title = drupal_json_encode(implode('/', $titles));
       }
     }
 
     // Site search tracking support.
-    if (module_exists('search') && variable_get('piwik_site_search', FALSE) && arg(0) == 'search' && $keys = search_get_keys()) {
-      $set_custom_url = drupal_to_js(url('search', array('query' => 'query='. drupal_urlencode($keys), 'absolute' => TRUE)));
-
-      // hook_preprocess_search_results() is not executed if search result is
-      // empty. Make sure the counter is set to 0 if there are no results.
+    if (module_exists('search') && variable_get('piwik_site_search', FALSE) && arg(0) == 'search' && $keys = piwik_search_get_keys()) {
+      $set_custom_url = drupal_json_encode(url('search', array('query' => array('query' => $keys), 'absolute' => TRUE)));
       $set_custom_data[] = '{SiteSearch_Results: (window.piwik_search_results) ? window.piwik_search_results : 0 }';
     }
 
@@ -97,72 +129,66 @@ function piwik_footer($main = 0) {
       // Check we have a node object, it supports translation, and its
       // translated node ID (tnid) doesn't match its own node ID.
       $node = menu_get_object();
-      if ($node && translation_supported_type($node->type) && isset($node->tnid) && ($node->tnid != $node->nid)) {
+      if ($node && translation_supported_type($node->type) && !empty($node->tnid) && ($node->tnid != $node->nid)) {
         $source_node = node_load($node->tnid);
         $languages = language_list();
-        $set_custom_url = drupal_to_js(url('node/'. $source_node->nid, array('language' => $languages[$source_node->language], 'absolute' => TRUE)));
+        $set_custom_url = drupal_json_encode(url('node/' . $source_node->nid, array('language' => $languages[$source_node->language], 'absolute' => TRUE)));
       }
     }
 
     // Track access denied (403) and file not found (404) pages.
-    if (function_exists('drupal_get_headers')) {
-      $headers = drupal_get_headers();
-      if (strstr($headers, '403 Forbidden')) {
-        $set_document_title = '"403/URL = " + String(document.location.pathname + document.location.search).replace(/\//g,"%2f") + "/From = " + String(document.referrer).replace(/\//g,"%2f")';
-      }
-      elseif (strstr($headers, '404 Not Found')) {
-        $set_document_title = '"404/URL = " + String(document.location.pathname + document.location.search).replace(/\//g,"%2f") + "/From = " + String(document.referrer).replace(/\//g,"%2f")';
-      }
+    if ($status == '403 Forbidden') {
+      $set_document_title = '"403/URL = " + String(document.location.pathname + document.location.search).replace(/\//g,"%2f") + "/From = " + String(document.referrer).replace(/\//g,"%2f")';
+    }
+    elseif ($status == '404 Not Found') {
+      $set_document_title = '"404/URL = " + String(document.location.pathname + document.location.search).replace(/\//g,"%2f") + "/From = " + String(document.referrer).replace(/\//g,"%2f")';
     }
 
-    // Add any custom code snippets if specified.
-    $codesnippet_before = variable_get('piwik_codesnippet_before', '');
-    $codesnippet_after = variable_get('piwik_codesnippet_after', '');
-
- // Add custom variables.
+    // Add custom variables.
     $piwik_custom_vars = variable_get('piwik_custom_var', array());
-    $custom_var = '';
+    $custom_variable = '';
     for ($i = 1; $i < 6; $i++) {
       $custom_var_name = !empty($piwik_custom_vars['slots'][$i]['name']) ? $piwik_custom_vars['slots'][$i]['name'] : '';
       if (!empty($custom_var_name)) {
         $custom_var_value = !empty($piwik_custom_vars['slots'][$i]['value']) ? $piwik_custom_vars['slots'][$i]['value'] : '';
-        $custom_var_scope = !empty($piwik_custom_vars['slots'][$i]['scope']) ? $piwik_custom_vars['slots'][$i]['scope'] : 3;
+        $custom_var_scope = !empty($piwik_custom_vars['slots'][$i]['scope']) ? $piwik_custom_vars['slots'][$i]['scope'] : 'visit';
 
-        if (module_exists('token')) {
-          $node = menu_get_object();
-          $custom_var_value = token_replace_multiple($custom_var_value, array('global' => NULL, 'user' => $user, 'node' => $node), '[', ']', array('clear' => TRUE));
+        $types = array();
+        $node = menu_get_object();
+        if (is_object($node)) {
+          $types += array('node' => $node);
         }
+        $custom_var_value = token_replace($custom_var_value, $types, array('clear' => TRUE));
 
         // Suppress empty custom variables.
         if (!is_numeric($custom_var_value) && empty($custom_var_value)) {
           continue;
         }
 
-        // The length of the string used for the 'name' and the length of the
-        // string used for the 'value' must not exceed 64 bytes after url encoding.
-        $name_length = drupal_strlen(rawurlencode($custom_var_name));
-        $tmp_value = rawurlencode($custom_var_value);
-        $value_length = drupal_strlen($tmp_value);
-        if ($name_length + $value_length > 64) {
-          // Trim value and remove fragments of url encoding.
-          $tmp_value = rtrim(substr($tmp_value, 0, 63 - $name_length), '%0..9A..F');
-          $custom_var_value = urldecode($tmp_value);
-        }
-
-        $custom_var_name = drupal_to_js($custom_var_name);
-        $custom_var_value = drupal_to_js($custom_var_value);
-/*        $custom_var .= "_paq.push(['_setCustomVar', $i, $custom_var_name, $custom_var_value, $custom_var_scope]);"; */
-        $custom_var .= "_paq.push(['setCustomVariable', $i, $custom_var_name, $custom_var_value]);";
+        // Custom variables names and values are limited to 100 characters in
+        // length each. It is recommended to store values that are as small as
+        // possible to ensure that the Piwik Tracking request URL doesn't go
+        // over the URL limit for the webserver or browser.
+        $custom_var_name = rtrim(substr($custom_var_name, 0, 100));
+        $custom_var_value = rtrim(substr($custom_var_value, 0, 100));
+
+        $custom_var_name = drupal_json_encode($custom_var_name);
+        $custom_var_value = drupal_json_encode($custom_var_value);
+        $custom_var_scope = drupal_json_encode($custom_var_scope);
+        $custom_variable .= "_paq.push(['setCustomVariable', $i, $custom_var_name, $custom_var_value, $custom_var_scope]);";
       }
     }
 
+    // Add any custom code snippets if specified.
+    $codesnippet_before = variable_get('piwik_codesnippet_before', '');
+    $codesnippet_after = variable_get('piwik_codesnippet_after', '');
+
     // Build tracker code. See http://piwik.org/docs/javascript-tracking/#toc-asynchronous-tracking
     $script = 'var _paq = _paq || [];';
     $script .= '(function(){';
     $script .= 'var u=(("https:" == document.location.protocol) ? "' . check_url($url_https) . '" : "' . check_url($url_http) . '");';
-    $script .= '_paq.push(["setSiteId", ' . drupal_to_js(variable_get('piwik_site_id', '')) . ']);';
+    $script .= '_paq.push(["setSiteId", ' . drupal_json_encode(variable_get('piwik_site_id', '')) . ']);';
     $script .= '_paq.push(["setTrackerUrl", u+"piwik.php"]);';
-    
 
     // Set custom data.
     if (!empty($set_custom_data)) {
@@ -181,7 +207,7 @@ function piwik_footer($main = 0) {
 
     // Custom file download extensions.
     if ((variable_get('piwik_track', 1)) && !(variable_get('piwik_trackfiles_extensions', PK_TRACKFILES_EXTENSIONS) == PK_TRACKFILES_EXTENSIONS)) {
-      $script .= '_paq.push(["setDownloadExtensions", ' . drupal_to_js(variable_get('piwik_trackfiles_extensions', PK_TRACKFILES_EXTENSIONS)) . ']);';
+      $script .= '_paq.push(["setDownloadExtensions", ' . drupal_json_encode(variable_get('piwik_trackfiles_extensions', PK_TRACKFILES_EXTENSIONS)) . ']);';
     }
 
     // Disable tracking for visitors who have opted out from tracking via DNT (Do-Not-Track) header.
@@ -196,16 +222,18 @@ function piwik_footer($main = 0) {
     // Per RFC 2109, cookie domains must contain at least one dot other than the
     // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
     if ($domain_mode == 1 && count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
-      $script .= '_paq.push(["setCookieDomain", ' . drupal_to_js($cookie_domain) . ']);';
+      $script .= '_paq.push(["setCookieDomain", ' . drupal_json_encode($cookie_domain) . ']);';
     }
 
+    // Ordering $custom_variable before $codesnippet_before allows users to add
+    // custom code snippets that may use deleteCustomVariable() and/or getCustomVariable().
+    if (!empty($custom_variable)) {
+      $script .= $custom_variable;
+    }
     if (!empty($codesnippet_before)) {
       $script .= $codesnippet_before;
     }
-		if (!empty($custom_var)) {
-      $script .= $custom_var;
-    }
-    
+
     $script .= '_paq.push(["trackPageView"]);';
 
     // Add link tracking.
@@ -224,12 +252,12 @@ function piwik_footer($main = 0) {
     $script .= 'g.async=true;';
 
     // Should a local cached copy of the tracking code be used?
-    if (variable_get('piwik_cache', 0) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC) && $url = _piwik_cache($url_http .'piwik.js')) {
+    if (variable_get('piwik_cache', 0) && $url = _piwik_cache($url_http . 'piwik.js')) {
       // A dummy query-string is added to filenames, to gain control over
       // browser-caching. The string changes on every update or full cache
       // flush, forcing browsers to load a new copy of the files, as the
       // URL changed.
-      $query_string = '?'. substr(variable_get('css_js_query_string', '0'), 0, 1);
+      $query_string = '?' . variable_get('css_js_query_string', '0');
 
       $script .= 'g.src="' . $url . $query_string . '";';
     }
@@ -240,74 +268,81 @@ function piwik_footer($main = 0) {
     $script .= 's.parentNode.insertBefore(g,s);';
     $script .= '})();';
 
-    drupal_add_js($script, 'inline', 'footer');
+    // Add tracker code to scope.
+    drupal_add_js($script, array('scope' => $scope, 'type' => 'inline'));
   }
 }
 
 /**
- * Implementation of hook_user().
+ * Implement hook_form_FORM_ID_alter().
  *
  * Allow users to decide if tracking code will be added to pages or not.
  */
-function piwik_user($type, $edit, &$account, $category = NULL) {
-  switch ($type) {
-    case 'form':
-      if ($category == 'account' && user_access('opt-in or out of tracking') && ($custom = variable_get('piwik_custom', 0)) != 0 && _piwik_visibility_roles($account)) {
-        $form['piwik'] = array(
-          '#type' => 'fieldset',
-          '#title' => t('Piwik configuration'),
-          '#weight' => 3,
-          '#collapsible' => TRUE,
-          '#tree' => TRUE
-        );
-
-        switch ($custom) {
-          case 1:
-            $description = t('Users are tracked by default, but you are able to opt out.');
-            break;
-
-          case 2:
-            $description = t('Users are <em>not</em> tracked by default, but you are able to opt in.');
-            break;
-        }
+function piwik_form_user_profile_form_alter(&$form, &$form_state) {
+  $account = $form['#user'];
+  $category = $form['#user_category'];
+
+  if ($category == 'account' && user_access('opt-in or out of tracking') && ($custom = variable_get('piwik_custom', 0)) != 0 && _piwik_visibility_roles($account)) {
+    $form['piwik'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Piwik configuration'),
+      '#weight' => 3,
+      '#collapsible' => TRUE,
+      '#tree' => TRUE
+    );
+
+    switch ($custom) {
+      case 1:
+        $description = t('Users are tracked by default, but you are able to opt out.');
+        break;
+
+      case 2:
+        $description = t('Users are <em>not</em> tracked by default, but you are able to opt in.');
+        break;
+    }
 
-        $form['piwik']['custom'] = array(
-          '#type' => 'checkbox',
-          '#title' => t('Enable user tracking'),
-          '#description' => $description,
-          '#default_value' => isset($account->piwik['custom']) ? $account->piwik['custom'] : ($custom == 1),
-        );
+    $form['piwik']['custom'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Enable user tracking'),
+      '#description' => $description,
+      '#default_value' => isset($account->data['piwik']['custom']) ? $account->data['piwik']['custom'] : ($custom == 1),
+    );
 
-        return $form;
-      }
-      break;
+    return $form;
+  }
+}
 
+/**
+ * Implements hook_user_presave().
+ */
+function piwik_user_presave(&$edit, $account, $category) {
+  if (isset($edit['piwik']['custom'])) {
+    $edit['data']['piwik']['custom'] = $edit['piwik']['custom'];
   }
 }
 
 /**
- * Implementation of hook_cron().
+ * Implements hook_cron().
  */
 function piwik_cron() {
   // Regenerate the piwik.js every day.
-  if (time() - variable_get('piwik_last_cache', 0) >= 86400 && variable_get('piwik_cache', 0)) {
-    _piwik_cache(variable_get('piwik_url_http', '') . '/piwik.js', TRUE);
-    variable_set('piwik_last_cache', time());
+  if (REQUEST_TIME - variable_get('piwik_last_cache', 0) >= 86400 && variable_get('piwik_cache', 0)) {
+    _piwik_cache(variable_get('piwik_url_http', '') . 'piwik.js', TRUE);
+    variable_set('piwik_last_cache', REQUEST_TIME);
   }
 }
 
 /**
- * Implementation of hook_preprocess_search_results().
+ * Implements hook_preprocess_search_results().
  *
- * Collects the number of search results. It need to be noted, that this
- * function is not executed if the search result is empty.
+ * Collects and adds the number of search results to the head.
  */
 function piwik_preprocess_search_results(&$variables) {
   // There is no search result $variable available that hold the number of items
   // found. But the pager item mumber can tell the number of search results.
   global $pager_total_items;
 
-  drupal_add_js('window.piwik_search_results = ' . intval($pager_total_items[0]) . ';', 'inline', 'header');
+  drupal_add_js('window.piwik_search_results = ' . intval($pager_total_items[0]) . ';', array('type' => 'inline', 'group' => JS_LIBRARY-1));
 }
 
 /**
@@ -321,8 +356,7 @@ function piwik_preprocess_search_results(&$variables) {
  *   The path to the local javascript file on success, boolean FALSE on failure.
  */
 function _piwik_cache($location, $sync_cached_file = FALSE) {
-
-  $path = file_create_path('piwik');
+  $path = 'public://piwik';
   $file_destination = $path . '/' . basename($location);
 
   if (!file_exists($file_destination) || $sync_cached_file) {
@@ -332,12 +366,12 @@ function _piwik_cache($location, $sync_cached_file = FALSE) {
     if ($result->code == 200) {
       if (file_exists($file_destination)) {
         // Synchronize tracking code and and replace local file if outdated.
-        $data_hash_local = md5(file_get_contents($file_destination));
-        $data_hash_remote = md5($result->data);
+        $data_hash_local = drupal_hash_base64(file_get_contents($file_destination));
+        $data_hash_remote = drupal_hash_base64($result->data);
         // Check that the files directory is writable.
-        if ($data_hash_local != $data_hash_remote && file_check_directory($path)) {
+        if ($data_hash_local != $data_hash_remote && file_prepare_directory($path)) {
           // Save updated tracking code file to disk.
-          file_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
+          file_unmanaged_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
           watchdog('piwik', 'Locally cached tracking code file has been updated.', array(), WATCHDOG_INFO);
 
           // Change query-strings on css/js files to enforce reload for all users.
@@ -346,21 +380,21 @@ function _piwik_cache($location, $sync_cached_file = FALSE) {
       }
       else {
         // Check that the files directory is writable.
-        if (file_check_directory($path, FILE_CREATE_DIRECTORY)) {
+        if (file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
           // There is no need to flush JS here as core refreshes JS caches
           // automatically, if new files are added.
-          file_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
+          file_unmanaged_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
           watchdog('piwik', 'Locally cached tracking code file has been saved.', array(), WATCHDOG_INFO);
 
           // Return the local JS file path.
-          return base_path() . $file_destination;
+          return file_create_url($file_destination);
         }
       }
     }
   }
   else {
     // Return the local JS file path.
-    return base_path() . $file_destination;
+    return file_create_url($file_destination);
   }
 }
 
@@ -368,10 +402,10 @@ function _piwik_cache($location, $sync_cached_file = FALSE) {
  * Delete cached files and directory.
  */
 function piwik_clear_js_cache() {
-  $path = file_create_path('piwik');
-  if (file_check_directory($path)) {
-    file_scan_directory($path, '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
-    rmdir($path);
+  $path = 'public://piwik';
+  if (file_prepare_directory($path)) {
+    file_scan_directory($path, '/.*/', array('callback' => 'file_unmanaged_delete'));
+    drupal_rmdir($path);
 
     // Change query-strings on css/js files to enforce reload for all users.
     _drupal_flush_css_js();
@@ -381,6 +415,23 @@ function piwik_clear_js_cache() {
 }
 
 /**
+ * Helper function for grabbing search keys. Function is missing in D7.
+ *
+ * http://api.drupal.org/api/function/search_get_keys/6
+ */
+function piwik_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'], 3);
+    $keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys'];
+    $return = count($path) == 3 ? $path[2] : $keys;
+  }
+  return $return;
+}
+
+/**
  * Tracking visibility check for an user object.
  *
  * @param $account
@@ -397,8 +448,8 @@ function _piwik_visibility_user($account) {
 
     // Use the user's block visibility setting, if necessary.
     if (($custom = variable_get('piwik_custom', 0)) != 0) {
-      if ($account->uid && isset($account->piwik['custom'])) {
-        $enabled = $account->piwik['custom'];
+      if ($account->uid && isset($account->data['piwik']['custom'])) {
+        $enabled = $account->data['piwik']['custom'];
       }
       else {
         $enabled = ($custom == 1);
@@ -473,8 +524,11 @@ function _piwik_visibility_pages() {
         // is displayed only on those pages listed in $pages.
         $page_match = !($visibility xor $page_match);
       }
+      elseif (module_exists('php')) {
+        $page_match = php_eval($setting_pages);
+      }
       else {
-        $page_match = drupal_eval($setting_pages);
+        $page_match = FALSE;
       }
     }
     else {
@@ -533,14 +587,3 @@ function _piwik_get_hierarchy_titles() {
 
   return $titles;
 }
-
-/**
- * Implementation of hook_theme().
- */
-function piwik_theme() {
-  return array(
-    'piwik_admin_custom_var_table' => array(
-      'arguments' => array('form' => NULL),
-    ),
-  );
-}
