html fixes and reeneable downloads count
html fixes and reeneable downloads count

--- a/ckanext/ga_report/command.py
+++ b/ckanext/ga_report/command.py
@@ -52,9 +52,7 @@
         assuming it is correct.
         """
         from ga_auth import init_service
-        init_service('token.dat',
-                      self.args[0] if self.args
-                                   else 'credentials.json')
+        init_service('token.dat', 'credentials.json')
 
 class FixTimePeriods(CkanCommand):
     """
@@ -115,6 +113,7 @@
                                default=False,
                                dest='skip_url_stats',
                                help='Skip the download of URL data - just do site-wide stats')
+        self.token = ""
 
     def command(self):
         self._load_config()
@@ -129,14 +128,14 @@
             return
 
         try:
-            svc = init_service(ga_token_filepath, None)
+            self.token, svc = init_service(ga_token_filepath, None)
         except TypeError:
             print ('Have you correctly run the getauthtoken task and '
                    'specified the correct token file in the CKAN config under '
                    '"googleanalytics.token.filepath"?')
             return
 
-        downloader = DownloadAnalytics(svc, profile_id=get_profile_id(svc),
+        downloader = DownloadAnalytics(svc, self.token, profile_id=get_profile_id(svc),
                                        delete_first=self.options.delete_first,
                                        skip_url_stats=self.options.skip_url_stats)
 

--- a/ckanext/ga_report/controller.py
+++ b/ckanext/ga_report/controller.py
@@ -211,7 +211,7 @@
             graph_dict = {}
             for stat in graph_query:
                 graph_dict[ stat.key ] = graph_dict.get(stat.key,{
-                    'name':stat.key, 
+                    'name':stat.key,
                     'raw': {}
                     })
                 graph_dict[ stat.key ]['raw'][stat.period_name] = float(stat.value)
@@ -304,7 +304,9 @@
         graph_data = _get_top_publishers_graph()
         c.top_publishers_graph = json.dumps( _to_rickshaw(graph_data) )
 
-        return render('ga_report/publisher/index.html')
+        x =  render('ga_report/publisher/index.html')
+
+        return x
 
     def _get_packages(self, publisher=None, month='', count=-1):
         '''Returns the datasets in order of views'''
@@ -340,7 +342,8 @@
                         downloads += int(x.value)
                 else:
                     downloads = 'No data'
-                top_packages.append((package, entry.pageviews, entry.visits, downloads))
+                if package.private == False:
+                    top_packages.append((package, entry.pageviews, entry.visits, downloads))
             else:
                 log.warning('Could not find package associated package')
 
@@ -412,7 +415,7 @@
 def _to_rickshaw(data, percentageMode=False):
     if data==[]:
         return data
-    # x-axis is every month in c.months. Note that data might not exist 
+    # x-axis is every month in c.months. Note that data might not exist
     # for entire history, eg. for recently-added datasets
     x_axis = [x[0] for x in c.months]
     x_axis.reverse() # Ascending order
@@ -444,10 +447,10 @@
         for i in range(len(x_axis)):
             x = _get_unix_epoch(x_axis[i])
             y = 0
-            for series in others: 
+            for series in others:
                 y += series['data'][i]['y']
             data_other.append({'x':x,'y':y})
-        data.append({ 
+        data.append({
             'name':'Other',
             'data': data_other
             })
@@ -505,8 +508,8 @@
 
     # Query for a history graph of these department ids
     q = model.Session.query(
-            GA_Url.department_id, 
-            GA_Url.period_name, 
+            GA_Url.department_id,
+            GA_Url.period_name,
             func.sum(cast(GA_Url.pageviews,sqlalchemy.types.INT)))\
         .filter( GA_Url.department_id.in_(department_ids) )\
         .filter( GA_Url.url.like('/dataset/%') )\
@@ -529,7 +532,7 @@
     '''
     publishers = []
     for pub in model.Session.query(model.Group).\
-               filter(model.Group.type=='publisher').\
+               filter(model.Group.type=='organization').\
                filter(model.Group.state=='active').\
                order_by(model.Group.name):
         publishers.append((pub.name, pub.title))

--- a/ckanext/ga_report/download_analytics.py
+++ b/ckanext/ga_report/download_analytics.py
@@ -1,7 +1,10 @@
 import os
 import logging
 import datetime
+import httplib
 import collections
+import requests
+import json
 from pylons import config
 from ga_model import _normalize_url
 import ga_model
@@ -18,13 +21,14 @@
 class DownloadAnalytics(object):
     '''Downloads and stores analytics info'''
 
-    def __init__(self, service=None, profile_id=None, delete_first=False,
+    def __init__(self, service=None, token=None, profile_id=None, delete_first=False,
                  skip_url_stats=False):
         self.period = config['ga-report.period']
         self.service = service
         self.profile_id = profile_id
         self.delete_first = delete_first
         self.skip_url_stats = skip_url_stats
+        self.token = token
 
     def specific_month(self, date):
         import calendar
@@ -117,13 +121,13 @@
                 accountName = config.get('googleanalytics.account')
 
                 log.info('Downloading analytics for dataset views')
-                data = self.download(start_date, end_date, '~/%s/dataset/[a-z0-9-_]+' % accountName)
+                data = self.download(start_date, end_date, '~^/dataset/[a-z0-9-_]+')
 
                 log.info('Storing dataset views (%i rows)', len(data.get('url')))
                 self.store(period_name, period_complete_day, data, )
 
                 log.info('Downloading analytics for publisher views')
-                data = self.download(start_date, end_date, '~/%s/publisher/[a-z0-9-_]+' % accountName)
+                data = self.download(start_date, end_date, '~^/organization/[a-z0-9-_]+')
 
                 log.info('Storing publisher views (%i rows)', len(data.get('url')))
                 self.store(period_name, period_complete_day, data,)
@@ -149,21 +153,31 @@
         metrics = 'ga:entrances'
         sort = '-ga:entrances'
 
-        # Supported query params at
-        # https://developers.google.com/analytics/devguides/reporting/core/v3/reference
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 filters=query,
-                                 start_date=start_date,
-                                 metrics=metrics,
-                                 sort=sort,
-                                 dimensions="ga:landingPagePath,ga:socialNetwork",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = dict(ids='ga:' + self.profile_id,
+                       filters=query,
+                       metrics=metrics,
+                       sort=sort,
+                       dimensions="ga:landingPagePath,ga:socialNetwork",
+                       max_results=10000)
+
+            args['start-date'] = start_date
+            args['end-date'] = end_date
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
+
         data = collections.defaultdict(list)
         rows = results.get('rows',[])
         for row in rows:
-            url = _normalize_url('http:/' + row[0])
+            url = row[0]
             data[url].append( (row[1], int(row[2]),) )
         ga_model.update_social(period_name, data)
 
@@ -178,23 +192,34 @@
 
         # Supported query params at
         # https://developers.google.com/analytics/devguides/reporting/core/v3/reference
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 filters=query,
-                                 start_date=start_date,
-                                 metrics=metrics,
-                                 sort=sort,
-                                 dimensions="ga:pagePath",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+	# https://ga-dev-tools.appspot.com/explorer/
+        try:
+            args = {}
+            args["sort"] = "-ga:pageviews"
+            args["max-results"] = 100000
+            args["dimensions"] = "ga:pagePath"
+            args["start-date"] = start_date
+            args["end-date"] = end_date
+            args["metrics"] = metrics
+            args["ids"] = "ga:" + self.profile_id
+            args["filters"] = query
+            args["alt"] = "json"
+            print args
+            results = self._get_json(args)
+
+        except Exception, e:
+            log.exception(e)
+            return dict(url=[])
 
         packages = []
         log.info("There are %d results" % results['totalResults'])
-        for entry in results.get('rows'):
+	if results['totalResults'] > 0:
+          for entry in results.get('rows'):
             (loc,pageviews,visits) = entry
-            url = _normalize_url('http:/' + loc) # strips off domain e.g. www.data.gov.uk or data.gov.uk
-
-            if not url.startswith('/dataset/') and not url.startswith('/publisher/'):
+            #url = _normalize_url('http:/' + loc) # strips off domain e.g. www.data.gov.uk or data.gov.uk
+            url = loc
+	    #print url
+            if not url.startswith('/dataset/') and not url.startswith('/organization/'):
                 # filter out strays like:
                 # /data/user/login?came_from=http://data.gov.uk/dataset/os-code-point-open
                 # /403.html?page=/about&from=http://data.gov.uk/publisher/planning-inspectorate
@@ -226,25 +251,78 @@
             data[key] = data.get(key,0) + result[1]
         return data
 
+    def _get_json(self, params, prev_fail=False):
+        ga_token_filepath = os.path.expanduser(config.get('googleanalytics.token.filepath', ''))
+        if not ga_token_filepath:
+            print 'ERROR: In the CKAN config you need to specify the filepath of the ' \
+                'Google Analytics token file under key: googleanalytics.token.filepath'
+            return
+
+        log.info("Trying to refresh our OAuth token")
+        try:
+            from ga_auth import init_service
+            self.token, svc = init_service(ga_token_filepath, None)
+            log.info("OAuth token refreshed")
+        except Exception, auth_exception:
+            log.error("Oauth refresh failed")
+            log.exception(auth_exception)
+            return
+
+        try:
+            headers = {'authorization': 'Bearer ' + self.token}
+            r = requests.get("https://www.googleapis.com/analytics/v3/data/ga", params=params, headers=headers)
+            if r.status_code != 200:
+                log.info("STATUS: %s" % (r.status_code,))
+                log.info("CONTENT: %s" % (r.content,))
+                raise Exception("Request with params: %s failed" % params)
+
+            return json.loads(r.content)
+        except Exception, e:
+              log.exception(e)
+
+        return dict(url=[])
+
     def _totals_stats(self, start_date, end_date, period_name, period_complete_day):
         """ Fetches distinct totals, total pageviews etc """
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 metrics='ga:pageviews',
-                                 sort='-ga:pageviews',
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+        try:
+            args = {}
+            args["max-results"] = 100000
+            args["start-date"] = start_date
+            args["end-date"] = end_date
+            args["ids"] = "ga:" + self.profile_id
+
+            args["metrics"] = "ga:pageviews"
+            args["sort"] = "-ga:pageviews"
+            args["alt"] = "json"
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
         result_data = results.get('rows')
         ga_model.update_sitewide_stats(period_name, "Totals", {'Total page views': result_data[0][0]},
             period_complete_day)
 
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 metrics='ga:pageviewsPerVisit,ga:avgTimeOnSite,ga:percentNewVisits,ga:visits',
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = {}
+            args["max-results"] = 100000
+            args["start-date"] = start_date
+            args["end-date"] = end_date
+            args["ids"] = "ga:" + self.profile_id
+
+            args["metrics"] = "ga:pageviewsPerVisit,ga:avgTimeOnSite,ga:percentNewVisits,ga:visits"
+            args["alt"] = "json"
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
         result_data = results.get('rows')
         data = {
             'Pages per visit': result_data[0][0],
@@ -255,16 +333,29 @@
         ga_model.update_sitewide_stats(period_name, "Totals", data, period_complete_day)
 
         # Bounces from / or another configurable page.
-        path = '/%s%s' % (config.get('googleanalytics.account'),
-                          config.get('ga-report.bounce_url', '/'))
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 filters='ga:pagePath==%s' % (path,),
-                                 start_date=start_date,
-                                 metrics='ga:visitBounceRate',
-                                 dimensions='ga:pagePath',
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+        path = '/' #% (config.get('googleanalytics.account'),                          config.get('ga-report.bounce_url', '/'))
+
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = {}
+            args["max-results"] = 100000
+            args["start-date"] = start_date
+            args["end-date"] = end_date
+            args["ids"] = "ga:" + self.profile_id
+
+            args["filters"] = 'ga:pagePath==%s' % (path,)
+            args["dimensions"] = 'ga:pagePath'
+            args["metrics"] = "ga:visitBounceRate"
+            args["alt"] = "json"
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
         result_data = results.get('rows')
         if not result_data or len(result_data) != 1:
             log.error('Could not pinpoint the bounces for path: %s. Got results: %r',
@@ -280,14 +371,28 @@
 
     def _locale_stats(self, start_date, end_date, period_name, period_complete_day):
         """ Fetches stats about language and country """
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 metrics='ga:pageviews',
-                                 sort='-ga:pageviews',
-                                 dimensions="ga:language,ga:country",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = {}
+            args["max-results"] = 100000
+            args["start-date"] = start_date
+            args["end-date"] = end_date
+            args["ids"] = "ga:" + self.profile_id
+
+            args["dimensions"] = "ga:language,ga:country"
+            args["metrics"] = "ga:pageviews"
+            args["sort"] = "-ga:pageviews"
+            args["alt"] = "json"
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
         result_data = results.get('rows')
         data = {}
         for result in result_data:
@@ -308,15 +413,27 @@
 
         data = {}
 
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 filters='ga:eventAction==download',
-                                 metrics='ga:totalEvents',
-                                 sort='-ga:totalEvents',
-                                 dimensions="ga:eventLabel",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = {}
+            args["max-results"] = 100000
+            args["start-date"] = start_date
+            args["end-date"] = end_date
+            args["ids"] = "ga:" + self.profile_id
+
+            args["filters"] = 'ga:eventAction==download'
+            args["dimensions"] = "ga:eventLabel"
+            args["metrics"] = "ga:totalEvents"
+            args["alt"] = "json"
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
         result_data = results.get('rows')
         if not result_data:
             # We may not have data for this time period, so we need to bail
@@ -355,15 +472,25 @@
         log.info('Associating downloads of resource URLs with their respective datasets')
         process_result_data(results.get('rows'))
 
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 filters='ga:eventAction==download-cache',
-                                 metrics='ga:totalEvents',
-                                 sort='-ga:totalEvents',
-                                 dimensions="ga:eventLabel",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = dict( ids='ga:' + self.profile_id,
+                         filters='ga:eventAction==download-cache',
+                         metrics='ga:totalEvents',
+                         sort='-ga:totalEvents',
+                         dimensions="ga:eventLabel",
+                         max_results=10000)
+            args['start-date'] = start_date
+            args['end-date'] = end_date
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
         log.info('Associating downloads of cache resource URLs with their respective datasets')
         process_result_data(results.get('rows'), cached=False)
 
@@ -372,14 +499,25 @@
 
     def _social_stats(self, start_date, end_date, period_name, period_complete_day):
         """ Finds out which social sites people are referred from """
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 metrics='ga:pageviews',
-                                 sort='-ga:pageviews',
-                                 dimensions="ga:socialNetwork,ga:referralPath",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = dict( ids='ga:' + self.profile_id,
+                         metrics='ga:pageviews',
+                         sort='-ga:pageviews',
+                         dimensions="ga:socialNetwork,ga:referralPath",
+                         max_results=10000)
+            args['start-date'] = start_date
+            args['end-date'] = end_date
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
         result_data = results.get('rows')
         data = {}
         for result in result_data:
@@ -391,14 +529,24 @@
 
     def _os_stats(self, start_date, end_date, period_name, period_complete_day):
         """ Operating system stats """
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 metrics='ga:pageviews',
-                                 sort='-ga:pageviews',
-                                 dimensions="ga:operatingSystem,ga:operatingSystemVersion",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = dict( ids='ga:' + self.profile_id,
+                         metrics='ga:pageviews',
+                         sort='-ga:pageviews',
+                         dimensions="ga:operatingSystem,ga:operatingSystemVersion",
+                         max_results=10000)
+            args['start-date'] = start_date
+            args['end-date'] = end_date
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
         result_data = results.get('rows')
         data = {}
         for result in result_data:
@@ -416,14 +564,27 @@
 
     def _browser_stats(self, start_date, end_date, period_name, period_complete_day):
         """ Information about browsers and browser versions """
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 metrics='ga:pageviews',
-                                 sort='-ga:pageviews',
-                                 dimensions="ga:browser,ga:browserVersion",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = dict( ids='ga:' + self.profile_id,
+                         metrics='ga:pageviews',
+                         sort='-ga:pageviews',
+                         dimensions="ga:browser,ga:browserVersion",
+                         max_results=10000)
+
+            args['start-date'] = start_date
+            args['end-date'] = end_date
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
+
         result_data = results.get('rows')
         # e.g. [u'Firefox', u'19.0', u'20']
 
@@ -465,14 +626,24 @@
     def _mobile_stats(self, start_date, end_date, period_name, period_complete_day):
         """ Info about mobile devices """
 
-        results = self.service.data().ga().get(
-                                 ids='ga:' + self.profile_id,
-                                 start_date=start_date,
-                                 metrics='ga:pageviews',
-                                 sort='-ga:pageviews',
-                                 dimensions="ga:mobileDeviceBranding, ga:mobileDeviceInfo",
-                                 max_results=10000,
-                                 end_date=end_date).execute()
+        try:
+            # Because of issues of invalid responses, we are going to make these requests
+            # ourselves.
+            headers = {'authorization': 'Bearer ' + self.token}
+
+            args = dict( ids='ga:' + self.profile_id,
+                         metrics='ga:pageviews',
+                         sort='-ga:pageviews',
+                         dimensions="ga:mobileDeviceBranding, ga:mobileDeviceInfo",
+                         max_results=10000)
+            args['start-date'] = start_date
+            args['end-date'] = end_date
+
+            results = self._get_json(args)
+        except Exception, e:
+            log.exception(e)
+            results = dict(url=[])
+
 
         result_data = results.get('rows')
         data = {}

--- a/ckanext/ga_report/ga_auth.py
+++ b/ckanext/ga_report/ga_auth.py
@@ -36,7 +36,7 @@
     credentials = _prepare_credentials(token_file, credentials_file)
     http = credentials.authorize(http)  # authorize the http object
 
-    return build('analytics', 'v3', http=http)
+    return credentials.access_token, build('analytics', 'v3', http=http)
 
 
 def get_profile_id(service):

--- a/ckanext/ga_report/ga_model.py
+++ b/ckanext/ga_report/ga_model.py
@@ -114,7 +114,7 @@
     >>> normalize_url('http://data.gov.uk/dataset/weekly_fuel_prices')
     '/dataset/weekly_fuel_prices'
     '''
-    return '/' + '/'.join(url.split('/')[3:])
+    return url #'/' + '/'.join(url.split('/')[3:])
 
 
 def _get_package_and_publisher(url):
@@ -125,12 +125,12 @@
         dataset_ref = dataset_match.groups()[0]
         dataset = model.Package.get(dataset_ref)
         if dataset:
-            publisher_groups = dataset.get_groups('publisher')
+            publisher_groups = dataset.get_groups('organization')
             if publisher_groups:
                 return dataset_ref,publisher_groups[0].name
         return dataset_ref, None
     else:
-        publisher_match = re.match('/publisher/([^/]+)(/.*)?', url)
+        publisher_match = re.match('/organization/([^/]+)(/.*)?', url)
         if publisher_match:
             return None, publisher_match.groups()[0]
     return None, None
@@ -323,11 +323,11 @@
     """
     toplevel = get_top_level()
     publishers = model.Session.query(model.Group).\
-        filter(model.Group.type=='publisher').\
+        filter(model.Group.type=='organization').\
         filter(model.Group.state=='active').all()
     for publisher in publishers:
         views, visits, subpub = update_publisher(period_name, publisher, publisher.name)
-        parent, parents = '', publisher.get_groups('publisher')
+        parent, parents = '', publisher.get_parent_groups(type='organization')
         if parents:
             parent = parents[0].name
         item = model.Session.query(GA_Publisher).\
@@ -377,15 +377,12 @@
                      model.Member.table_name == 'group' and \
                      model.Member.state == 'active').\
            filter(model.Member.id==None).\
-           filter(model.Group.type=='publisher').\
+           filter(model.Group.type=='organization').\
            order_by(model.Group.name).all()
 
 def get_children(publisher):
-    '''Finds child publishers for the given publisher (object). (Not recursive)'''
-    from ckan.model.group import HIERARCHY_CTE
-    return model.Session.query(model.Group).\
-           from_statement(HIERARCHY_CTE).params(id=publisher.id, type='publisher').\
-           all()
+    '''Finds child publishers for the given publisher (object). (Not recursive i.e. returns one level)'''
+    return publisher.get_children_groups(type='organization')
 
 def go_down_tree(publisher):
     '''Provided with a publisher object, it walks down the hierarchy and yields each publisher,

--- a/ckanext/ga_report/helpers.py
+++ b/ckanext/ga_report/helpers.py
@@ -71,7 +71,7 @@
 def single_popular_dataset_html(top=20):
     dataset_dict = single_popular_dataset(top)
     groups = package.get('groups', [])
-    publishers = [ g for g in groups if g.get('type') == 'publisher' ]
+    publishers = [ g for g in groups if g.get('type') == 'organization' ]
     publisher = publishers[0] if publishers else {'name':'', 'title': ''}
     context = {
         'dataset': dataset_dict,
@@ -107,12 +107,22 @@
     for entry in entries:
         if len(datasets) < count:
             p = model.Package.get(entry.url[len('/dataset/'):])
+
             if not p:
                 _log.warning("Could not find Package for {url}".format(url=entry.url))
                 continue
 
+            if not p.state == 'active':
+                _log.warning("Package {0} is not active, it is {1}".format(p.name, p.state))
+                continue
+
+            if not p.private == False:
+                _log.warning("Package {0} is private {1}".format(p.name, p.state))
+                continue
+
             if not p in datasets:
                 datasets[p] = {'views':0, 'visits': 0}
+
             datasets[p]['views'] = datasets[p]['views'] + int(entry.pageviews)
             datasets[p]['visits'] = datasets[p]['visits'] + int(entry.visits)
 

--- a/ckanext/ga_report/plugin.py
+++ b/ckanext/ga_report/plugin.py
@@ -35,49 +35,49 @@
     def after_map(self, map):
         # GaReport
         map.connect(
-            '/data/site-usage',
+            '/site-usage',
             controller='ckanext.ga_report.controller:GaReport',
             action='index'
         )
         map.connect(
-            '/data/site-usage/data_{month}.csv',
+            '/site-usage_{month}.csv',
             controller='ckanext.ga_report.controller:GaReport',
             action='csv'
         )
         map.connect(
-            '/data/site-usage/downloads',
+            '/site-usage/downloads',
             controller='ckanext.ga_report.controller:GaReport',
             action='downloads'
         )
         map.connect(
-            '/data/site-usage/downloads_{month}.csv',
+            '/site-usage/downloads_{month}.csv',
             controller='ckanext.ga_report.controller:GaReport',
             action='csv_downloads'
         )
 
         # GaDatasetReport
         map.connect(
-            '/data/site-usage/publisher',
+            '/site-usage/publisher',
             controller='ckanext.ga_report.controller:GaDatasetReport',
             action='publishers'
         )
         map.connect(
-            '/data/site-usage/publishers_{month}.csv',
+            '/site-usage/publishers_{month}.csv',
             controller='ckanext.ga_report.controller:GaDatasetReport',
             action='publisher_csv'
         )
         map.connect(
-            '/data/site-usage/dataset/datasets_{id}_{month}.csv',
+            '/site-usage/dataset/datasets_{id}_{month}.csv',
             controller='ckanext.ga_report.controller:GaDatasetReport',
             action='dataset_csv'
         )
         map.connect(
-            '/data/site-usage/dataset',
+            '/site-usage/dataset',
             controller='ckanext.ga_report.controller:GaDatasetReport',
             action='read'
         )
         map.connect(
-            '/data/site-usage/dataset/{id}',
+            '/site-usage/dataset/{id}',
             controller='ckanext.ga_report.controller:GaDatasetReport',
             action='read_publisher'
         )

--- a/ckanext/ga_report/public/css/ga_report.css
+++ b/ckanext/ga_report/public/css/ga_report.css
@@ -66,4 +66,7 @@
   margin-top: 4px;
   float: left;
 }
+.tab-content {
+  padding-top: 12px;
+}
 

--- a/ckanext/ga_report/public/scripts/ckanext_ga_reports.js
+++ b/ckanext/ga_report/public/scripts/ckanext_ga_reports.js
@@ -97,7 +97,7 @@
    * Show the correct rickshaw graph in the sidebar. 
    * Not to be called before all graphs load.
    */
-  $('a[data-toggle="hashtab"]').on(
+  $('a[data-toggle="tab"]').on(
     'shown',
     function(e) {
       var href = $(e.target).attr('href');
@@ -115,7 +115,7 @@
     }
   );
   /* The first tab might already have been shown */
-  $('li.active > a[data-toggle="hashtab"]').trigger('shown');
+  $('li.active > a[data-toggle="tab"]').trigger('shown');
 };
 
 CKAN.GA_Reports.bind_month_selector = function() {
@@ -126,7 +126,6 @@
     window.location = url;
   };
   var selectors = $('select[name="month"]');
-  assert(selectors.length>0);
   selectors.bind('change', handler);
 };
 

--- /dev/null
+++ b/ckanext/ga_report/public/scripts/modernizr-2.6.2.custom.js
@@ -1,1 +1,815 @@
-
+/* Modernizr 2.6.2 (Custom Build) | MIT & BSD
+ * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load
+ */
+;
+
+
+
+window.Modernizr = (function( window, document, undefined ) {
+
+    var version = '2.6.2',
+
+    Modernizr = {},
+
+    enableClasses = true,
+
+    docElement = document.documentElement,
+
+    mod = 'modernizr',
+    modElem = document.createElement(mod),
+    mStyle = modElem.style,
+
+    inputElem  = document.createElement('input')  ,
+
+    smile = ':)',
+
+    toString = {}.toString,
+
+    prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
+
+
+
+    omPrefixes = 'Webkit Moz O ms',
+
+    cssomPrefixes = omPrefixes.split(' '),
+
+    domPrefixes = omPrefixes.toLowerCase().split(' '),
+
+    ns = {'svg': 'http://www.w3.org/2000/svg'},
+
+    tests = {},
+    inputs = {},
+    attrs = {},
+
+    classes = [],
+
+    slice = classes.slice,
+
+    featureName, 
+
+
+    injectElementWithStyles = function( rule, callback, nodes, testnames ) {
+
+      var style, ret, node, docOverflow,
+          div = document.createElement('div'),
+                body = document.body,
+                fakeBody = body || document.createElement('body');
+
+      if ( parseInt(nodes, 10) ) {
+                      while ( nodes-- ) {
+              node = document.createElement('div');
+              node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
+              div.appendChild(node);
+          }
+      }
+
+                style = ['&#173;','<style id="s', mod, '">', rule, '</style>'].join('');
+      div.id = mod;
+          (body ? div : fakeBody).innerHTML += style;
+      fakeBody.appendChild(div);
+      if ( !body ) {
+                fakeBody.style.background = '';
+                fakeBody.style.overflow = 'hidden';
+          docOverflow = docElement.style.overflow;
+          docElement.style.overflow = 'hidden';
+          docElement.appendChild(fakeBody);
+      }
+
+      ret = callback(div, rule);
+        if ( !body ) {
+          fakeBody.parentNode.removeChild(fakeBody);
+          docElement.style.overflow = docOverflow;
+      } else {
+          div.parentNode.removeChild(div);
+      }
+
+      return !!ret;
+
+    },
+
+
+
+    isEventSupported = (function() {
+
+      var TAGNAMES = {
+        'select': 'input', 'change': 'input',
+        'submit': 'form', 'reset': 'form',
+        'error': 'img', 'load': 'img', 'abort': 'img'
+      };
+
+      function isEventSupported( eventName, element ) {
+
+        element = element || document.createElement(TAGNAMES[eventName] || 'div');
+        eventName = 'on' + eventName;
+
+            var isSupported = eventName in element;
+
+        if ( !isSupported ) {
+                if ( !element.setAttribute ) {
+            element = document.createElement('div');
+          }
+          if ( element.setAttribute && element.removeAttribute ) {
+            element.setAttribute(eventName, '');
+            isSupported = is(element[eventName], 'function');
+
+                    if ( !is(element[eventName], 'undefined') ) {
+              element[eventName] = undefined;
+            }
+            element.removeAttribute(eventName);
+          }
+        }
+
+        element = null;
+        return isSupported;
+      }
+      return isEventSupported;
+    })(),
+
+
+    _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
+
+    if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
+      hasOwnProp = function (object, property) {
+        return _hasOwnProperty.call(object, property);
+      };
+    }
+    else {
+      hasOwnProp = function (object, property) { 
+        return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
+      };
+    }
+
+
+    if (!Function.prototype.bind) {
+      Function.prototype.bind = function bind(that) {
+
+        var target = this;
+
+        if (typeof target != "function") {
+            throw new TypeError();
+        }
+
+        var args = slice.call(arguments, 1),
+            bound = function () {
+
+            if (this instanceof bound) {
+
+              var F = function(){};
+              F.prototype = target.prototype;
+              var self = new F();
+
+              var result = target.apply(
+                  self,
+                  args.concat(slice.call(arguments))
+              );
+              if (Object(result) === result) {
+                  return result;
+              }
+              return self;
+
+            } else {
+
+              return target.apply(
+                  that,
+                  args.concat(slice.call(arguments))
+              );
+
+            }
+
+        };
+
+        return bound;
+      };
+    }
+
+    function setCss( str ) {
+        mStyle.cssText = str;
+    }
+
+    function setCssAll( str1, str2 ) {
+        return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
+    }
+
+    function is( obj, type ) {
+        return typeof obj === type;
+    }
+
+    function contains( str, substr ) {
+        return !!~('' + str).indexOf(substr);
+    }
+
+    function testProps( props, prefixed ) {
+        for ( var i in props ) {
+            var prop = props[i];
+            if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
+                return prefixed == 'pfx' ? prop : true;
+            }
+        }
+        return false;
+    }
+
+    function testDOMProps( props, obj, elem ) {
+        for ( var i in props ) {
+            var item = obj[props[i]];
+            if ( item !== undefined) {
+
+                            if (elem === false) return props[i];
+
+                            if (is(item, 'function')){
+                                return item.bind(elem || obj);
+                }
+
+                            return item;
+            }
+        }
+        return false;
+    }
+
+    function testPropsAll( prop, prefixed, elem ) {
+
+        var ucProp  = prop.charAt(0).toUpperCase() + prop.slice(1),
+            props   = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
+
+            if(is(prefixed, "string") || is(prefixed, "undefined")) {
+          return testProps(props, prefixed);
+
+            } else {
+          props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
+          return testDOMProps(props, prefixed, elem);
+        }
+    }    tests['flexbox'] = function() {
+      return testPropsAll('flexWrap');
+    };    tests['canvas'] = function() {
+        var elem = document.createElement('canvas');
+        return !!(elem.getContext && elem.getContext('2d'));
+    };
+
+    tests['canvastext'] = function() {
+        return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
+    };
+
+
+
+    tests['webgl'] = function() {
+        return !!window.WebGLRenderingContext;
+    };
+
+
+    tests['touch'] = function() {
+        var bool;
+
+        if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
+          bool = true;
+        } else {
+          injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
+            bool = node.offsetTop === 9;
+          });
+        }
+
+        return bool;
+    };
+
+
+
+    tests['geolocation'] = function() {
+        return 'geolocation' in navigator;
+    };
+
+
+    tests['postmessage'] = function() {
+      return !!window.postMessage;
+    };
+
+
+    tests['websqldatabase'] = function() {
+      return !!window.openDatabase;
+    };
+
+    tests['indexedDB'] = function() {
+      return !!testPropsAll("indexedDB", window);
+    };
+
+    tests['hashchange'] = function() {
+      return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
+    };
+
+    tests['history'] = function() {
+      return !!(window.history && history.pushState);
+    };
+
+    tests['draganddrop'] = function() {
+        var div = document.createElement('div');
+        return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
+    };
+
+    tests['websockets'] = function() {
+        return 'WebSocket' in window || 'MozWebSocket' in window;
+    };
+
+
+    tests['rgba'] = function() {
+        setCss('background-color:rgba(150,255,150,.5)');
+
+        return contains(mStyle.backgroundColor, 'rgba');
+    };
+
+    tests['hsla'] = function() {
+            setCss('background-color:hsla(120,40%,100%,.5)');
+
+        return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
+    };
+
+    tests['multiplebgs'] = function() {
+                setCss('background:url(https://),url(https://),red url(https://)');
+
+            return (/(url\s*\(.*?){3}/).test(mStyle.background);
+    };    tests['backgroundsize'] = function() {
+        return testPropsAll('backgroundSize');
+    };
+
+    tests['borderimage'] = function() {
+        return testPropsAll('borderImage');
+    };
+
+
+
+    tests['borderradius'] = function() {
+        return testPropsAll('borderRadius');
+    };
+
+    tests['boxshadow'] = function() {
+        return testPropsAll('boxShadow');
+    };
+
+    tests['textshadow'] = function() {
+        return document.createElement('div').style.textShadow === '';
+    };
+
+
+    tests['opacity'] = function() {
+                setCssAll('opacity:.55');
+
+                    return (/^0.55$/).test(mStyle.opacity);
+    };
+
+
+    tests['cssanimations'] = function() {
+        return testPropsAll('animationName');
+    };
+
+
+    tests['csscolumns'] = function() {
+        return testPropsAll('columnCount');
+    };
+
+
+    tests['cssgradients'] = function() {
+        var str1 = 'background-image:',
+            str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
+            str3 = 'linear-gradient(left top,#9f9, white);';
+
+        setCss(
+                       (str1 + '-webkit- '.split(' ').join(str2 + str1) +
+                       prefixes.join(str3 + str1)).slice(0, -str1.length)
+        );
+
+        return contains(mStyle.backgroundImage, 'gradient');
+    };
+
+
+    tests['cssreflections'] = function() {
+        return testPropsAll('boxReflect');
+    };
+
+
+    tests['csstransforms'] = function() {
+        return !!testPropsAll('transform');
+    };
+
+
+    tests['csstransforms3d'] = function() {
+
+        var ret = !!testPropsAll('perspective');
+
+                        if ( ret && 'webkitPerspective' in docElement.style ) {
+
+                      injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) {
+            ret = node.offsetLeft === 9 && node.offsetHeight === 3;
+          });
+        }
+        return ret;
+    };
+
+
+    tests['csstransitions'] = function() {
+        return testPropsAll('transition');
+    };
+
+
+
+    tests['fontface'] = function() {
+        var bool;
+
+        injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) {
+          var style = document.getElementById('smodernizr'),
+              sheet = style.sheet || style.styleSheet,
+              cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';
+
+          bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
+        });
+
+        return bool;
+    };
+
+    tests['generatedcontent'] = function() {
+        var bool;
+
+        injectElementWithStyles(['#',mod,'{font:0/0 a}#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) {
+          bool = node.offsetHeight >= 3;
+        });
+
+        return bool;
+    };
+    tests['video'] = function() {
+        var elem = document.createElement('video'),
+            bool = false;
+
+            try {
+            if ( bool = !!elem.canPlayType ) {
+                bool      = new Boolean(bool);
+                bool.ogg  = elem.canPlayType('video/ogg; codecs="theora"')      .replace(/^no$/,'');
+
+                            bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
+
+                bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
+            }
+
+        } catch(e) { }
+
+        return bool;
+    };
+
+    tests['audio'] = function() {
+        var elem = document.createElement('audio'),
+            bool = false;
+
+        try {
+            if ( bool = !!elem.canPlayType ) {
+                bool      = new Boolean(bool);
+                bool.ogg  = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,'');
+                bool.mp3  = elem.canPlayType('audio/mpeg;')               .replace(/^no$/,'');
+
+                                                    bool.wav  = elem.canPlayType('audio/wav; codecs="1"')     .replace(/^no$/,'');
+                bool.m4a  = ( elem.canPlayType('audio/x-m4a;')            ||
+                              elem.canPlayType('audio/aac;'))             .replace(/^no$/,'');
+            }
+        } catch(e) { }
+
+        return bool;
+    };
+
+
+    tests['localstorage'] = function() {
+        try {
+            localStorage.setItem(mod, mod);
+            localStorage.removeItem(mod);
+            return true;
+        } catch(e) {
+            return false;
+        }
+    };
+
+    tests['sessionstorage'] = function() {
+        try {
+            sessionStorage.setItem(mod, mod);
+            sessionStorage.removeItem(mod);
+            return true;
+        } catch(e) {
+            return false;
+        }
+    };
+
+
+    tests['webworkers'] = function() {
+        return !!window.Worker;
+    };
+
+
+    tests['applicationcache'] = function() {
+        return !!window.applicationCache;
+    };
+
+
+    tests['svg'] = function() {
+        return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
+    };
+
+    tests['inlinesvg'] = function() {
+      var div = document.createElement('div');
+      div.innerHTML = '<svg/>';
+      return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
+    };
+
+    tests['smil'] = function() {
+        return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
+    };
+
+
+    tests['svgclippaths'] = function() {
+        return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
+    };
+
+    function webforms() {
+                                            Modernizr['input'] = (function( props ) {
+            for ( var i = 0, len = props.length; i < len; i++ ) {
+                attrs[ props[i] ] = !!(props[i] in inputElem);
+            }
+            if (attrs.list){
+                                  attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
+            }
+            return attrs;
+        })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
+                            Modernizr['inputtypes'] = (function(props) {
+
+            for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
+
+                inputElem.setAttribute('type', inputElemType = props[i]);
+                bool = inputElem.type !== 'text';
+
+                                                    if ( bool ) {
+
+                    inputElem.value         = smile;
+                    inputElem.style.cssText = 'position:absolute;visibility:hidden;';
+
+                    if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
+
+                      docElement.appendChild(inputElem);
+                      defaultView = document.defaultView;
+
+                                        bool =  defaultView.getComputedStyle &&
+                              defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
+                                                                                  (inputElem.offsetHeight !== 0);
+
+                      docElement.removeChild(inputElem);
+
+                    } else if ( /^(search|tel)$/.test(inputElemType) ){
+                                                                                    } else if ( /^(url|email)$/.test(inputElemType) ) {
+                                        bool = inputElem.checkValidity && inputElem.checkValidity() === false;
+
+                    } else {
+                                        bool = inputElem.value != smile;
+                    }
+                }
+
+                inputs[ props[i] ] = !!bool;
+            }
+            return inputs;
+        })('search tel url email datetime date month week time datetime-local number range color'.split(' '));
+        }
+    for ( var feature in tests ) {
+        if ( hasOwnProp(tests, feature) ) {
+                                    featureName  = feature.toLowerCase();
+            Modernizr[featureName] = tests[feature]();
+
+            classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
+        }
+    }
+
+    Modernizr.input || webforms();
+
+
+     Modernizr.addTest = function ( feature, test ) {
+       if ( typeof feature == 'object' ) {
+         for ( var key in feature ) {
+           if ( hasOwnProp( feature, key ) ) {
+             Modernizr.addTest( key, feature[ key ] );
+           }
+         }
+       } else {
+
+         feature = feature.toLowerCase();
+
+         if ( Modernizr[feature] !== undefined ) {
+                                              return Modernizr;
+         }
+
+         test = typeof test == 'function' ? test() : test;
+
+         if (typeof enableClasses !== "undefined" && enableClasses) {
+           docElement.className += ' ' + (test ? '' : 'no-') + feature;
+         }
+         Modernizr[feature] = test;
+
+       }
+
+       return Modernizr; 
+     };
+
+
+    setCss('');
+    modElem = inputElem = null;
+
+    ;(function(window, document) {
+        var options = window.html5 || {};
+
+        var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
+
+        var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
+
+        var supportsHtml5Styles;
+
+        var expando = '_html5shiv';
+
+        var expanID = 0;
+
+        var expandoData = {};
+
+        var supportsUnknownElements;
+
+      (function() {
+        try {
+            var a = document.createElement('a');
+            a.innerHTML = '<xyz></xyz>';
+                    supportsHtml5Styles = ('hidden' in a);
+
+            supportsUnknownElements = a.childNodes.length == 1 || (function() {
+                        (document.createElement)('a');
+              var frag = document.createDocumentFragment();
+              return (
+                typeof frag.cloneNode == 'undefined' ||
+                typeof frag.createDocumentFragment == 'undefined' ||
+                typeof frag.createElement == 'undefined'
+              );
+            }());
+        } catch(e) {
+          supportsHtml5Styles = true;
+          supportsUnknownElements = true;
+        }
+
+      }());        function addStyleSheet(ownerDocument, cssText) {
+        var p = ownerDocument.createElement('p'),
+            parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
+
+        p.innerHTML = 'x<style>' + cssText + '</style>';
+        return parent.insertBefore(p.lastChild, parent.firstChild);
+      }
+
+        function getElements() {
+        var elements = html5.elements;
+        return typeof elements == 'string' ? elements.split(' ') : elements;
+      }
+
+          function getExpandoData(ownerDocument) {
+        var data = expandoData[ownerDocument[expando]];
+        if (!data) {
+            data = {};
+            expanID++;
+            ownerDocument[expando] = expanID;
+            expandoData[expanID] = data;
+        }
+        return data;
+      }
+
+        function createElement(nodeName, ownerDocument, data){
+        if (!ownerDocument) {
+            ownerDocument = document;
+        }
+        if(supportsUnknownElements){
+            return ownerDocument.createElement(nodeName);
+        }
+        if (!data) {
+            data = getExpandoData(ownerDocument);
+        }
+        var node;
+
+        if (data.cache[nodeName]) {
+            node = data.cache[nodeName].cloneNode();
+        } else if (saveClones.test(nodeName)) {
+            node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
+        } else {
+            node = data.createElem(nodeName);
+        }
+
+                                    return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node;
+      }
+
+        function createDocumentFragment(ownerDocument, data){
+        if (!ownerDocument) {
+            ownerDocument = document;
+        }
+        if(supportsUnknownElements){
+            return ownerDocument.createDocumentFragment();
+        }
+        data = data || getExpandoData(ownerDocument);
+        var clone = data.frag.cloneNode(),
+            i = 0,
+            elems = getElements(),
+            l = elems.length;
+        for(;i<l;i++){
+            clone.createElement(elems[i]);
+        }
+        return clone;
+      }
+
+        function shivMethods(ownerDocument, data) {
+        if (!data.cache) {
+            data.cache = {};
+            data.createElem = ownerDocument.createElement;
+            data.createFrag = ownerDocument.createDocumentFragment;
+            data.frag = data.createFrag();
+        }
+
+
+        ownerDocument.createElement = function(nodeName) {
+                if (!html5.shivMethods) {
+              return data.createElem(nodeName);
+          }
+          return createElement(nodeName, ownerDocument, data);
+        };
+
+        ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
+          'var n=f.cloneNode(),c=n.createElement;' +
+          'h.shivMethods&&(' +
+                    getElements().join().replace(/\w+/g, function(nodeName) {
+              data.createElem(nodeName);
+              data.frag.createElement(nodeName);
+              return 'c("' + nodeName + '")';
+            }) +
+          ');return n}'
+        )(html5, data.frag);
+      }        function shivDocument(ownerDocument) {
+        if (!ownerDocument) {
+            ownerDocument = document;
+        }
+        var data = getExpandoData(ownerDocument);
+
+        if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
+          data.hasCSS = !!addStyleSheet(ownerDocument,
+                    'article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}' +
+                    'mark{background:#FF0;color:#000}'
+          );
+        }
+        if (!supportsUnknownElements) {
+          shivMethods(ownerDocument, data);
+        }
+        return ownerDocument;
+      }        var html5 = {
+
+            'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video',
+
+            'shivCSS': (options.shivCSS !== false),
+
+            'supportsUnknownElements': supportsUnknownElements,
+
+            'shivMethods': (options.shivMethods !== false),
+
+            'type': 'default',
+
+            'shivDocument': shivDocument,
+
+            createElement: createElement,
+
+            createDocumentFragment: createDocumentFragment
+      };        window.html5 = html5;
+
+        shivDocument(document);
+
+    }(this, document));
+
+    Modernizr._version      = version;
+
+    Modernizr._prefixes     = prefixes;
+    Modernizr._domPrefixes  = domPrefixes;
+    Modernizr._cssomPrefixes  = cssomPrefixes;
+
+
+    Modernizr.hasEvent      = isEventSupported;
+
+    Modernizr.testProp      = function(prop){
+        return testProps([prop]);
+    };
+
+    Modernizr.testAllProps  = testPropsAll;
+
+
+    Modernizr.testStyles    = injectElementWithStyles;
+    Modernizr.prefixed      = function(prop, obj, elem){
+      if(!obj) {
+        return testPropsAll(prop, 'pfx');
+      } else {
+            return testPropsAll(prop, obj, elem);
+      }
+    };
+
+
+    docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
+
+                                                    (enableClasses ? ' js ' + classes.join(' ') : '');
+
+    return Modernizr;
+
+})(this, this.document);
+/*yepnope1.5.4|WTFPL*/
+(function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,h){var i=b(a),j=i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]),i.instead?i.instead(a,e,f,g,h):(y[i.url]?i.noexec=!0:y[i.url]=1,f.load(i.url,i.forceCSS||!i.forceJS&&"css"==i.url.split(".").pop().split("?").shift()?"c":c,i.noexec,i.attrs,i.timeout),(d(e)||d(j))&&f.load(function(){k(),e&&e(i.origUrl,h,g),j&&j(i.origUrl,h,g),y[i.url]=2})))}function h(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var i,j,l=this.yepnope.loader;if(e(a))g(a,0,l,0);else if(w(a))for(i=0;i<a.length;i++)j=a[i],e(j)?g(j,0,l,0):w(j)?B(j):Object(j)===j&&h(j,l);else Object(a)===a&&h(a,l)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,null==b.readyState&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}})(this,document);
+Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0));};
+;

--- a/ckanext/ga_report/templates/ga_report/ga_util.html
+++ b/ckanext/ga_report/templates/ga_report/ga_util.html
@@ -57,43 +57,19 @@
     </py:for>
  </table>
 
-
-<div py:def="usage_nav(active_name)">
-  <span class="subheading">
-    <img src="/images/arrow-down-right.png" />
-    <a class="${if_(active_name=='Site-wide','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}${if_(c.month,'?month='+c.month)}">Site-wide</a>
-    <span class="divider">|</span>
-    <a class="${if_(active_name=='Publishers','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}${if_(c.month,'?month='+c.month)}">Publishers</a>
-    <span class="divider">|</span>
-    <a class="${if_(active_name=='Datasets','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}${if_(c.month,'?month='+c.month)}">Datasets</a>
-  </span>
-</div>
-
-<div py:def="ga_sidebar(download_link)">
-  <div class="boxed">
-    <div class="widget-container widget_text">
-      <h4>Download</h4>
-      <p><center>
-        <a class="btn button btn-primary" href="${download_link}">Download as CSV</a></center>
-      </p>
-    </div>
-    <div class="widget-container widget_text">
-      <h4>Graph Legend</h4>
-      <div id="graph-legend-container">
-        <div style="display: none;" id="legend_none">(No graph is loaded)</div>
-      </div>
-    </div>
-    <div class="widget-container widget_text">
-      <h4>Notes</h4>
+<py:def function="ga_footer()">
+  <div class="panel panel-default">
+    <div class="panel-heading"><strong>Notes</strong></div>
+    <div class="panel-body">
       <ul>
-          <li>"Views" is the number of times a page was loaded in users' browsers.</li>
-          <li>"Downloads" is the number of times a user has clicked to download either an original or cached resource for a particular dataset. Download information is only available from 2nd December 2012; 'No data' is shown for records before that date.</li>
-          <li>These usage statistics are confined to users with javascript enabled, which excludes web crawlers and API calls.</li>
-          <li>The results are not shown when the number of views/visits is tiny. Where these relate to site pages, results are available in full in the CSV download. Where these relate to users' web browser information, results are not disclosed, for privacy reasons.</li>
+        <li>"Views" is the number of times a page was loaded in users' browsers.</li>
+        <li>"Downloads" is the number of times a user has clicked to download either an original or cached resource for a particular dataset. Download information is only available from 2nd December 2012; 'No data' is shown for records before that date.</li>
+        <li>These usage statistics are confined to users with javascript enabled, which excludes web crawlers and API calls.</li>
+        <li>The results are not shown when the number of views/visits is tiny. Where these relate to site pages, results are available in full in the CSV download. Where these relate to users' web browser information, results are not disclosed, for privacy reasons.</li>
       </ul>
     </div>
   </div>
-</div>
+</py:def>
 
 
 </html>

--- a/ckanext/ga_report/templates/ga_report/publisher/index.html
+++ b/ckanext/ga_report/templates/ga_report/publisher/index.html
@@ -7,14 +7,10 @@
 
   <py:def function="page_title">Usage by Publisher</py:def>
 
-
-  <py:match path="primarysidebar">
-    ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publisher_csv',month=c.month or 'all'))}
-  </py:match>
-
   <py:def function="optional_head">
     <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/>
     <link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/>
+    <script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script>
     <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script>
     <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script>
     <script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script>
@@ -23,37 +19,48 @@
     <script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script>
   </py:def>
 
+ <py:match path="breadcrumbs">
+    <li><a href="/site-usage">Site Analytics</a></li>
+    <li><a href="/site-usage/publisher">Publishers</a></li>
+  </py:match>
+
   <div py:match="content">
+    <py:with vars="download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publisher_csv',month=c.month or 'all')">
+      <a class="btn button btn-primary btn-sm pull-right" href="${download_link}"><i class="icon-download"></i>&nbsp; Download as CSV</a>
+    </py:with>
+    <h1>Site Usage</h1>
 
-    <h1>Site Usage ${usage_nav('Publishers')}</h1>
-
-    <div class="boxed">
-
-       ${rickshaw_graph(c.top_publishers_graph,'publishers')}
+    <div class="row" style="background: #fff;">
+      <div class="col-md-8">
+        <div class="whitebox">
+          <strong>Publishers</strong>
+           ${rickshaw_graph(c.top_publishers_graph,'publishers')}
+       </div>
+      </div>
+    </div>
 
        <hr/>
        <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}" method="get">
           <div class="controls">
-            <h4 class="ga-reports-heading">Statistics for</h4>
-            ${month_selector(c.month, c.months, c.day)}
+            <h2 class="ga-reports-heading">Statistics for</h2>
+              ${month_selector(c.month, c.months, c.day)}
           </div>
        </form>
 
-       <table class="ga-reports-table table table-condensed table-bordered table-striped">
-         <tr>
-           <th>Publisher</th>
-           <th class="td-numeric">Dataset Views</th>
-         </tr>
-        <py:for each="publisher, views, visits in c.top_publishers">
-          <tr>
-            <td>
-                ${h.link_to(publisher.title, h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport', action='read_publisher', id=publisher.name) + (("?month=" + c.month) if c.month else ''))}
-            </td>
-            <td class="td-numeric">${views}</td>
-          </tr>
-        </py:for>
-       </table>
-    </div><!--/boxed-->
+     <table class="ga-reports-table table table-condensed table-bordered table-striped">
+       <tr>
+         <th>Publisher</th>
+         <th class="td-numeric">Dataset Views</th>
+       </tr>
+      <py:for each="publisher, views, visits in c.top_publishers">
+        <tr>
+          <td>
+              ${h.link_to(publisher.title, h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport', action='read_publisher', id=publisher.name) + (("?month=" + c.month) if c.month else ''))}
+          </td>
+          <td class="td-numeric">${views}</td>
+        </tr>
+      </py:for>
+     </table>
   </div><!--/content-->
 
   <py:def function="optional_footer">
@@ -63,7 +70,7 @@
       });
     </script>
   </py:def>
-  <xi:include href="../../layout.html" />
+  <xi:include href="../site/layout.html" />
 
 </html>
 

--- a/ckanext/ga_report/templates/ga_report/publisher/read.html
+++ b/ckanext/ga_report/templates/ga_report/publisher/read.html
@@ -10,6 +10,7 @@
   <py:def function="optional_head">
     <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/>
     <link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/>
+    <script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script>
     <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script>
     <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script>
     <script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script>
@@ -18,65 +19,83 @@
     <script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script>
   </py:def>
 
-  <py:match path="primarysidebar">
-    ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='dataset_csv',id=c.publisher_name or 'all',month=c.month or 'all'))}
+
+ <py:match path="breadcrumbs">
+    <li><a href="/site-usage">Site Analytics</a></li>
+    <py:if test="c.publisher">
+      <li><a href="/site-usage/publisher">Publishers</a></li>
+      <li py:if="c.publisher"><a href="/site-usage/publisher/${c.publisher.name}">${c.publisher.title}</a></li>
+    </py:if>
+    <py:if test="not c.publisher">
+      <li><a href="${request.url}">Usage By Dataset</a></li>
+    </py:if>
   </py:match>
 
+  <div py:match="content">
 
-  <div py:match="content">
-    <h1>Site Usage ${usage_nav('Datasets')}</h1>
+    <py:with vars="download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='dataset_csv',id=c.publisher_name or 'all',month=c.month or 'all')">
+      <a class="btn button btn-primary btn-sm pull-right" href="${download_link}"><i class="icon-download"></i>&nbsp; Download as CSV</a>
+    </py:with>
+    <h1>Site Usage
+        <small py:if="c.publisher">${c.publisher.title}</small>
+        <small py:if="not c.publisher">All datasets</small>
+    </h1>
 
-    <div class="boxed">
-     <h3 py:if="c.publisher"><a href="${h.url_for(controller='ckanext.dgu.controllers.publisher:PublisherController',action='read',id=c.publisher.name)}">${c.publisher.title}</a></h3>
+    <div class="row" style="background: #fff;">
+      <div class="col-md-8">
+        <div class="whitebox">
+          <py:if test="c.graph_data">
+            ${rickshaw_graph(c.graph_data,'dataset-downloads',debug=True)}
+          </py:if>
+       </div>
+      </div>
+    </div>
+    <hr/>
+   <py:if test="c.month">
+     <h4>Statistics for ${h.month_option_title(c.month,c.months,c.day)}:</h4>
+   </py:if>
+   <py:if test="not c.month">
+     <h2>Statistics for all months</h2>
+   </py:if>
+   <form style="margin-bottom:10px;" class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}" method="get">
+      <div class="controls">
 
-     <py:if test="c.graph_data">
-       ${rickshaw_graph(c.graph_data,'dataset-downloads',debug=True)}
-     </py:if>
-     <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}" method="get">
-        <div class="controls">
+        ${month_selector(c.month, c.months, c.day)}
 
-          ${month_selector(c.month, c.months, c.day)}
+        <select name="publisher">
+              <option value='' py:attrs="{'selected': 'selected' if not c.publisher else None}">All publishers</option>
+            <py:for each="val,desc in c.publishers">
+              <option value='${val}' py:attrs="{'selected': 'selected' if c.publisher_name == val else None}">${desc}</option>
+            </py:for>
+        </select>
+        <input class="btn button btn-primary btn-xs" type='submit' value="Update"/>
+      </div>
+   </form>
+   <div class="alert alert-info" py:if="not c.top_packages">No page views in this period.</div>
+   <py:if test="c.top_packages">
+     <table class="ga-reports-table table table-condensed table-bordered">
+       <tr>
+         <th>Dataset</th>
+         <th>Views</th>
+         <th>Downloads</th>
+       </tr>
+       <py:for each="package, views, visits,downloads in c.top_packages">
+         <tr>
+           <td>
+             ${h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name))}
+           </td>
+           <td class="td-numeric">${views}</td>
+           <td class="td-numeric">${downloads}</td> 
+        </tr>
+      </py:for>
+    </table>
+  </py:if>
 
-          <select name="publisher">
-                <option value='' py:attrs="{'selected': 'selected' if not c.publisher else None}">All publishers</option>
-              <py:for each="val,desc in c.publishers">
-                <option value='${val}' py:attrs="{'selected': 'selected' if c.publisher_name == val else None}">${desc}</option>
-              </py:for>
-          </select>
-          <input class="btn button btn-primary" type='submit' value="Update"/>
-        </div>
-     </form>
-     <py:if test="c.month">
-       <h4>Statistics for ${h.month_option_title(c.month,c.months,c.day)}:</h4>
-     </py:if>
-     <py:if test="not c.month">
-       <h4>Statistics for all months:</h4>
-     </py:if>
-     <div class="alert alert-info" py:if="not c.top_packages">No page views in this period.</div>
-     <py:if test="c.top_packages">
-       <table class="ga-reports-table table table-condensed table-bordered table-striped">
-         <tr>
-           <th>Dataset</th>
-           <th>Views</th>
-           <th>Downloads</th>
-         </tr>
-         <py:for each="package, views, visits,downloads in c.top_packages">
-           <tr>
-             <td>
-               ${h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name))}
-             </td>
-             <td class="td-numeric">${views}</td>
-             <td class="td-numeric">${downloads}</td>
-          </tr>
-        </py:for>
-      </table>
-    </py:if>
+  ${ga_footer()}
 
   </div>
 
-  </div>
-
-  <xi:include href="../../layout.html" />
+  <xi:include href="../site/layout.html" />
 </html>
 
 

--- a/ckanext/ga_report/templates/ga_report/site/downloads.html
+++ b/ckanext/ga_report/templates/ga_report/site/downloads.html
@@ -7,12 +7,12 @@
 
   <py:def function="page_title">Downloads</py:def>
 
-  <py:match path="primarysidebar">
+  <py:def path="sidebar">
     ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv_downloads',month=c.month or 'all'))}
-  </py:match>
+  </py:def>
 
+  <py:def function="page_heading">Downloads ${usage_nav('Downloads')}</py:def>
   <div py:match="content">
-      <h1>Downloads ${usage_nav('Downloads')}</h1>
 
     <div class="boxed">
 

--- a/ckanext/ga_report/templates/ga_report/site/index.html
+++ b/ckanext/ga_report/templates/ga_report/site/index.html
@@ -8,155 +8,178 @@
   <py:def function="page_title">Site usage</py:def>
 
   <py:def function="optional_head">
-    <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/>
-    <link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/>
-    <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script>
-    <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script>
-    <script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script>
-    <script type="text/javascript" src="/scripts/vendor/d3.v2.js"></script>
-    <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script>
-    <script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script>
   </py:def>
 
-  <py:match path="primarysidebar">
-    ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv',month=c.month or 'all'))}
+ <py:match path="breadcrumbs">
+    <li><a href="/site-usage">Site Analytics</a></li>
+    <li><a href="/site-usage">Site-wide</a></li>
   </py:match>
 
   <div py:match="content">
-      <h1>Site Usage ${usage_nav('Site-wide')}</h1>
-
-    <div class="boxed">
-    <div class="tabbable">
-      <ul class="nav nav-tabs">
-        <li class="active"><a href="#totals" data-toggle="hashtab">Totals</a></li>
-        <li class="dropdown">
-            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Browsers
-                <b class="caret"></b></a>
-            <ul class="dropdown-menu">
-                <li><a href="#browsers_names" data-toggle="hashtab">Browsers</a></li>
-                <li><a href="#browsers_versions" data-toggle="hashtab">Versions</a></li>
+    <div class="row">
+      <div class="col-sm-7 col-md-8 col-lg-9">
+        <py:with vars="download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv',month=c.month or 'all')">
+          <a class="btn button btn-primary btn-sm pull-right" href="${download_link}"><i class="icon-download"></i>&nbsp; Download as CSV</a>
+        </py:with>
+        <h1>Site Usage</h1>
+      </div>
+      <div class="col-sm-5 col-md-4 col-lg-3">
+        <div class="panel panel-default">
+          <div class="panel-heading"><strong>Jump To...</strong></div>
+          <div class="panel-body">
+            <ul>
+              <li><a href="/site-usage/publisher">Publisher Usage Statistics</a></li>
+              <li><a href="/site-usage/dataset">Dataset Usage Statistics</a></li>
             </ul>
-        </li>
-        <li class="dropdown">
-            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Operating Systems
-                <b class="caret"></b></a>
-            <ul class="dropdown-menu">
-                <li><a href="#os" data-toggle="hashtab">Operating Systems</a></li>
-                <li><a href="#os_versions" data-toggle="hashtab">Versions</a></li>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="row" style="background: #fff;">
+      <div class="col-md-8">
+        <div class="whitebox">
+          <div class="tabbable">
+            <ul class="nav nav-tabs">
+              <li class="active"><a href="#totals" data-hash="totals" data-toggle="tab">Totals</a></li>
+              <li class="dropdown">
+                  <a href="#" class="dropdown-toggle" data-toggle="dropdown">Browsers
+                      <b class="caret"></b></a>
+                  <ul class="dropdown-menu">
+                      <li><a href="#browsers_names" data-hash="browsers_names" data-toggle="tab">Browsers</a></li>
+                      <li><a href="#browsers_versions" data-hash="browsers_versions" data-toggle="tab">Versions</a></li>
+                  </ul>
+              </li>
+              <li class="dropdown">
+                  <a href="#" class="dropdown-toggle" data-toggle="dropdown">Operating Systems
+                      <b class="caret"></b></a>
+                  <ul class="dropdown-menu">
+                      <li><a href="#os" data-hash="os" data-toggle="tab">Operating Systems</a></li>
+                      <li><a href="#os_versions" data-hash="os_versions" data-toggle="tab">Versions</a></li>
+                  </ul>
+              </li>
+              <li class="dropdown">
+                  <a href="#" class="dropdown-toggle" data-toggle="dropdown">Social
+                      <b class="caret"></b></a>
+                  <ul class="dropdown-menu">
+                      <li><a href="#social_networks" data-hash="social_networks" data-toggle="tab">All networks</a></li>
+                      <li><a href="#social_referrals_totals" data-hash="social_referrals_totals" data-toggle="tab">Referral links</a></li>
+                  </ul>
+              </li>
+              <li><a href="#languages" data-hash="languages" data-toggle="tab">Languages</a></li>
+              <li><a href="#country" data-hash="country" data-toggle="tab">Country</a></li>
             </ul>
-        </li>
-        <li class="dropdown">
-            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Social
-                <b class="caret"></b></a>
-            <ul class="dropdown-menu">
-                <li><a href="#social_networks" data-toggle="hashtab">All networks</a></li>
-                <li><a href="#social_referrals_totals" data-toggle="hashtab">Referral links</a></li>
-            </ul>
-        </li>
-        <li><a href="#languages" data-toggle="hashtab">Languages</a></li>
-        <li><a href="#country" data-toggle="hashtab">Country</a></li>
-      </ul>
-      <div class="tab-content">
-        <div class="tab-pane active" id="totals">
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-             <table class="ga-reports-table table table-condensed table-bordered table-striped">
-            	 <tr>
-            	   <th>Name</th>
-            	   <th class="td-numeric">Value</th>
-            	   <th>History</th>
-            	 </tr>
-                <py:for each="name, value, graph in c.global_totals">
-                    <tr>
-                        <td>${name}</td>
-                        <td class="td-numeric">${value}</td>
-                        <td class="sparkline-cell">
-                          <span class="sparkline" sparkTooltips="${','.join([x for x,y in graph])}">
-                            ${','.join([y for x,y in graph])}
-                          </span>
-                        </td>
-                    </tr>
-                </py:for>
-               </table>
-        </div>
-         <div class="tab-pane" id="browsers_versions">
-             ${rickshaw_graph(c.browser_versions_graph,'browser-versions',mode='stack')}
-             <hr/>
-             <p>Note: Where a browser has a large number of versions, these have been grouped together.</p>
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-             ${stat_table(c.browser_versions)}
-         </div>
-         <div class="tab-pane" id="browsers_names">
-             ${rickshaw_graph(c.browsers_graph,'browsers',mode='stack')}
-             <hr/>
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-             ${stat_table(c.browsers)}
-         </div>
-         <div class="tab-pane" id="os">
-             ${rickshaw_graph(c.os_graph,'os',mode='stack')}
-             <hr/>
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-             ${stat_table(c.os)}
-         </div>
-         <div class="tab-pane" id="os_versions">
-             ${rickshaw_graph(c.os_versions_graph,'os_versions',mode='stack')}
-             <hr/>
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-             ${stat_table(c.os_versions)}
-         </div>
-        <div class="tab-pane" id="social_referrals_totals">
-            <p>Number of visits that were referred from social networks</p>
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-            ${social_table(c.social_referrer_totals)}
-        </div>
-        <div class="tab-pane" id="social_networks">
-             ${rickshaw_graph(c.social_networks_graph, 'social_networks',mode='stack')}
-             <hr/>
-            <p>Percentage of visits that were referred from these social networks</p>
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-             ${stat_table(c.social_networks, 'Visits')}
-        </div>
-        <div class="tab-pane" id="languages">
-             ${rickshaw_graph(c.languages_graph,'languages',mode='stack')}
-             <hr/>
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-             ${stat_table(c.languages)}
-        </div>
-        <div class="tab-pane" id="country">
-             ${rickshaw_graph(c.country_graph,'country',mode='stack')}
-             <hr/>
-             <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
-               <h4 class="ga-reports-heading">Show stats table for:</h4>
-               ${month_selector(c.month, c.months, c.day)}
-             </form>
-             ${stat_table(c.country)}
-        </div>
+            <div class="tab-content">
+              <div class="tab-pane active" id="totals">
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                   <table class="ga-reports-table table table-condensed table-bordered table-striped">
+                     <tr>
+                       <th>Name</th>
+                       <th class="td-numeric">Value</th>
+                       <th>History</th>
+                     </tr>
+                      <py:for each="name, value, graph in c.global_totals">
+                          <tr>
+                              <td>${name}</td>
+                              <td class="td-numeric">${value}</td>
+                              <td class="sparkline-cell">
+                                <span class="sparkline" sparkTooltips="${','.join([x for x,y in graph])}">
+                                  ${','.join([y for x,y in graph])}
+                                </span>
+                              </td>
+                          </tr>
+                      </py:for>
+                     </table>
+              </div>
+               <div class="tab-pane" id="browsers_versions">
+                   ${rickshaw_graph(c.browser_versions_graph,'browser-versions',mode='stack')}
+                   <hr/>
+                   <p>Note: Where a browser has a large number of versions, these have been grouped together.</p>
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                <hr/>
+                   ${stat_table(c.browser_versions)}
+               </div>
+               <div class="tab-pane" id="browsers_names">
+                   ${rickshaw_graph(c.browsers_graph,'browsers',mode='stack')}
+                   <hr/>
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                <hr/>
+                   ${stat_table(c.browsers)}
+               </div>
+               <div class="tab-pane" id="os">
+                   ${rickshaw_graph(c.os_graph,'os',mode='stack')}
+                   <hr/>
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                <hr/>
+                   ${stat_table(c.os)}
+               </div>
+               <div class="tab-pane" id="os_versions">
+                   ${rickshaw_graph(c.os_versions_graph,'os_versions',mode='stack')}
+                   <hr/>
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                <hr/>
+                   ${stat_table(c.os_versions)}
+               </div>
+              <div class="tab-pane" id="social_referrals_totals">
+                  <p>Number of visits that were referred from social networks</p>
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                  ${social_table(c.social_referrer_totals)}
+              </div>
+              <div class="tab-pane" id="social_networks">
+                   ${rickshaw_graph(c.social_networks_graph, 'social_networks',mode='stack')}
+                   <hr/>
+                  <p>Percentage of visits that were referred from these social networks</p>
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                <hr/>
+                   ${stat_table(c.social_networks, 'Visits')}
+              </div>
+              <div class="tab-pane" id="languages">
+                   ${rickshaw_graph(c.languages_graph,'languages',mode='stack')}
+                   <hr/>
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                <hr/>
+                   ${stat_table(c.languages)}
+              </div>
+              <div class="tab-pane" id="country">
+                   ${rickshaw_graph(c.country_graph,'country',mode='stack')}
+                   <hr/>
+                   <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
+                     <h4 class="ga-reports-heading">Show stats table for:</h4>
+                     ${month_selector(c.month, c.months, c.day)}
+                   </form>
+                <hr/>
+                   ${stat_table(c.country)}
+              </div>
+             </div>
+           </div>
        </div>
-     </div>
-   </div>
+      </div>
+    </div>
+
   </div>
 
 
@@ -166,11 +189,11 @@
         CKAN.GA_Reports.bind_sparklines();
         CKAN.GA_Reports.bind_sidebar();
         CKAN.GA_Reports.bind_month_selector();
-      });  
+      });
     </script>
   </py:def>
 
-  <xi:include href="../../layout.html" />
+  <xi:include href="layout.html" />
 </html>
 
 

--- /dev/null
+++ b/ckanext/ga_report/templates/ga_report/site/layout.html
@@ -1,1 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:i18n="http://genshi.edgewall.org/i18n"
+  xmlns:py="http://genshi.edgewall.org/" 
+  xmlns:xi="http://www.w3.org/2001/XInclude"
+  py:strip="">
+  <xi:include href="layout_base.html" />
+</html>
 

--- /dev/null
+++ b/ckanext/ga_report/templates/ga_report/site/layout_base.html
@@ -1,1 +1,357 @@
-
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--[if IE 8 ]>    <html class="no-js ie8" lang="en"> <![endif]-->
+  <!--[if (gte IE 9)|!(IE)]><!--> <html class="no-js" lang="en"
+      xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:i18n="http://genshi.edgewall.org/i18n"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      > <!--<![endif]-->
+<xi:include href="../../_util.html" />
+<head>
+  <meta charset="utf-8" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+
+  <title>${page_title()} - ${g.site_title}</title>
+  <meta name="description" content="" />
+  <meta name="author" content="" />
+
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="shortcut icon" href="${h.url_for_static(g.favicon)}" />
+
+  <py:choose>
+  <py:when test="defined('optional_feed')">
+    ${optional_feed()}
+  </py:when>
+  <py:otherwise>
+  <link rel="alternate" type="application/atom+xml" title="${g.site_title} - Recent Revision History" href="${h.url_for(controller='revision', action='list', format='atom', days=1)}" />
+  </py:otherwise>
+  </py:choose>
+  <link href='http://fonts.googleapis.com/css?family=Ubuntu:400,700' rel='stylesheet' type='text/css' />
+
+  <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/jqueryui/1.8.14/css/jquery-ui.custom.css')}" type="text/css" media="screen, print" />
+  <link rel="stylesheet" href="${h.url_for_static('/css/bootstrap.min.css')}" type="text/css" media="screen, projection" />
+  <link rel="stylesheet" href="${h.url_for_static('/css/chosen.css')}" type="text/css" />
+  <link rel="stylesheet" href="${h.url_for_static('/css/style.css?v=2')}" />
+  ${jsConditionalForIe(9, '&lt;script type="text/javascript" src="' + h.url_for_static('/scripts/vendor/html5shiv/html5.js') + '"&gt;&lt;/script&gt;')}
+<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
+<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
+    <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/>
+    <link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/>
+    <script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script>
+    <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script>
+    <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script>
+    <script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script>
+    <script type="text/javascript" src="/scripts/vendor/d3.v2.js"></script>
+    <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script>
+    <script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script>
+<style>
+#pre-content #breadcrumbs {
+    padding-left: 0;
+    padding-top: 10px;
+    padding-bottom: 10px;
+    margin-bottom: 0;
+    list-style-type: none;
+    color: #ccc
+}
+
+#pre-content #breadcrumbs li {
+    color: #999;
+    display: inline-block;
+    zoom: 1;
+    *display: inline;
+    margin-right: 8px
+}
+
+#pre-content #breadcrumbs li:after {
+    content: '/';
+    display: inline-block;
+    zoom: 1;
+    *display: inline;
+    margin-left: 12px
+}
+
+#pre-content #breadcrumbs li:last-child:after {
+    display: none
+}
+
+#pre-content #breadcrumbs a {
+    color: #999
+}
+
+#pre-content #breadcrumbs a:hover {
+    color: #000;
+    text-decoration: none
+}
+
+#pre-content #breadcrumbs .spacer {
+    color: #ccc
+}
+</style>
+  <py:if test="defined('optional_head')">
+    ${optional_head()}
+  </py:if>
+
+  ${h.literal(getattr(g, 'template_head_end', ''))}
+</head>
+
+<body class="${request.environ.get('pylons.routes_dict', {}).get('action')}
+             ${request.environ.get('pylons.routes_dict', {}).get('controller').split(':')[-1]}
+             ${defined('body_class') and body_class()}
+  ">
+
+  <div id="wrap">
+    <div class="header outer">
+      <header class="container">
+        <div class="menu account">
+          <span class="ckan-logged-in" style="display: none;">
+            <a href="${h.url_for(controller='user',action='me')}">${h.gravatar((c.userobj.email_hash if c and c.userobj else ''),size=20)}${c.user}</a>
+            <a href="${h.url_for('/user/_logout')}">Logout</a>
+          </span>
+          <span class="ckan-logged-out">
+            <a href="${h.url_for(controller='user',action='login')}">Login</a>
+            <a href="${h.url_for(controller='user',action='register')}">Register</a>
+          </span>
+        </div>
+        <a href="${h.url('home')}">
+          <img width="196" src="${h.url_for_static(g.site_logo)}" alt="${g.site_title} Logo" title="${g.site_title} Logo" id="logo" />
+        </a>
+        <div id="site-name">
+<!--          <a href="${h.url('home')}">${g.site_title} &mdash; ${g.site_description}</a>-->
+        </div>
+        <div class="menu">
+<!--          <span id="menusearch">
+            <form action="${h.url(controller='package', action='search')}" method="GET">
+              <input name="q" value="${c.q if hasattr(c, 'q') else ''}" class="search" placeholder="${_('Find datasets')}" />
+            </form>
+          </span>
+          <div id="mainmenu">
+            <span py:if="h.check_access('package_create')">${h.nav_link(_('Add a dataset'), controller='package', action='new')}</span>
+            ${h.nav_link(_('Search'), controller='package', action='search', highlight_actions = 'new index')}
+            ${h.nav_link(_('Groups'), named_route='%s_index' % h.default_group_type())}
+            ${h.nav_link(_('About'), controller='home', action='about')}
+          </div>-->
+        </div>
+      </header>
+    </div>
+ <div id="pre-content">
+    <div class="container">
+      <div class="row">
+        <div class="col-md-12">
+          <ul id="breadcrumbs">
+            <li><a href="/"><i class="icon-home"></i></a></li>
+            <breadcrumbs>
+            </breadcrumbs>
+          </ul>
+        </div>
+      </div>
+    </div>
+  </div>
+
+    <py:with vars="messages = list(h.flash.pop_messages())">
+    <div class="flash-messages container">
+      <div class="alert ${m.category}" py:for="m in messages">
+        ${h.literal(m)}
+      </div>
+    </div>
+    </py:with>
+
+    <div id="main" class="container" role="main">
+      <h1 py:if="defined('page_heading')" class="page_heading">
+        <img py:if="defined('page_logo')" id="page-logo" src="${page_logo()}" alt="Page Logo" />
+<!--        ${page_heading()} -->
+      </h1>
+      <div class="row">
+        <div class="span12">
+          <div id="minornavigation">
+              <minornavigation></minornavigation>
+          </div>
+        </div>
+      </div>
+
+      <div class="row">
+        <div class="span9 content-outer">
+          <div id="content">
+            <py:if test="defined('content')">
+              ${content()}
+            </py:if>
+            <content>
+              <p>Master content template placeholder &hellip; please replace me.</p>
+            </content>
+          </div> <!-- /content -->
+        </div>
+        <div class="span3 sidebar-outer">
+          <div id="sidebar">
+      <div class="col-md-4">
+        <div class="whitebox">
+          <strong>Graph Legend</strong>
+          <div id="graph-legend-container">
+            <div style="display: none;" id="legend_none">(No graph is loaded)</div>
+          </div>
+        </div>
+      </div>
+
+            <ul class="widget-list">
+              <py:if test="defined('primary_sidebar_extras')">
+                ${primary_sidebar_extras()}
+              </py:if>
+              <primarysidebar>
+                <!-- Primary Side Bar Goes Here -->
+              </primarysidebar>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </div>
+    <br/><br/>
+  </div>
+  <div class="clearfix"></div>
+  <div class="footer outer">
+    <footer class="container">
+      <div class="row">
+<!--        <div class="span3">
+          <h3 class="widget-title">About ${g.site_title}</h3>
+          <div class="textwidget">
+            <ul>
+                <li>${h.link_to(_('About'), h.url_for(controller='home', action='about'))}</li>
+              <li>
+                <a href="http://twitter.com/ckanproject">Twitter @ckanproject</a>
+              </li>
+                <li>${h.link_to(_('API'), h.url_for(controller='api', action='get_api', ver=1))}</li>
+                <li>${h.link_to(_('API Docs'), 'http://docs.ckan.org/en/latest/api.html')}</li>
+              <li>
+                <a href="http://ckan.org/contact/">Contact Us</a>
+              </li>
+              <li>
+                <a href="http://okfn.org/privacy-policy/">Privacy Policy</a>
+              </li>
+            </ul>
+          </div>
+        </div>
+        <div class="span3">
+          <h3 class="widget-title">Sections</h3>
+          <div class="textwidget">
+            <ul>
+              <li>
+                <a href="${h.url(controller='user', action='index')}">
+                  Users
+                </a>
+              </li>
+              <li>
+                <a href="${h.url(controller='tag', action='index')}">
+                  Tags
+                </a>
+              </li>
+              <li py:if="'stats' in config.get('ckan.plugins','').split(' ')">
+                <a href="${h.url('stats')}">
+                  Statistics
+                </a>
+              </li>
+              <li>
+                <a href="${h.url(controller='revision', action='index')}">
+                  Revisions
+                </a>
+              </li>
+              <li>
+                <a href="${h.url_for('ckanadmin_index')}">
+                  Site Admin
+                </a>
+              </li>
+            </ul>
+          </div>
+        </div>
+        <div class="span3">
+          <h3 class="widget-title">Languages</h3>
+          <div class="textwidget">
+            <ul>
+<?python
+  current_url = request.environ['CKAN_CURRENT_URL']
+?>
+              <li py:for="locale in h.get_available_locales()">
+              <a href="${h.url(current_url, locale=str(locale))}">
+                  ${locale.display_name or locale.english_name}
+                </a>
+              </li>
+            </ul>
+          </div>
+        </div>-->
+        <div class="span12">
+          <p id="credits">
+            &copy; 2012-2014
+            <img src="//assets.okfn.org/images/logo/okf_logo_white_and_green_tiny.png" id="footer-okf-logo" />
+            <a href="http://okfn.org/">Open Knowledge Foundation</a>
+            Licensed under the <a href="http://opendatacommons.org/licenses/odbl/1.0/">Open Database License</a>
+            <a href="http://opendefinition.org/"><img alt="This Content and Data is Open" src="//assets.okfn.org/images/ok_buttons/od_80x15_blue.png" style="border: none;"/></a>
+
+            <br/><br/>
+            Powered by <a href="http://ckan.org">CKAN</a> v${c.__version__}.<br/>
+          </p>
+        </div>
+      </div>
+    </footer>
+  </div> <!-- eo #container -->
+  
+  <div style="display:none;" id="scripts">
+  <!--<script src="${h.url_for_static('/scripts/vendor/jquery/1.7.1/jquery.js')}"></script>-->
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/json2.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/jquery.tmpl/beta1/jquery.tmpl.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/jquery.cookie/jquery.cookie.min.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/jquery.chosen/0.9.7/chosen.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/jquery.placeholder/jquery.placeholder.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/jqueryui/1.8.14/jquery-ui.min.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/bootstrap/2.0.3/bootstrap.min.js')}"></script>
+
+  <!-- for application.js -->
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/underscore/1.1.6/underscore.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/backbone/0.5.1/backbone.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/jquery.fileupload/20110801/jquery.iframe-transport.js')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/vendor/jquery.fileupload/20110801/jquery.fileupload.js')}"></script>
+
+  <!-- Translated js strings live inside an html template. -->
+  <xi:include href="../../js_strings.html" />
+  <!-- finally our application js that sets everything up-->
+  <script type="text/javascript" src="${h.url_for_static('/scripts/application.js?lang=${c.locale}')}"></script>
+  <script type="text/javascript" src="${h.url_for_static('/scripts/templates.js')}"></script>
+
+  <script type="text/javascript">
+    CKAN.plugins = [
+      // Declare js array from Python string
+      ${['\'%s\', '%s  for s in config.get('ckan.plugins','').split(' ')]}
+    ];
+    <py:if test="config.get('ckan.storage.bucket', '')">
+    CKAN.plugins.push('storage');
+    </py:if>
+    CKAN.SITE_URL = '${h.url('/')}';
+    CKAN.SITE_URL_NO_LOCALE = '${h.url('/', locale='default')}';
+    CKAN.LANG = '${h.lang()}';
+    // later use will add offsets with leading '/' so ensure no trailing slash
+    CKAN.SITE_URL = CKAN.SITE_URL.replace(/\/$/, '');
+    CKAN.SITE_URL_NO_LOCALE = CKAN.SITE_URL_NO_LOCALE.replace(/\/$/, '');
+    $(document).ready(function() {
+        var ckan_user = '${c.user}';
+        if (ckan_user) {
+            $(".ckan-logged-out").hide();
+            $(".ckan-logged-in").show();
+        }
+        $('input[placeholder], textarea[placeholder]').placeholder();
+
+		$(".chzn-select").chosen();
+    });
+  </script>
+
+  <py:if test="h.asbool(config.get('ckan.tracking_enabled', 'false'))">
+	${ h.snippet('snippets/internal-tracking.html') }
+  </py:if>
+  </div> <!-- #scripts -->
+
+  <py:if test="defined('optional_footer')">
+    ${optional_footer()}
+  </py:if>
+
+${h.literal(getattr(g, 'template_footer_end', ''))}
+</body>
+</html>
+
+
+
+