[ckan-changes] commit/ckan: 4 new changesets

Bitbucket commits-noreply at bitbucket.org
Wed Sep 21 11:04:22 UTC 2011


4 new changesets in ckan:

http://bitbucket.org/okfn/ckan/changeset/f4d41c0af259/
changeset:   f4d41c0af259
branch:      feature-1302-resource-tag-search
user:        John Glover
date:        2011-09-20 17:39:21
summary:     [search] Remove query and terms arguments from resource search query, not used
affected #:  1 file (-1 bytes)

--- a/ckan/lib/search/query.py	Tue Sep 20 15:49:18 2011 +0100
+++ b/ckan/lib/search/query.py	Tue Sep 20 16:39:21 2011 +0100
@@ -237,7 +237,7 @@
 
 class ResourceSearchQuery(SearchQuery):
     """Search for resources."""
-    def run(self, query=None, terms=[], fields={}, facet_by=[], options=None, **kwargs):
+    def run(self, fields={}, options=None, **kwargs):
         if options is None:
             options = QueryOptions(**kwargs) 
         else:
@@ -255,7 +255,7 @@
         # if options.all_fields is set, return a dict
         # if not, return a list of resource IDs
         if options.all_fields:
-           results['results'] = [r.as_dict() for r in results['results']]
+            results['results'] = [r.as_dict() for r in results['results']]
         else:
             results['results'] = [r.id for r in results['results']]
         return results


http://bitbucket.org/okfn/ckan/changeset/3ab7ea861258/
changeset:   3ab7ea861258
branch:      feature-1302-resource-tag-search
user:        John Glover
date:        2011-09-20 18:04:03
summary:     [search] Add basic tests for tag search
affected #:  1 file (-1 bytes)

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/tests/lib/test_tag_search.py	Tue Sep 20 17:04:03 2011 +0100
@@ -0,0 +1,45 @@
+from nose.tools import assert_raises
+from ckan.tests import *
+from ckan.tests import is_search_supported
+import ckan.lib.search as search
+from ckan import model
+from ckan.lib.create_test_data import CreateTestData
+
+class TestTagSearch(object):
+    @classmethod
+    def setup_class(self):
+        if not is_search_supported():
+            raise SkipTest("Search not supported")
+        CreateTestData.create()
+
+    @classmethod
+    def teardown_class(self):
+        model.repo.rebuild_db()
+
+    def test_good_search_terms(self):
+        result = search.query_for(model.Tag).run(terms=[u'ru'])
+        assert result['count'] == 1, result
+        assert 'russian' in result['results'], result
+
+        result = search.query_for(model.Tag).run(terms=[u's'])
+        assert result['count'] == 2, result
+        assert 'russian' in result['results'], result
+        assert 'tolstoy' in result['results'], result
+
+    def test_bad_search_terms(self):
+        result = search.query_for(model.Tag).run(terms=[u'asdf'])
+        assert result['count'] == 0, result
+
+    def test_good_search_fields(self):
+        result = search.query_for(model.Tag).run(fields={'tags': u'ru'})
+        assert result['count'] == 1, result
+        assert 'russian' in result['results'], result
+
+        result = search.query_for(model.Tag).run(fields={'tags': u's'})
+        assert result['count'] == 2, result
+        assert 'russian' in result['results'], result
+        assert 'tolstoy' in result['results'], result
+
+    def test_bad_search_fields(self):
+        result = search.query_for(model.Tag).run(fields={'tags': u'asdf'})
+        assert result['count'] == 0, result


http://bitbucket.org/okfn/ckan/changeset/f7115e152825/
changeset:   f7115e152825
branch:      feature-1302-resource-tag-search
user:        John Glover
date:        2011-09-21 12:02:44
summary:     [search] Move tag search function to logic layer, remove CKAN query parser
affected #:  4 files (-1 bytes)

--- a/ckan/lib/search/query.py	Tue Sep 20 17:04:03 2011 +0100
+++ b/ckan/lib/search/query.py	Wed Sep 21 11:02:44 2011 +0100
@@ -1,7 +1,5 @@
-from sqlalchemy import or_
 import json
 from pylons import config
-from paste.util.multidict import MultiDict 
 from paste.deploy.converters import asbool
 from ckan import model
 from ckan.logic import get_action
@@ -61,91 +59,6 @@
         self[name] = value
 
 
-class QueryParser(object):
-    """
-    The query parser will take any incoming query specifications and turn 
-    them into field-specific and general query parts. 
-    """
-    
-    def __init__(self, query, terms, fields):
-        self._query = query
-        self._terms = terms
-        self._fields = MultiDict(fields)
-    
-    @property    
-    def query(self):
-        if not hasattr(self, '_combined_query'):
-            parts = [self._query if self._query is not None else '']
-            
-            for term in self._terms:
-                if term.find(u' ') != -1:
-                    term = u"\"%s\"" % term
-                parts.append(term.strip())
-                
-            for field, value in self._fields.items():
-                if field != 'tags' and value.find(' ') != -1:
-                    value = u"\"%s\"" % value
-                parts.append(u"%s:%s" % (field.strip(), value.strip()))
-                
-            self._combined_query = u' '.join(parts)
-        return self._combined_query
-    
-    def _query_tokens(self):
-        """ Split the query string, leaving quoted strings intact. """
-        if self._query:
-            inside_quote = False
-            buf = u''
-            for ch in self._query:
-                if ch == u' ' and not inside_quote:
-                    if len(buf):
-                        yield buf.strip()
-                    buf = u''
-                elif ch == inside_quote:
-                    inside_quote = False
-                elif ch in [u"\"", u"'"]:
-                    inside_quote = ch
-                else:
-                    buf += ch
-            if len(buf):
-                yield buf.strip()
-    
-    def _parse_query(self):
-        """ Decompose the query string into fields and terms. """
-        self._combined_fields = MultiDict(self._fields)
-        self._combined_terms = list(self._terms)
-        for token in self._query_tokens():
-            colon_pos = token.find(u':')
-            if colon_pos != -1:
-                field = token[:colon_pos]
-                value = token[colon_pos+1:]
-                value = value.strip('"').strip("'").strip()
-                self._combined_fields.add(field, value)
-            else:
-                self._combined_terms.append(token)
-    
-    @property
-    def fields(self):
-        if not hasattr(self, '_combined_fields'):
-            self._parse_query()
-        return self._combined_fields
-    
-    @property
-    def terms(self):
-        if not hasattr(self, '_combined_terms'):
-            self._parse_query()
-        return self._combined_terms
-    
-    def validate(self):
-        """ Check that this is a valid query. """
-        pass
-    
-    def __str__(self):
-        return self.query
-        
-    def __repr__(self):
-        return "Query(%r)" % self.query
-
-
 class SearchQuery(object):
     """
     A query is ... when you ask the search engine things. SearchQuery is intended 
@@ -169,14 +82,6 @@
                     _open_licenses.append(license.id)
         return _open_licenses
     
-    def _format_results(self):
-        if not self.options.return_objects and len(self.results):
-            if self.options.all_fields:
-                self.results = [r.as_dict() for r in self.results]
-            else:
-                attr_name = self.options.ref_entity_with_attr
-                self.results = [getattr(entity, attr_name) for entity in self.results]
-
     def get_all_entity_ids(self, max_results=1000):
         """
         Return a list of the IDs of all indexed packages.
@@ -184,55 +89,40 @@
         return []
     
     def run(self, query=None, terms=[], fields={}, facet_by=[], options=None, **kwargs):
+        raise SearchError("SearchQuery.run() not implemented!")
+        
+    # convenience, allows to query(..)
+    __call__ = run
+
+
+class TagSearchQuery(SearchQuery):
+    """Search for tags."""
+    def run(self, query=[], fields={}, options=None, **kwargs):
         if options is None:
             options = QueryOptions(**kwargs) 
         else:
             options.update(kwargs)
-        self.options = options
-        self.options.validate()
-        self.facet_by = facet_by
-        self.facets = dict()
-        self.query = QueryParser(query, terms, fields)
-        self.query.validate()
-        self._run()
-        self._format_results()
-        return {'results': self.results, 'count': self.count}
+
+        context = {'model': model, 'session': model.Session}
+        data_dict = {
+            'query': query, 
+            'fields': fields,
+            'offset': options.get('offset'),
+            'limit': options.get('limit')
+        }
+        results = get_action('tag_search')(context, data_dict)
+
+        if not options.return_objects:
+            # if options.all_fields is set, return a dict
+            # if not, return a list of resource IDs
+            if options.all_fields:
+                results['results'] = [r.as_dict() for r in results['results']]
+            else:
+                results['results'] = [r.name for r in results['results']]
         
-    def _run(self):
-        raise SearchError("SearchQuery._run() not implemented!")
-
-    def _db_query(self, q):
-        # Run the query
-        self.count = q.count()
-        q = q.offset(self.options.get('offset'))
-        q = q.limit(self.options.get('limit'))
-        
-        self.results = []
-        for result in q:
-            if isinstance(result, tuple) and isinstance(result[0], model.DomainObject):
-                # This is the case for order_by rank due to the add_column.
-                self.results.append(result[0])
-            else:
-                self.results.append(result)
-        
-    # convenience, allows to query(..)
-    __call__ = run
-
-
-class TagSearchQuery(SearchQuery):
-    """Search for tags."""
-    def _run(self):
-        q = model.Session.query(model.Tag)
-        q = q.distinct().join(model.Tag.package_tags)
-        terms = list(self.query.terms)
-        for field, value in self.query.fields.items():
-            if field in ('tag', 'tags'):
-                terms.append(value)
-        if not len(terms):
-            return
-        for term in terms:
-            q = q.filter(model.Tag.name.contains(term.lower()))
-        self._db_query(q)
+        self.count = results['count']
+        self.results = results['results']
+        return results
 
 
 class ResourceSearchQuery(SearchQuery):
@@ -252,12 +142,16 @@
         }
         results = get_action('resource_search')(context, data_dict)
 
-        # if options.all_fields is set, return a dict
-        # if not, return a list of resource IDs
-        if options.all_fields:
-            results['results'] = [r.as_dict() for r in results['results']]
-        else:
-            results['results'] = [r.id for r in results['results']]
+        if not options.return_objects:
+            # if options.all_fields is set, return a dict
+            # if not, return a list of resource IDs
+            if options.all_fields:
+                results['results'] = [r.as_dict() for r in results['results']]
+            else:
+                results['results'] = [r.id for r in results['results']]
+
+        self.count = results['count']
+        self.results = results['results']
         return results
 
 


--- a/ckan/logic/action/get.py	Tue Sep 20 17:04:03 2011 +0100
+++ b/ckan/logic/action/get.py	Wed Sep 21 11:02:44 2011 +0100
@@ -500,16 +500,14 @@
 
     check_access('tag_autocomplete', context, data_dict)
 
-    q = data_dict.get('q',None)
+    q = data_dict.get('q', None)
     if not q:
         return []
 
     limit = data_dict.get('limit',10)
 
-    like_q = u"%s%%" % q
-
     query = query_for('tag')
-    query.run(query=like_q,
+    query.run(query=q,
               return_objects=True,
               limit=10,
               username=user)
@@ -676,3 +674,33 @@
             results.append(result)
 
     return {'count': count, 'results': results}
+
+def tag_search(context, data_dict):
+    model = context['model']
+    session = context['session']
+
+    query = data_dict.get('query')
+    terms = [query] if query else []
+
+    fields = data_dict.get('fields', {})
+    offset = data_dict.get('offset')
+    limit = data_dict.get('limit')
+
+    # TODO: should we check for user authentication first?
+    q = model.Session.query(model.Tag)
+    q = q.distinct().join(model.Tag.package_tags)
+    for field, value in fields.items():
+        if field in ('tag', 'tags'):
+            terms.append(value)
+
+    if not len(terms):
+        return
+
+    for term in terms:
+        q = q.filter(model.Tag.name.contains(term.lower()))
+
+    count = q.count()
+    q = q.offset(offset)
+    q = q.limit(limit)
+    results = [r for r in q]
+    return {'count': count, 'results': results}


--- a/ckan/tests/functional/api/test_action.py	Tue Sep 20 17:04:03 2011 +0100
+++ b/ckan/tests/functional/api/test_action.py	Wed Sep 21 11:02:44 2011 +0100
@@ -459,7 +459,6 @@
         postparams = '%s=1' % json.dumps({'q':'r'})
         res = self.app.post('/api/action/tag_autocomplete', params=postparams)
         res_obj = json.loads(res.body)
-        print res_obj
         assert res_obj == {
             'help': 'Returns tags containing the provided string', 
             'result': ['russian'], 


--- a/ckan/tests/lib/test_tag_search.py	Tue Sep 20 17:04:03 2011 +0100
+++ b/ckan/tests/lib/test_tag_search.py	Wed Sep 21 11:02:44 2011 +0100
@@ -16,18 +16,18 @@
     def teardown_class(self):
         model.repo.rebuild_db()
 
-    def test_good_search_terms(self):
-        result = search.query_for(model.Tag).run(terms=[u'ru'])
+    def test_good_search_query(self):
+        result = search.query_for(model.Tag).run(query=u'ru')
         assert result['count'] == 1, result
         assert 'russian' in result['results'], result
 
-        result = search.query_for(model.Tag).run(terms=[u's'])
+        result = search.query_for(model.Tag).run(query=u's')
         assert result['count'] == 2, result
         assert 'russian' in result['results'], result
         assert 'tolstoy' in result['results'], result
 
-    def test_bad_search_terms(self):
-        result = search.query_for(model.Tag).run(terms=[u'asdf'])
+    def test_bad_search_query(self):
+        result = search.query_for(model.Tag).run(query=u'asdf')
         assert result['count'] == 0, result
 
     def test_good_search_fields(self):


http://bitbucket.org/okfn/ckan/changeset/05ab5cc99251/
changeset:   05ab5cc99251
branch:      feature-1302-resource-tag-search
user:        John Glover
date:        2011-09-21 13:04:00
summary:     merge with default
affected #:  31 files (-1 bytes)

--- a/ckan/controllers/package.py	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/controllers/package.py	Wed Sep 21 12:04:00 2011 +0100
@@ -110,8 +110,6 @@
             abort(401, _('Not authorized to see this page'))
 
         q = c.q = request.params.get('q', u'') # unicode format (decoded from utf8)
-        c.open_only = request.params.get('open_only', 0)
-        c.downloadable_only = request.params.get('downloadable_only', 0)
         c.query_error = False
         try:
             page = int(request.params.get('page', 1))
@@ -144,7 +142,7 @@
         try:
             c.fields = []
             for (param, value) in request.params.items():
-                if not param in ['q', 'open_only', 'downloadable_only', 'page'] \
+                if not param in ['q', 'page'] \
                         and len(value) and not param.startswith('_'):
                     c.fields.append((param, value))
                     q += ' %s: "%s"' % (param, value)
@@ -157,8 +155,6 @@
                 'facet.field':g.facets,
                 'rows':limit,
                 'start':(page-1)*limit,
-                'filter_by_openness':c.open_only,
-                'filter_by_downloadable':c.downloadable_only,
             }
 
             query = get_action('package_search')(context,data_dict)


--- a/ckan/controllers/user.py	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/controllers/user.py	Wed Sep 21 12:04:00 2011 +0100
@@ -169,7 +169,6 @@
     def edit(self, id=None, data=None, errors=None, error_summary=None):
         context = {'model': model, 'session': model.Session,
                    'user': c.user or c.author,
-                   'preview': 'preview' in request.params,
                    'save': 'save' in request.params,
                    'schema': self._edit_form_to_db_schema(),
                    }
@@ -180,7 +179,7 @@
                 abort(400, _('No user specified'))
         data_dict = {'id': id}
 
-        if (context['save'] or context['preview']) and not data:
+        if (context['save']) and not data:
             return self._save_edit(id, context)
 
         try:
@@ -210,6 +209,7 @@
 
         self._setup_template_variables(context)
 
+        c.is_myself = True
         c.form = render(self.edit_user_form, extra_vars=vars)
 
         return render('user/edit.html')
@@ -222,15 +222,6 @@
             data_dict['id'] = id
             user = get_action('user_update')(context, data_dict)
 
-            if context['preview']:
-                about = request.params.getone('about')
-                c.preview = self._format_about(about)
-                c.user_about = about
-                c.full_name = request.params.get('fullname','')
-                c.email = request.params.getone('email')
-
-                return self.edit(id, data_dict)
-
             h.redirect_to(controller='user', action='read', id=user['id'])
         except NotAuthorized:
             abort(401, _('Unauthorized to edit user %s') % id)


--- a/ckan/lib/search/__init__.py	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/lib/search/__init__.py	Wed Sep 21 12:04:00 2011 +0100
@@ -12,8 +12,6 @@
 DEFAULT_OPTIONS = {
     'limit': 20,
     'offset': 0,
-    'filter_by_openness': False,
-    'filter_by_downloadable': False,
     # about presenting the results
     'order_by': 'rank',
     'return_objects': False,


--- a/ckan/logic/action/update.py	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/logic/action/update.py	Wed Sep 21 12:04:00 2011 +0100
@@ -339,7 +339,6 @@
 
     model = context['model']
     user = context['user']
-    preview = context.get('preview', False)
     schema = context.get('schema') or default_update_user_schema() 
     id = data_dict['id']
 
@@ -357,11 +356,8 @@
 
     user = user_dict_save(data, context)
     
-    if not preview:
-        model.repo.commit()        
-        return user_dictize(user, context)
-
-    return data
+    model.repo.commit()        
+    return user_dictize(user, context)
 
 ## Modifications for rest api
 


--- a/ckan/public/css/style.css	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/public/css/style.css	Wed Sep 21 12:04:00 2011 +0100
@@ -1,3 +1,4 @@
+ at import url('/css/forms.css');
 
 .header.outer {
   background-color: #e2e2e2;
@@ -176,6 +177,22 @@
 #minornavigation ul.tabbed li.action {
   float: right;
 }
+#minornavigation li {
+  border: 1px solid transparent;
+}
+#minornavigation li.current-tab {
+  background: #000;
+  background-color: #fff;
+  border: 1px solid #aaa;
+     -moz-border-radius: 5px; 
+  -webkit-border-radius: 5px; 
+          border-radius: 5px; 
+}
+#minornavigation li.current-tab a,
+#minornavigation li.current-tab a:hover,
+#minornavigation li.current-tab a:visited {
+  color: #222;
+}
 
 /* Side bar widgets */
 ul.widget-list {
@@ -262,13 +279,14 @@
 
 #minornavigation ul {
   list-style: none; 
-  padding: 7px;
+  padding: 1px;
   margin: 0;
 }
 
 #minornavigation ul li {
   display: inline-block;
-  margin-right: 2em;
+  margin-right: 3px;
+  padding: 5px 11px 5px 9px
 }
 
 #minornavigation ul li a {
@@ -711,11 +729,23 @@
   display: none;
 }
 
-body.edit.package div#content {
+body.package.edit div#content {
+  margin-right: 29px;
+  margin-left: 0px;
+  float: right;
+  padding-right: 0;
+  padding-left: 20px;
+  border: none;
+  border-left: 1px solid #eee;
+}
+body.package.edit div#sidebar {
+  padding-left: 0px;
+  float: left;
   margin-right: 0px;
 }
-body.edit.package div#sidebar {
-  padding-left: 0px;
+body.package.edit ul.widget-list {
+  margin-left: 1.5em;
+  margin-right: 0;
 }
 ul.edit-form-navigation {
   list-style-type: none;
@@ -741,16 +771,13 @@
   background-image:         linear-gradient(top, #f0f0f0, #e2e2e2);
             filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#f0f0f0', EndColorStr='#e2e2e2');
 
-  border-left: none;
-     -moz-border-radius: 5px; 
-  -webkit-border-radius: 5px; 
-          border-radius: 5px; 
-     -moz-border-bottom-left-radius: 0px; 
-  -webkit-border-bottom-left-radius: 0px; 
-          border-bottom-left-radius: 0px; 
-     -moz-border-top-left-radius: 0px; 
-  -webkit-border-top-left-radius: 0px; 
-          border-top-left-radius: 0px; 
+  border-right: none;
+     -moz-border-radius-bottomleft: 5px; 
+  -webkit-border-bottom-left-radius: 5px; 
+          border-bottom-left-radius: 5px; 
+     -moz-border-radius-topleft: 5px; 
+  -webkit-border-top-left-radius: 5px; 
+          border-top-left-radius: 5px; 
 
   -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; 
 }
@@ -762,6 +789,7 @@
   -moz-border-radius: 5px;
   border: 1px solid #CCC;
   padding: 0 5px 5px 10px;
+  width: 32em;
 }
 
 div.markdown-preview {
@@ -816,6 +844,23 @@
   float: right;
 }
 
+div.resource-add {
+  background: #eee;
+  padding-top: 10px;
+  padding-bottom: 5px;
+  border: 1px solid #e0e0e0;
+  border-left: none;
+  border-right: none;
+}
+div.resource-add li h4 {
+  display: inline;
+  padding-right: 20px;
+}
+div.resource-add-subpane {
+  margin-top: 10px;
+}
+
+
 /* ==================== */
 /* = Add Dataset Page = */
 /* ==================== */
@@ -894,11 +939,15 @@
 ul.tabs li a {
   display: inline-block;
   padding: 2px 8px;
+  margin-right: 10px;
   font-size: 10px;
   font-weight: bold;
   text-decoration: none;
   color: #666;
-  border: 1px solid transparent;
+  border: 1px solid #DDD;
+  border-color: #DDD;
+  border-right-color: #BBB;
+  border-bottom-color: #BBB;
   -webkit-border-radius: 10px;
   -moz-border-radius: 10px;
   border-top-left-radius: 10px 10px;
@@ -916,25 +965,138 @@
 }
 
 
+/* ============================== */
+/* = Controller-specific tweaks = */
+/* ============================== */
+
+body.group.index #minornavigation { 
+  visibility: hidden; 
+}
+
+body.package.search #minornavigation { 
+  visibility: hidden; 
+}
+body.package.search #menusearch {
+  display: none;
+}
+ 
+body.index.home #minornavigation {
+  display: none;
+}
+
+body.index.home #sidebar {
+  display: none;
+}
+
+body.index.home .front-page .action-box h1 {
+  padding-top: 0.6em;
+  padding-bottom: 0.5em;
+  font-size: 2.1em;
+}
+
+body.index.home .front-page .action-box {
+  border-radius: 20px;
+  background:  #FFF7C0;
+}
+
+body.index.home .front-page .action-box-inner {
+  margin: 20px;
+  margin-bottom: 5px;
+  min-height: 15em;
+}
+body.index.home .front-page .action-box-inner.collaborate {
+  background:url(/img/collaborate.png) no-repeat right top;
+}
+
+body.index.home .front-page .action-box-inner.share {
+  background:url(/img/share.png) no-repeat right top;
+}
+
+body.index.home .front-page .action-box-inner.find {
+  background:url(/img/find.png) no-repeat right top;
+}
+
+body.index.home .front-page .action-box-inner a {
+  font-weight: bold;
+}
+
+body.index.home .front-page .action-box-inner input {
+  font-family: 'Ubuntu';
+  border-radius: 10px;
+  background-color: #fff;
+  border: 0px;
+  font-size: 1.3em;
+  width: 90%;
+  border: 1px solid #999;
+  color: #666;
+  padding: 0.5em;
+}
+
+body.index.home .front-page .action-box-inner .create-button {
+  display: block;
+  float: right;
+  font-weight: normal;
+  font-family: 'Ubuntu';
+  margin-top: 1.5em;
+  border-radius: 10px;
+  background-color: #B22;
+  border: 0px;
+  font-size: 1.3em;
+  color: #fff;
+  padding: 0.5em;
+}
+
+body.index.home .front-page .action-box-inner .create-button:hover {
+  background-color: #822;
+}
+
+body.index.home .front-page .action-box-inner ul {
+margin-top: 1em;
+margin-bottom: 0;
+}
+
+body.index.home .front-page .whoelse {
+  margin-top: 1em;
+}
+
+body.index.home .front-page .group {
+  overflow: hidden;
+}
+
+body.index.home .front-page .group h3 {
+  margin-bottom: 0.5em;
+}
+
+body.index.home .front-page .group p {
+  margin-bottom: 0em;
+  min-height: 6em;
+}
+
+body.index.home .front-page .group strong {
+  display: block;
+  margin-bottom: 1.5em;
+}
+  
+
 /* ================================== */
 /* = Twitter.Bootstrap Form Buttons = */
 /* ================================== */
 div.form-submit {
-    background: #eee;
-    padding: 20px;
-    margin-bottom: 8px;
-    border: 1px solid #ccc;
-    border-left: none;
-    border-right: none;
-    height: 60px;
+  background: #eee;
+  padding: 20px;
+  margin-bottom: 8px;
+  border: 1px solid #ccc;
+  border-left: none;
+  border-right: none;
 }
-
 div.form-submit p.hints {
-    display: block;
-    width: 50%;
-    float: right;
+  width: 50%;
+  float: right;
+  margin: 0;
 }
-
+div.clear {
+  clear: both;
+}
 
 .pretty-button {
   cursor: pointer;
@@ -1044,5 +1206,16 @@
 }
 
 
+/* ====================================== */
+/* = Correct mistakes made by blueprint = */
+/* ====================================== */
 
+body.success,
+body.error {
+  background: #fff;
+  padding: 0;
+  margin-bottom: 0;
+  border: none;
+  color: #000;
+}
 


--- a/ckan/public/scripts/application.js	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/public/scripts/application.js	Wed Sep 21 12:04:00 2011 +0100
@@ -20,11 +20,23 @@
 
     var isDatasetNew = $('body.package.new').length > 0;
     if (isDatasetNew) {
-      $('#save').val("Add Dataset")
+      $('#save').val("Add Dataset");
     }
 
+    // Buttons with href-action should navigate when clicked
+    $('input.href-action').click(function(e) {
+      e.preventDefault();
+      window.location = ($(e.target).attr('action'));
+    });
+    
     var isDatasetEdit = $('body.package.edit').length > 0;
     if (isDatasetEdit) {
+      // Selectively enable the upload button
+      var storageEnabled = $.inArray('storage',CKAN.plugins)>=0;
+      if (storageEnabled) {
+        $('div.resource-add li.upload-file').show();
+      }
+
       // Set up hashtag nagivigation
       CKAN.Utils.setupDatasetEditNavigation();
 


--- a/ckan/public/scripts/templates.js	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/public/scripts/templates.js	Wed Sep 21 12:04:00 2011 +0100
@@ -1,12 +1,3 @@
-
-CKAN.Templates.resourceAddChoice = ' \
-  <ul> \
-    <li>Add a resource:</li> \
-    <li><a href="#" action="upload-file" class="action-resource-tab">Upload a file</a></li> \
-    <li><a href="#" action="link-file" class="action-resource-tab">Link to a file</a></li> \
-    <li><a href="#" action="link-api" class="action-resource-tab">Link to an API</a></li> \
-  </ul> \
-';
 
 CKAN.Templates.resourceAddLinkFile = ' \
   <form class="resource-add" action=""> \
@@ -35,7 +26,7 @@
         </label> \
       </dt> \
       <dd> \
-        <input name="url" type="text" placeholder="http://mydataset.com/file.csv" style="width: 60%" /> \
+        <input name="url" type="text" placeholder="http://mydataset.com/api/" style="width: 60%" /> \
         <input name="save" type="submit" class="pretty-button primary" value="Add" /> \
         <input name="reset" type="reset" class="pretty-button" value="Cancel" /> \
       </dd> \
@@ -80,12 +71,12 @@
   <td class="resource-summary resource-url"> \
     ${resource.url} \
   </td> \
+  <td class="resource-summary resource-name"> \
+    ${resource.name} \
+  </td> \
   <td class="resource-summary resource-format"> \
     ${resource.format} \
   </td> \
-  <td class="resource-summary resource-description"> \
-    ${resource.description} \
-  </td> \
   <td class="resource-expanded" colspan="3"> \
     <div class="inner"> \
     <table> \
@@ -97,8 +88,8 @@
       </thead> \
       <tbody> \
       <tr> \
-      <td style="display: none;" class="form-label">Name</td> \
-      <td style="display: none;" class="form-value" colspan="3"> \
+      <td class="form-label">Name</td> \
+      <td class="form-value" colspan="3"> \
         <input name="resources__${num}__name" type="text" value="${resource.name}" class="long" /> \
       </td> \
       </tr> \


--- a/ckan/templates/authorization_group/layout.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/authorization_group/layout.html	Wed Sep 21 12:04:00 2011 +0100
@@ -8,11 +8,11 @@
 
   <py:match path="minornavigation" py:if="c.authorization_group"><ul class="tabbed">
-    <li>${h.subnav_link(c, h.icon('authorization_group') + _('View'), controller='authorization_group', action='read', id=c.authorization_group.name or c.authorization_group.id)}</li>
-    <li py:if="h.check_access('authorization_group_update',{'id':c.authorization_group.id})">
+    <li py:attrs="{'class':'current-tab'} if c.action=='read' else {}">${h.subnav_link(c, h.icon('authorization_group') + _('View'), controller='authorization_group', action='read', id=c.authorization_group.name or c.authorization_group.id)}</li>
+    <li py:attrs="{'class':'current-tab'} if c.action=='edit' else {}" py:if="h.check_access('authorization_group_update',{'id':c.authorization_group.id})">
       ${h.subnav_link(c, h.icon('authorization_group_edit') + _('Edit'), controller='authorization_group', action='edit', id=c.authorization_group.name or c.authorization_group.id)}
     </li>
-    <li py:if="h.check_access('authorization_group_edit_permissions',{'id':c.authorization_group.id})">
+    <li py:attrs="{'class':'current-tab'} if c.action=='authz' else {}" py:if="h.check_access('authorization_group_edit_permissions',{'id':c.authorization_group.id})">
       ${h.subnav_link(c, h.icon('lock') + _('Authorization'), controller='authorization_group', action='authz', id=c.authorization_group.name or c.authorization_group.id)}
     </li></ul>


--- a/ckan/templates/group/index.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/group/index.html	Wed Sep 21 12:04:00 2011 +0100
@@ -6,10 +6,6 @@
   <py:def function="page_title">Groups of Datasets</py:def><py:def function="page_heading">Groups of Datasets</py:def>
   
-  <py:def function="optional_head">
-    <style>#minornavigation { visibility: hidden; }</style>    
-  </py:def>
- 
   <div py:match="content">
 
     ${c.page.pager()}


--- a/ckan/templates/group/layout.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/group/layout.html	Wed Sep 21 12:04:00 2011 +0100
@@ -23,12 +23,12 @@
 
   <py:match path="minornavigation" py:if="c.group"><ul class="tabbed">
-    <li>${h.subnav_link(c, h.icon('group') + _('View'), controller='group', action='read', id=c.group.name)}</li>
-    <li py:if="h.check_access('group_update',{'id':c.group.id})">
+    <li py:attrs="{'class':'current-tab'} if c.action=='read' else {}">${h.subnav_link(c, h.icon('group') + _('View'), controller='group', action='read', id=c.group.name)}</li>
+    <li py:attrs="{'class':'current-tab'} if c.action=='edit' else {}" py:if="h.check_access('group_update',{'id':c.group.id})">
       ${h.subnav_link(c, h.icon('group_edit') + _('Edit'), controller='group', action='edit', id=c.group.name)}
     </li>
-    <li>${h.subnav_link(c, h.icon('page_white_stack') + _('History'), controller='group', action='history', id=c.group.name)}</li>
-    <li py:if="h.check_access('group_edit_permissions',{'id':c.group.id})">
+    <li py:attrs="{'class':'current-tab'} if c.action=='history' else {}">${h.subnav_link(c, h.icon('page_white_stack') + _('History'), controller='group', action='history', id=c.group.name)}</li>
+    <li py:attrs="{'class':'current-tab'} if c.action=='authz' else {}" py:if="h.check_access('group_edit_permissions',{'id':c.group.id})">
       ${h.subnav_link(c, h.icon('lock') + _('Authorization'), controller='group', action='authz', id=c.group.name)}
     </li><li class="action">


--- a/ckan/templates/home/index.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/home/index.html	Wed Sep 21 12:04:00 2011 +0100
@@ -6,108 +6,6 @@
   <py:def function="page_title">Welcome</py:def><py:def function="body_class">hide-sidebar</py:def>
 
-  <py:def function="optional_head">
-      <style>
-        #minornavigation {
-          display: none;
-        }
-  
-        #sidebar {
-          display: none;
-        }
-
-        .front-page .action-box h1 {
-          padding-top: 0.6em;
-          padding-bottom: 0.5em;
-          font-size: 2.1em;
-        }
-
-        .front-page .action-box {
-          border-radius: 20px;
-          background:  #FFF7C0;
-        }
-        
-        .front-page .action-box-inner {
-          margin: 20px;
-          margin-bottom: 5px;
-          min-height: 15em;
-        }
-        .front-page .action-box-inner.collaborate {
-          background:url(/img/collaborate.png) no-repeat right top;
-        }
-        
-        .front-page .action-box-inner.share {
-          background:url(/img/share.png) no-repeat right top;
-        }
-        
-        .front-page .action-box-inner.find {
-          background:url(/img/find.png) no-repeat right top;
-        }
-        
-        .front-page .action-box-inner a {
-          font-weight: bold;
-        }
-       
-        .front-page .action-box-inner input {
-          font-family: 'Ubuntu';
-          border-radius: 10px;
-          background-color: #fff;
-          border: 0px;
-          font-size: 1.3em;
-          width: 90%;
-          border: 1px solid #999;
-          color: #666;
-          padding: 0.5em;
-        }
-        
-        .front-page .action-box-inner .create-button {
-          display: block;
-          float: right;
-          font-weight: normal;
-          font-family: 'Ubuntu';
-          margin-top: 1.5em;
-          border-radius: 10px;
-          background-color: #B22;
-          border: 0px;
-          font-size: 1.3em;
-          color: #fff;
-          padding: 0.5em;
-        }
-        
-        .front-page .action-box-inner .create-button:hover {
-          background-color: #822;
-        }
-        
-        .front-page .action-box-inner ul {
-        margin-top: 1em;
-        margin-bottom: 0;
-        }
-
-        .front-page .whoelse {
-          margin-top: 1em;
-        }
-
-        .front-page .group {
-          overflow: hidden;
-        }
-        
-        .front-page .group h3 {
-          margin-bottom: 0.5em;
-        }
-        
-        .front-page .group p {
-          margin-bottom: 0em;
-          min-height: 6em;
-        }
-        
-        .front-page .group strong {
-          display: block;
-          margin-bottom: 1.5em;
-        }
-
-      </style>
-  </py:def>
-
     <div py:match="//div[@id='content']" class="front-page"><div class="span-24 last"><h1>Welcome to ${g.site_title}!</h1>


--- a/ckan/templates/layout_base.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/layout_base.html	Wed Sep 21 12:04:00 2011 +0100
@@ -37,7 +37,6 @@
     <link rel="stylesheet" href="${g.site_url}/css/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]--><link rel="stylesheet" href="${g.site_url}/css/style.css?v=2" />
-  <link rel="stylesheet" href="${g.site_url}/css/forms.css" type="text/css" media="screen, print" /><py:if test="defined('optional_head')">
     ${optional_head()}
@@ -201,6 +200,7 @@
     </footer></div><!-- eo #container -->
 
+
   <script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script><!--script><![CDATA[window.jQuery || document.write("<script src='${g.site_url}/scripts/vendor/jquery/1.6.2/jquery.js'>\x3C/script>")]]></script--><script type="text/javascript" src="${g.site_url}/scripts/vendor/json2.js"></script>
@@ -223,6 +223,10 @@
   <script src="${g.site_url}/scripts/vendor/modernizr/1.7/modernizr.min.js"></script><script type="text/javascript">
+    CKAN.plugins = [ 
+      // Declare js array from Python string
+      ${['\'%s\', '%s  for s in config['ckan.plugins'].split(' ')]}
+    ];
     $(document).ready(function() {
         var ckan_user = $.cookie("ckan_display_name");
         if (ckan_user) {


--- a/ckan/templates/package/edit.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/package/edit.html	Wed Sep 21 12:04:00 2011 +0100
@@ -27,6 +27,11 @@
     </li></py:match>
 
+  <py:match path="cancelbutton">
+  HELLO JEN
+    <input id="cancel" tabindex="100" class="pretty-button" name="cancel" type="reset" action="${h.url_for(controller='package', action='read', id=c.pkg.name)}" value="Cancel" />
+  </py:match>
+
   <div py:match="content" class="dataset">
     ${c.form}
   </div>


--- a/ckan/templates/package/edit_form.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/package/edit_form.html	Wed Sep 21 12:04:00 2011 +0100
@@ -25,7 +25,6 @@
       </div><div class="submit">
-        <input name="preview" type="submit" value="Preview" />
         ${h.submit('save', _('Save'))}
       </div>
 


--- a/ckan/templates/package/layout.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/package/layout.html	Wed Sep 21 12:04:00 2011 +0100
@@ -8,12 +8,12 @@
   <py:match path="minornavigation"><py:if test="c.pkg"><ul class="tabbed">
-      <li>${h.subnav_link(c, h.icon('package') + _('View'), controller='package', action='read', id=c.pkg.name)}</li>
-      <li py:if="h.check_access('package_update',{'id':c.pkg.id})">
+      <li py:attrs="{'class':'current-tab'} if c.action=='read' else {}">${h.subnav_link(c, h.icon('package') + _('View'), controller='package', action='read', id=c.pkg.name)}</li>
+      <li py:attrs="{'class':'current-tab'} if c.action=='edit' else {}" py:if="h.check_access('package_update',{'id':c.pkg.id})">
           ${h.subnav_link(c, h.icon('package_edit') + _('Edit'), controller='package', action='edit', id=c.pkg.name)}
       </li>
-      <li>${h.subnav_link(c, h.icon('page_stack') + _('History'), controller='package', action='history', id=c.pkg.name)}</li>
-      <li py:if="h.check_access('package_edit_permissions',{'id':c.pkg.id})">
+      <li py:attrs="{'class':'current-tab'} if c.action=='history' else {}">${h.subnav_link(c, h.icon('page_stack') + _('History'), controller='package', action='history', id=c.pkg.name)}</li>
+      <li py:attrs="{'class':'current-tab'} if c.action=='authz' else {}" py:if="h.check_access('package_edit_permissions',{'id':c.pkg.id})">
         ${h.subnav_link(c, h.icon('lock') + _('Authorization'), controller='package', action='authz', id=c.pkg.name)}
       </li><li class="action">


--- a/ckan/templates/package/new_package_form.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/package/new_package_form.html	Wed Sep 21 12:04:00 2011 +0100
@@ -92,9 +92,9 @@
     <thead><tr><th class="resource-expand-link"></th>
-        <th class="field_req resource-url">URL*</th>
+        <th class="field_req resource-url">URL</th>
+        <th class="field_opt resource-description">Name</th><th class="field_opt resource-format">Format</th>
-        <th class="field_opt resource-description">Description</th><th class="field_opt resource-is-changed"></th></tr></thead>
@@ -108,12 +108,12 @@
         <td class="resource-summary resource-url">
           ${res.get('url', '')}
         </td>
+        <td class="resource-summary resource-name">
+          ${res.get('name', '')}
+        </td><td class="resource-summary resource-format">
           ${res.get('format', '')}
         </td>
-        <td class="resource-summary resource-description">
-          ${res.get('description', '')}
-        </td><td class="resource-expanded" colspan="3" style="display: none;"><dl><dt><label class="field_opt">Url</label></dt>
@@ -151,12 +151,11 @@
 
   <div class="resource-add"><ul class="tabs">
-      <li>Add a resource:</li>
-      <li><a href="#" action="upload-file" class="action-resource-tab">Upload a file</a></li>
+      <li><h4>Add a resource:</h4></li><li><a href="#" action="link-file" class="action-resource-tab">Link to a file</a></li><li><a href="#" action="link-api" class="action-resource-tab">Link to an API</a></li>
+      <li class="upload-file" style="display:none;"><a href="#" action="upload-file" class="action-resource-tab">Upload a file</a></li></ul>
-    <div class="resource-add-form"></div></div></fieldset>
 
@@ -258,11 +257,15 @@
 </div><div class="form-submit">
+  <input id="save" tabindex="99" class="pretty-button primary" name="save" type="submit" value="Save Changes" />
+  <py:if test="c.pkg">
+    <input id="cancel" tabindex="100" class="pretty-button href-action" name="cancel" type="reset" value="Cancel" action="${h.url_for(controller='package', action='read', id=c.pkg.name)}" />
+  </py:if><p class="hints"><strong>Important:</strong> By submitting content, you agree to release your contributions
     under the <a href="http://opendatacommons.org/licenses/odbl/1.0/">Open Database License</a>. Please <strong>refrain</strong> from editing this page if you are <strong>not</strong> happy to do this.
   </p>
-  <input id="save" tabindex="99" class="pretty-button primary" name="save" type="submit" value="Save Changes" />
+  <div class="clear"></div></div>
 
 


--- a/ckan/templates/package/read_core.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/package/read_core.html	Wed Sep 21 12:04:00 2011 +0100
@@ -14,21 +14,27 @@
     <div class="resources subsection"><h3>Resources</h3><table>
-        <tr>
-            <th>Description</th>
+        <thead>
+            <th>Url</th>
+            <th>Name/Description</th><th>Format</th>
-        </tr>
+        </thead><py:for each="res in c.pkg_dict.get('resources', [])"><tr rel="dcat:distribution" resource="_:res${res.id}"
             typeof="dcat:Distribution"><td>
+                <a href="${res.get('url', '')}" target="_blank">${res.get('url', '')}</a>  
+              </td>
+              <td><py:choose test="">
+                    <py:when test="res.get('name')">
+                      <span property="rdfs:label">${res.name}</span>
+                    </py:when><py:when test="res.get('description')">
-                    <a href="${res.get('url', '')}" rel="dcat:accessURL" target="_blank"><span
-                        property="rdfs:label">${res.description}</span></a>  
+                      <span property="rdfs:label">${res.description}</span></py:when><py:otherwise test="">
-                      <a href="${res.get('url', '')}" target="_blank">Download <em>(no description)</em></a>  
+                      <em>(none)</em></py:otherwise></py:choose></td>


--- a/ckan/templates/package/search.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/package/search.html	Wed Sep 21 12:04:00 2011 +0100
@@ -9,15 +9,6 @@
   <py:def function="page_title">Search - ${g.site_title}</py:def><py:def function="page_heading">Search - ${g.site_title}</py:def>
 
-  <py:def function="optional_head">
-  <style>
-      #minornavigation { visibility: hidden; }
-      #menusearch {
-        display: none;
-      }
-    </style>    
-  </py:def>
-
   <py:match path="primarysidebar"><li class="widget-container boxed widget_text" py:if="h.check_access('package_create')">


--- a/ckan/templates/package/search_form.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/package/search_form.html	Wed Sep 21 12:04:00 2011 +0100
@@ -12,10 +12,6 @@
     <input type="hidden" name="${k}" value="${v}" /></py:for></span>
-  <!-- Feature disabled for a cleaner page. TODO Remove entirely from backend? -->
-  <div style="display: none;" class="dataset-search-filters">Filter by <label for="open_only" class="inline">${h.checkbox(name='open_only', checked=c.open_only)} datasets with open licenses</label>
-  <label for="downloadable_only" class="inline">${h.checkbox(name='downloadable_only', checked=c.downloadable_only)} datasets with downloads</label>
-  </div><input type="submit" value="${_('Search')}" class="pretty-button primary button" /></form>
 


--- a/ckan/templates/revision/list.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/revision/list.html	Wed Sep 21 12:04:00 2011 +0100
@@ -7,7 +7,7 @@
 
   <py:match path="minornavigation"><ul class="tabbed">
-      <li>
+      <li class="current-tab">
         ${h.subnav_link(c,_('Home'), controller='revision', action='index')}</li><li class="action">
       ${h.subnav_link(c, h.icon('atom_feed') + _('Subscribe'),


--- a/ckan/templates/user/edit.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/user/edit.html	Wed Sep 21 12:04:00 2011 +0100
@@ -9,49 +9,6 @@
   </py:def><div py:match="content">
-    <a href="#preview" py:if="c.preview">(skip to preview)</a>
-
-    <form id="user-edit" action="" method="post" class="simple-form" 
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      >
-      <fieldset>
-        <legend>Base details</legend>
-        <label for="fullname">Full name:</label>
-        <input name="fullname" value="${c.user_fullname}" /><br/>
-         
-        <label for="email">E-Mail:</label>
-        <input name="email" value="${c.user_email}" /><br/>
-      </fieldset>
-      <fieldset>
-        <legend>Change your password</legend>
-        <label for="password1">Password:</label>
-        <input type="password" name="password1" value="" />
-        <br/>
-        <label for="password2">Password (repeat):</label>
-        <input type="password" name="password2" value="" />
-        <br/>
-      </fieldset>
-      <label for="about">About user:</label>
-      <textarea id="about" rows="5" name="about" cols="60">${c.user_about}</textarea>
-      <p class="small" i18n:msg="">You can use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown formatting</a> here.</p>
-
-      <div>
-        <input name="preview" type="submit" value="Preview" />
-        ${h.submit('save', _('Save'))}
-      </div>
-    </form>
-
-
-    <div id="preview" style="margin-left: 20px;" py:if="c.preview">
-      <hr />
-      <h2>Preview</h2>
-      <h4>Full name: ${c.full_name}</h4>
-      <div style="border: 2px dashed red; padding: 5px;"> 
-        ${c.preview}
-      </div>
-    </div>
-
     ${Markup(c.form)}
   </div>
 


--- a/ckan/templates/user/edit_user_form.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/user/edit_user_form.html	Wed Sep 21 12:04:00 2011 +0100
@@ -19,6 +19,19 @@
      
     <dt><label for="email">E-Mail:</label></dt><dd><input type="text" name="email" value="${data.get('email','')}" /></dd>
+    
+    <dt><label for="about">About:</label></dt>
+    <dd class="description-field">
+      <div class="markdown-editor">
+        <ul class="tabs">
+          <li><a href="#" action="write" class="selected">Write</a></li>
+          <li><a href="#" action="preview">Preview</a></li>
+        </ul>
+        <textarea class="markdown-input" tabindex="3" name="about" id="about" placeholder="A little about you...">${data.get('about','')}</textarea>
+        <div class="markdown-preview" style="display: none;"></div>
+        <span class="hints">You can use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown formatting</a> here.</span>
+      </div>
+    </dd></dl></fieldset><fieldset>
@@ -30,13 +43,9 @@
     <dd><input type="password" name="password2" value="" /></dd></dl></fieldset>
-  <label for="about">About user:</label>
-  <textarea id="about" rows="5" name="about" cols="60">${data.get('about','')}</textarea>
-  <p class="small" i18n:msg="">You can use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown formatting</a> here.</p>
-
-  <div>
-    <input name="preview" type="submit" value="Preview" />
-    ${h.submit('save', _('Save'))}
+  <div class="form-submit">
+    <input id="save" class="pretty-button primary" name="save" type="submit" value="Save Changes" />
+    <input id="cancel" class="pretty-button href-action" name="cancel" type="reset" value="Cancel" action="${h.url_for(controller='user', action='read')}" /></div></form>
 


--- a/ckan/templates/user/layout.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/user/layout.html	Wed Sep 21 12:04:00 2011 +0100
@@ -5,6 +5,15 @@
   py:strip=""
   >
 
+  <py:match path="minornavigation">
+    <ul class="tabbed" py:if="c.is_myself">
+      <li py:attrs="{'class':'current-tab'} if c.action=='read' else {}"><a href="${h.url_for(controller='user', action='read')}">My Profile</a></li>
+      <li py:attrs="{'class':'current-tab'} if c.action=='edit' else {}"><a href="${h.url_for(controller='user', action='edit')}">Edit Profile</a></li>
+      <li><a href="${h.url_for('/user/logout')}">Log out</a></li>
+    </ul>
+  </py:match>
+  
+
   <xi:include href="../layout.html" /></html>
 


--- a/ckan/templates/user/read.html	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/templates/user/read.html	Wed Sep 21 12:04:00 2011 +0100
@@ -6,13 +6,6 @@
   <py:def function="page_heading">${c.user_dict['display_name']}</py:def><py:def function="body_class">user-view</py:def>
   
-  <py:match path="minornavigation">
-    <ul class="tabbed" py:if="c.is_myself">
-      <li><a href="${h.url_for(controller='user', action='edit')}">Edit your profile</a></li>
-      <li><a href="${h.url_for('/user/logout')}">Log out</a></li>
-    </ul>
-  </py:match>
-  
   <py:match path="primarysidebar"><li class="widget-container widget_text" py:if="not c.hide_welcome_message"><h3>Activity</h3>


--- a/ckan/tests/functional/api/test_package_search.py	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/tests/functional/api/test_package_search.py	Wed Sep 21 12:04:00 2011 +0100
@@ -49,8 +49,8 @@
         # uri parameters
         check(UnicodeMultiDict({'q': '', 'ref': 'boris'}),
               {"q": "", "ref": "boris"})
-        check(UnicodeMultiDict({'filter_by_openness': '1'}),
-              {'filter_by_openness': '1'})
+        check(UnicodeMultiDict({}),
+              {})
         # uri json
         check(UnicodeMultiDict({'qjson': '{"q": "", "ref": "boris"}'}),
               {"q": "", "ref": "boris"})
@@ -310,36 +310,6 @@
         res_dict = self.data_from_res(res)
         assert_equal(res_dict['count'], 3)
 
-    def test_12_filter_by_openness_qjson(self):
-        query = {'q': '', 'filter_by_openness': '1'}
-        json_query = self.dumps(query)
-        offset = self.base_url + '?qjson=%s' % json_query
-        res = self.app.get(offset, status=200)
-        res_dict = self.data_from_res(res)
-        assert_equal(res_dict['count'], 2)
-        self.assert_results(res_dict, (u'annakarenina', u'testpkg'))
-
-    def test_12_filter_by_openness_q(self):
-        offset = self.base_url + '?filter_by_openness=1'
-        res = self.app.get(offset, status=200)
-        res_dict = self.data_from_res(res)
-        assert_equal(res_dict['count'], 2)
-        self.assert_results(res_dict, (u'annakarenina', u'testpkg'))
-
-    def test_12_filter_by_openness_off_qjson(self):
-        query = {'q': '', 'filter_by_openness': '0'}
-        json_query = self.dumps(query)
-        offset = self.base_url + '?qjson=%s' % json_query
-        res = self.app.get(offset, status=200)
-        res_dict = self.data_from_res(res)
-        assert_equal(res_dict['count'], 3)
-
-    def test_12_filter_by_openness_off_q(self):
-        offset = self.base_url + '?filter_by_openness=0'
-        res = self.app.get(offset, status=200)
-        res_dict = self.data_from_res(res)
-        assert_equal(res_dict['count'], 3)
-
     def test_13_just_groups(self):
         offset = self.base_url + '?q=groups:roger'
         res = self.app.get(offset, status=200)


--- a/ckan/tests/functional/test_package.py	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/tests/functional/test_package.py	Wed Sep 21 12:04:00 2011 +0100
@@ -338,9 +338,9 @@
         res = self.app.get(offset)
         assert 'Search - ' in res
         self._check_search_results(res, 'annakarenina', ['<strong>1</strong>', 'A Novel By Tolstoy'] )
-        self._check_search_results(res, 'warandpeace', ['<strong>0</strong>'], only_downloadable=True )
-        self._check_search_results(res, 'warandpeace', ['<strong>0</strong>'], only_open=True )
-        self._check_search_results(res, 'annakarenina', ['<strong>1</strong>'], only_open=True, only_downloadable=True )
+        self._check_search_results(res, 'warandpeace', ['<strong>1</strong>'])
+        self._check_search_results(res, 'warandpeace', ['<strong>1</strong>'])
+        self._check_search_results(res, 'annakarenina', ['<strong>1</strong>'])
         # check for something that also finds tags ...
         self._check_search_results(res, 'russian', ['<strong>2</strong>'])
 
@@ -362,11 +362,9 @@
         # solr's edismax parser won't throw an error, so this should return 0 results
         assert '>0<' in results_page, results_page
 
-    def _check_search_results(self, page, terms, requireds, only_open=False, only_downloadable=False):
+    def _check_search_results(self, page, terms, requireds):
         form = page.forms['dataset-search']
         form['q'] = terms.encode('utf8') # paste doesn't handle this!
-        form['open_only'] = only_open
-        form['downloadable_only'] = only_downloadable
         results_page = form.submit()
         assert 'Search - ' in results_page, results_page
         results_page = self.main_div(results_page)


--- a/ckan/tests/functional/test_user.py	Wed Sep 21 11:02:44 2011 +0100
+++ b/ckan/tests/functional/test_user.py	Wed Sep 21 12:04:00 2011 +0100
@@ -482,13 +482,6 @@
         fv['about'] = new_about
         fv['password1'] = new_password
         fv['password2'] = new_password
-        res = fv.submit('preview', extra_environ={'REMOTE_USER':username})
-        
-        # preview
-        main_res = self.main_div(res)
-        assert 'Edit User: testedit' in main_res, main_res
-        in_preview = main_res[main_res.find('Preview'):]
-        assert new_about in in_preview, in_preview
 
         # commit
         res = fv.submit('save', extra_environ={'REMOTE_USER':username})      
@@ -533,14 +526,6 @@
         fv['password1'] = ''
         fv['password2'] = ''
 
-        res = fv.submit('preview', extra_environ={'REMOTE_USER':username})
-        
-        # preview
-        main_res = self.main_div(res)
-        assert 'Edit User: testedit2' in main_res, main_res
-        in_preview = main_res[main_res.find('Preview'):]
-        assert new_about in in_preview, in_preview
-
         # commit
         res = fv.submit('save', extra_environ={'REMOTE_USER':username})      
         assert res.status == 302, self.main_div(res).encode('utf8')
@@ -603,7 +588,6 @@
         assert 'Edit User: ' in main_res, main_res
         assert 'Test About <a href="http://spamsite.net">spamsite</a>' in main_res, main_res
         fv = res.forms['user-edit']
-        res = fv.submit('preview', extra_environ={'REMOTE_USER':username})
         # commit
         res = fv.submit('save', extra_environ={'REMOTE_USER':username})      
         assert res.status == 200, res.status


--- a/doc/configuration.rst	Wed Sep 21 11:02:44 2011 +0100
+++ b/doc/configuration.rst	Wed Sep 21 12:04:00 2011 +0100
@@ -524,3 +524,24 @@
   ckan.backup_dir = /var/backups/ckan/
 
 This is a directory where SQL database backups are to be written, assuming a script has been installed to do this.
+
+template_footer_end
+^^^^^^^^^^^^^^^^^^^
+
+HTML content to be inserted just before </body> tag (e.g. google analytics code).
+
+.. note:: can use html e.g. <strong>blah</strong> and can have multiline strings (just indent following lines)
+
+Example (showing insertion of google analytics code)::
+
+  ckan.template_footer_end = <!-- Google Analytics -->
+    <script src='http://www.google-analytics.com/ga.js' type='text/javascript'></script>
+    <script type="text/javascript">
+    try {
+    var pageTracker = _gat._getTracker("XXXXXXXXX");
+    pageTracker._setDomainName(".ckan.net");
+    pageTracker._trackPageview();
+    } catch(err) {}
+    </script>
+    <!-- /Google Analytics -->
+


--- a/doc/theming.rst	Wed Sep 21 11:02:44 2011 +0100
+++ b/doc/theming.rst	Wed Sep 21 12:04:00 2011 +0100
@@ -69,16 +69,20 @@
 
 Next, copy the ``layout.html`` template and add a reference to the new CSS file. Here is an example of the edited ``layout.html`` template::
 
- <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="">
-   <py:def function="optional_head">
-       <link rel="stylesheet" href="${g.site_url}/css/mycss.css" />
-   </py:def>
-   <xi:include href="layout_base.html" />
- </html>
+  <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="">                                                 
+                                                                 
+    <head py:match="head">
+      ${select('*')}
+
+      <link rel="stylesheet" href="${g.site_url}/css/mycss.css" />
+    </head>
+
+    <xi:include href="layout_base.html" />
+  </html>
 
 Retheming the Site with Templates
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -98,4 +102,4 @@
 
 .. note::
 
-  For more information on the syntax of the CKAN templates, refer to the `Genshi documentation <http://genshi.edgewall.org/wiki/Documentation>`_.
\ No newline at end of file
+  For more information on the syntax of the CKAN templates, refer to the `Genshi documentation <http://genshi.edgewall.org/wiki/Documentation>`_.

Repository URL: https://bitbucket.org/okfn/ckan/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.




More information about the ckan-changes mailing list