Merge branch 'master' of git+ssh://maxious.lambdacomplex.org/git/ckanext-ga-report
--- 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):
"""
--- a/ckanext/ga_report/controller.py
+++ b/ckanext/ga_report/controller.py
@@ -342,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')
--- a/ckanext/ga_report/download_analytics.py
+++ b/ckanext/ga_report/download_analytics.py
@@ -2,9 +2,11 @@
import logging
import datetime
import httplib
+import urllib
import collections
import requests
import json
+import re
from pylons import config
from ga_model import _normalize_url
import ga_model
@@ -121,13 +123,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,)
@@ -177,7 +179,7 @@
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)
@@ -192,6 +194,7 @@
# Supported query params at
# https://developers.google.com/analytics/devguides/reporting/core/v3/reference
+ # https://ga-dev-tools.appspot.com/explorer/
try:
args = {}
args["sort"] = "-ga:pageviews"
@@ -203,7 +206,7 @@
args["ids"] = "ga:" + self.profile_id
args["filters"] = query
args["alt"] = "json"
-
+ print args
results = self._get_json(args)
except Exception, e:
@@ -212,11 +215,13 @@
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
@@ -330,8 +335,7 @@
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', '/'))
+ 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
@@ -422,7 +426,7 @@
args["end-date"] = end_date
args["ids"] = "ga:" + self.profile_id
- args["filters"] = 'ga:eventAction==download'
+ args["filters"] = 'ga:eventAction==Download'
args["dimensions"] = "ga:eventLabel"
args["metrics"] = "ga:totalEvents"
args["alt"] = "json"
@@ -448,7 +452,7 @@
if progress_count % 100 == 0:
log.debug('.. %d/%d done so far', progress_count, progress_total)
- url = result[0].strip()
+ url = urllib.unquote(result[0].strip())
# Get package id associated with the resource that has this URL.
q = model.Session.query(model.Resource)
@@ -456,8 +460,15 @@
r = q.filter(model.Resource.cache_url.like("%s%%" % url)).first()
else:
r = q.filter(model.Resource.url.like("%s%%" % url)).first()
+
+ # new style internal download links
+ if re.search('(?:/resource/)(.*)(?:/download/)',url):
+ resource_id = re.search('(?:/resource/)(.*)(?:/download/)',url)
+ r = q.filter(model.Resource.id.like("%s%%" % resource_id.group(1))).first()
package_name = r.resource_group.package.name if r else ""
+
+
if package_name:
data[package_name] = data.get(package_name, 0) + int(result[1])
else:
@@ -470,7 +481,7 @@
log.info('Associating downloads of resource URLs with their respective datasets')
process_result_data(results.get('rows'))
- try:
+ '''try:
# Because of issues of invalid responses, we are going to make these requests
# ourselves.
headers = {'authorization': 'Bearer ' + self.token}
@@ -490,7 +501,7 @@
results = dict(url=[])
log.info('Associating downloads of cache resource URLs with their respective datasets')
- process_result_data(results.get('rows'), cached=False)
+ process_result_data(results.get('rows'), cached=False)'''
self._filter_out_long_tail(data, MIN_DOWNLOADS)
ga_model.update_sitewide_stats(period_name, "Downloads", data, period_complete_day)
--- 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):
@@ -130,7 +130,7 @@
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
@@ -327,7 +327,7 @@
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('organization')
+ parent, parents = '', publisher.get_parent_groups(type='organization')
if parents:
parent = parents[0].name
item = model.Session.query(GA_Publisher).\
--- a/ckanext/ga_report/helpers.py
+++ b/ckanext/ga_report/helpers.py
@@ -116,6 +116,10 @@
_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}
--- 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/scripts/ckanext_ga_reports.js
+++ b/ckanext/ga_report/public/scripts/ckanext_ga_reports.js
@@ -126,7 +126,6 @@
window.location = url;
};
var selectors = $('select[name="month"]');
- assert(selectors.length>0);
selectors.bind('change', handler);
};
--- a/ckanext/ga_report/templates/ga_report/publisher/index.html
+++ b/ckanext/ga_report/templates/ga_report/publisher/index.html
@@ -20,8 +20,8 @@
</py:def>
<py:match path="breadcrumbs">
- <li><a href="/data/site-usage">Site Analytics</a></li>
- <li><a href="/data/site-usage/publisher">Publishers</a></li>
+ <li><a href="/site-usage">Site Analytics</a></li>
+ <li><a href="/site-usage/publisher">Publishers</a></li>
</py:match>
<div py:match="content">
@@ -31,14 +31,6 @@
<h1>Site Usage</h1>
<div class="row" style="background: #fff;">
- <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>
<div class="col-md-8">
<div class="whitebox">
<strong>Publishers</strong>
@@ -78,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
@@ -21,10 +21,10 @@
<py:match path="breadcrumbs">
- <li><a href="/data/site-usage">Site Analytics</a></li>
+ <li><a href="/site-usage">Site Analytics</a></li>
<py:if test="c.publisher">
- <li><a href="/data/site-usage/publisher">Publishers</a></li>
- <li py:if="c.publisher"><a href="/data/site-usage/publisher/${c.publisher.name}">${c.publisher.title}</a></li>
+ <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>
@@ -36,21 +36,14 @@
<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> Download as CSV</a>
</py:with>
- <h1>Site Usage</h1>
+ <h1>Site Usage
+ <small py:if="c.publisher">${c.publisher.title}</small>
+ <small py:if="not c.publisher">All datasets</small>
+ </h1>
<div class="row" style="background: #fff;">
- <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>
<div class="col-md-8">
<div class="whitebox">
- <strong py:if="c.publisher">Datasets From <a href="${h.url_for(controller='ckanext.dgu.controllers.publisher:PublisherController',action='read',id=c.publisher.name)}">${c.publisher.title}</a></strong>
- <strong py:if="not c.publisher">All Datasets</strong>
<py:if test="c.graph_data">
${rickshaw_graph(c.graph_data,'dataset-downloads',debug=True)}
</py:if>
@@ -92,7 +85,7 @@
${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>
+ <td class="td-numeric">${downloads}</td>
</tr>
</py:for>
</table>
@@ -102,7 +95,7 @@
</div>
- <xi:include href="../../layout.html" />
+ <xi:include href="../site/layout.html" />
</html>
--- a/ckanext/ga_report/templates/ga_report/site/index.html
+++ b/ckanext/ga_report/templates/ga_report/site/index.html
@@ -8,20 +8,11 @@
<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/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>
- <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="breadcrumbs">
- <li><a href="/data/site-usage">Site Analytics</a></li>
- <li><a href="/data/site-usage">Site-wide</a></li>
+ <li><a href="/site-usage">Site Analytics</a></li>
+ <li><a href="/site-usage">Site-wide</a></li>
</py:match>
<div py:match="content">
@@ -37,8 +28,8 @@
<div class="panel-heading"><strong>Jump To...</strong></div>
<div class="panel-body">
<ul>
- <li><a href="/data/site-usage/publisher">Publisher Usage Statistics</a></li>
- <li><a href="/data/site-usage/dataset">Dataset Usage Statistics</a></li>
+ <li><a href="/site-usage/publisher">Publisher Usage Statistics</a></li>
+ <li><a href="/site-usage/dataset">Dataset Usage Statistics</a></li>
</ul>
</div>
</div>
@@ -46,14 +37,6 @@
</div>
<div class="row" style="background: #fff;">
- <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>
<div class="col-md-8">
<div class="whitebox">
<div class="tabbable">
@@ -210,7 +193,7 @@
</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, '<script type="text/javascript" src="' + h.url_for_static('/scripts/vendor/html5shiv/html5.js') + '"></script>')}
+<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} — ${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 … 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>
+